1

对于我的第一个 node.js 项目,我正在为一些 mjpeg 输出创建代理。现在,一些客户端无法像服务器获取流一样快地读取流,所以我正在丢帧让它们赶上。我目前正在使用对象的drain事件,http.ServerResponse或多或少类似于下面,但是因为被调用了很多,我想知道是否有更好/更轻量级的方法(我在可写流文档drain中找不到)

httpServer.on('request',function(request,response){
    //skipped setting some headers etc.
    res.socket.on('drain', function(){
        res.drained = true;
        res.draincounter = 0;
    });
    //followed by some unconditional first write (set drained to false first)
    res.drained = false;
    res.write(someData);
};

现在,向客户端写入帧是这样的:

writeFrame = function(res,frame){
   if(res.drained){
       //nothing in queue, ready for new data

       //first set drained to false
       res.drained = false;
       //and feed it more data
       res.write(frame, 'binary');
   } else if(res.draincounter > maxDroppedFrames){
       //to many dropped frames in a row, disconnect
       res.end();
   } else {
       res.draincounter++;
   }
}

但这使用了自定义属性,并且对 drain 事件进行了很多调用(这可能没问题,但以我有限的经验并不适合我)。简而言之,我的问题是:

是否有更有效的方法来检测 aWritable Stream的写入缓冲区为空?


编辑:在关闭和休眠一段时间后使用节点 0.10.18 重新启动此项目后,出现了严重错误,因为我不再收到drain事件(至少,不是以上述方式)。现在,我检查是否res.socket.bufferSize高于某个标记来决定是否写入 mjpeg 帧,draincounter仍然有效。pipe() 从评论中的流文档中提倡或在我的情况下不是解决方案,因为问题不是在客户端速度上写入所有数据,而是在客户端速度低于源流时丢弃数据。

谁能告诉我什么是处理背压数据丢失的正确方法(如果不是这样)?

4

1 回答 1

2

我个人会res._writableState直接检查,但此时您正在深入研究可能会发生变化的节点源,但我怀疑这在未来会发生很大变化。

但这就是事情变成艺术的时候。您可以检查很多不同的标志:https ://github.com/joyent/node/blob/master/lib/_stream_writable.js#L40

如果要检查缓冲区的当前大小,可以检查res._writableState.length- https://github.com/joyent/node/blob/master/lib/_stream_writable.js#L79

这只会在缓冲区为 0 时写入。

function writeFrame(res, frame) {
  if (res._writableState.length) {
    if (++res.droppedFrames > maxDroppedFrames) res.end()
  } else {
    res.write(frame)
  }
}

如果您只想在缓冲区低于高水位线时写入(即不需要排水),您可以检查res._writableState.needDrain- https://github.com/joyent/node/blob/master/lib/ _stream_writable.js#L57

这只会在缓冲区低于高水位线时写入:

function writeFrame(res, frame) {
  if (res._writableState.needDrain) {
    if (++res.droppedFrames > maxDroppedFrames) res.end()
  } else {
    res.write(frame)
  }
}

您可能会感兴趣的其他标志。要点是您不必监听任何drain事件或进行任何内部状态标记。

附带说明,您可能应该只关心maxDroppedFrames / second,而不是绝对总数。您也不应该使用binary编码进行编写,而只需编写原始缓冲区即可。

此外,这可能仅适用于节点 0.10+

于 2013-09-20T11:58:06.370 回答