我注意到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();
这似乎并不直观。查看Firebug的网络选项卡,增加/差异都来自于接收(另一方面,两者的等待时间都是16ms(。
我做了以下更改来修复它,使两种情况都有16ms的响应时间:
response.writeHead(200, {"Content-Type": "application/json"});
response.end(JSON.stringify(content, null, 0));
我浏览了node.js文档,但到目前为止还没有找到相关信息。我想这与缓冲有关,但node.js能在write()
和end()
之间抢占吗?
更新:
这在Linux的0.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('0rn' + this._trailer + 'rn'); // Last chunk.
this._writeRaw(chunk);
this.connection.write(chunk);
第二个好版本只有1个Socket.write调用:
writeHead(...)
end(chunk)
var l = Buffer.byteLength(chunk).toString(16);
ret = this.connection.write(this._header + l + CRLF +
chunk + 'rn0rn' +
this._trailer + 'rn', encoding);
仍然不确定是什么原因导致第一个版本在较小的响应大小下无法正常工作。
简短回答:
您可以显式设置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();
事实:
经过几次实验,我发现如果content
足够小(在我的情况下,小于1310字节(,单个MTU可以继续运行,则响应时间约为200ms。然而,对于任何大于该值的content
,响应时间大约为20ms。
然后我使用wireshark捕获服务器端的网络包。以下是一个典型的结果:
对于小型content
:
- [00000ms]
response.write(content)
- [02000ms]收到来自客户端的ACK包
- [0021ms]
response.end()
对于较大的content
:
- [00000ms]
response.write(content)
//发送第一个MTU - [0001ms]发送第二个MTU
- [0070ms]收到来自客户端的ACK包
- [0071ms]
response.end()
可能的解释:
如果未设置Content-Length
标头,则数据将以"分块"模式传输。在"分块"模式下,服务器和客户端都不知道数据的确切长度,因此客户端会等待一段时间(200ms(来查看是否有以下包。
然而,这种解释提出了另一个问题:为什么在较大的content
情况下,客户端没有等待200ms(相反,它只等待了50ms左右(