与浏览器相比,通过 Node.js 的 HTTP 请求延迟

Delays in HTTP requests via Node.js compared to browser

使用 Node.js 通过 HTTP 请求查询一些 public API。因此,我正在使用 request 模块。我正在测量我的应用程序中的响应时间,发现我的应用程序 return 来自 API 查询的结果比 "direct" 通过 curl 或在浏览器中的请求慢大约 2-3 倍。此外,我注意到与启用 HTTPS 的服务的连接通常比普通 HTTP 服务需要更长的时间,但这可能是巧合。

我尝试优化我的 request 选项,但无济于事。比如我查询

https://www.linkedin.com/countserv/count/share?url=http%3A%2F%2Fwww.google.com%2F&lang=en_US

我正在使用 request.defaults 为所有请求设置整体默认值:

var baseRequest = request.defaults({
    pool: {maxSockets: Infinity},
    jar: true,
    json: true,
    timeout: 5000,
    gzip: true,
    headers: {
        'Content-Type': 'application/json'
    }
});

实际请求是通过

完成的
...
var start = new Date().getTime();

var options = {
    url: 'https://www.linkedin.com/countserv/count/share?url=http%3A%2F%2Fwww.google.com%2F&lang=en_US',
    method: 'GET'
};

baseRequest(options, function(error, response, body) {

    if (error) {
        console.log(error);
    } else {
        console.log((new Date().getTime()-start) + ": " + response.statusCode);
    }

});

有人看到优化潜力吗?我做错了什么吗?在此先感谢您的任何建议!

其实,我有一些新的元素,足以打开一个真正的答案。看看方法 request uses the HTTP agent 你能试试下面的方法吗:

var baseRequest = request.defaults({
    pool: false,
    agent: false,
    jar: true,
    json: true,
    timeout: 5000,
    gzip: true,
    headers: {
        'Content-Type': 'application/json'
    }
});

这将禁用连接池并使其速度更快。

根据我对您的体系结构的了解,您需要解决几个潜在的问题。排名不分先后:

  • 使用 request 总是比直接使用 http 慢,因为正如智者曾经说过的:"abstraction costs"。 ;) 事实上,为了最大限度地发挥性能,我会直接使用节点的 net 模块处理所有 HTTP 请求。对于 HTTPS,不值得重写 https 模块。郑重声明,根据定义,HTTPS 总是比 HTTP 慢,因为既需要握手加密密钥,又需要对有效负载执行 crypt/decrypt 工作。
  • 如果您的要求包括从任何单个服务器检索多个资源,请确保这些请求按顺序发出并设置了 http KeepAlive,以便您可以从已经打开的套接字中受益。与在已打开的套接字上发出请求相比,握手新 TCP 套接字所需的时间巨大
  • 确保禁用 http 连接池(请参阅 Nodejs Max Socket Pooling Settings
  • 确保您的操作系统和 shell 没有限制可用套接字的数量。有关提示,请参阅 How many socket connections possible?
  • 如果您使用的是 linux,请检查 Increasing the maximum number of tcp/ip connections in linux,我还强烈建议微调内核套接字缓冲区。

我会根据自己的想法添加更多建议。

更新

更多关于对同一端点的多个请求的主题:

如果您需要从同一个端点检索大量资源,将您的请求分段到保持与该端点的开放连接的特定工作人员会很有用。这样,您可以放心,您可以尽快获得请求的资源,而无需初始 TCP 握手的开销。

TCP 握手是一个三阶段过程。

第一步:客户端向远程服务器发送一个SYN包。 第二步:远程服务器用SYN+ACK回复客户端。 第三步:客户端用ACK回复远程服务器。

根据客户端对远程服务器的延迟,这可能加起来(正如 William Proxmire 曾经说过的)"real money",或者在这种情况下,延迟。

在我的桌面上,2K 八位字节数据包到 www.google.com 的当前延迟(通过 ping 测量的往返时间)在 37 到 227 毫秒之间。

因此假设我们可以依赖 95 毫秒的往返平均值(通过完美连接),初始 TCP 握手的时间约为 130 毫秒或 SYN(45ms) + SYN+ACK(45ms) + ACK(45ms),这是建立初始连接的十分之一秒。

如果连接需要重新传输,可能需要 很多 的时间。

这是假设您通过新的 TCP 连接检索单个资源。

为了改善这个问题,我会让你的工作人员保留一个到 "known" 目的地的开放连接池,然后他们会将这些连接广告回主管进程,这样它就可以将请求定向到负载最少的服务器"live" 连接到目标服务器。