node.js response.write(data)在数据大小很小的情况下花费很长时间

bry*_*sai 5 node.js

我注意到node.js中以下代码性能的奇怪行为.当大小content为1.4KB时,请求的响应时间大约为16ms.但是,当大小content只有988字节时,请求的响应时间奇怪得多,大约200ms:

response.writeHead(200, {"Content-Type": "application/json"});
response.write(JSON.stringify(content, null, 0));
response.end();
Run Code Online (Sandbox Code Playgroud)

这似乎不直观.查看Firebug的网络标签,增加/差异全部来自接收(另一方面等待两者都是16ms).

我做了以下更改来修复它,以便两个案例都有16毫秒的响应时间:

response.writeHead(200, {"Content-Type": "application/json"});
response.end(JSON.stringify(content, null, 0));
Run Code Online (Sandbox Code Playgroud)

我查看了node.js 文档,但到目前为止还没有找到相关信息.我的猜测这与缓冲有关,但是node.js可以在write()和之间抢占end()吗?

更新:

这是在Linux上的v0.10.1上测试的.

我试图窥视并确定了2路径之间的区别.第一个版本有2个Socket.write调用.

writeHead(...)
write(chunk)
  chunk = Buffer.byteLength(chunk).toString(16) + CRLF + chunk + CRLF;
  ret = this._send(chunk);
    this._writeRaw(chunk);
      this.connection.write(chunk);
end()
  ret = this._send('0\r\n' + this._trailer + '\r\n'); // Last chunk.
    this._writeRaw(chunk);
      this.connection.write(chunk);
Run Code Online (Sandbox Code Playgroud)

第二个好的版本只有1个Socket.write调用:

writeHead(...)
end(chunk)
  var l = Buffer.byteLength(chunk).toString(16);
  ret = this.connection.write(this._header + l + CRLF +
                              chunk + '\r\n0\r\n' +
                              this._trailer + '\r\n', encoding);
Run Code Online (Sandbox Code Playgroud)

仍然不确定是什么让第一个版本在响应尺寸较小时效果不佳.

Cal*_*ang 9

简短回答:

您可以显式设置 Content-Length 标头.它将响应时间从大约200ms减少到20ms.

var body = JSON.stringify(content, null, 0);
response.writeHead(200, {
    "Content-Type": "application/json",
    'Content-Length': body.length
});
response.write(content);
response.end();
Run Code Online (Sandbox Code Playgroud)

事实:

经过几次实验后,我发现如果 content 单个MTU继续运行的时间足够小(在我的情况下,小于1310个字节),响应时间大约为200ms.但是,对于content大于该值的任何值,响应时间大约为20ms.

然后我使用wireshark来捕获服务器端的网络包.以下是典型结果:

对于小content:

  • [0000ms]response.write(content)
  • [0200ms]从客户端收到ACK包
  • [0201ms]response.end()

更大的content:

  • [0000ms] response.write(content)//发送第一个MTU
  • [0001ms]发送第二MTU
  • [0070ms]从客户端接收ACK包
  • [0071ms]response.end()

可能的解释:

如果Content-Length未设置标头,则数据将以"Chunked"模式传输.在"Chunked"模式下,服务器和客户端都不知道数据的确切长度,因此客户端将等待(200ms)以查看是否有任何后续包.

然而,这个解释提出了另一个问题:为什么在更大的 content 情况下,客户端没有等待200ms(相反,它只等待大约50ms)?