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

Posted

技术标签:

【中文标题】与浏览器相比,通过 Node.js 的 HTTP 请求延迟【英文标题】:Delays in HTTP requests via Node.js compared to browser 【发布时间】:2015-05-07 19:28:44 【问题描述】:

使用 Node.js 通过 HTTP 请求查询一些公共 API。因此,我使用request 模块。我正在测量我的应用程序中的响应时间,并看到我的应用程序从 API 查询返回的结果比通过 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);
    

);

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

【问题讨论】:

你是从你的 node.js 代码发出的请求和来自同一台机器的 curl 请求吗? @TristanFoureur 是的,我愿意。我认为这种行为可能是由某些请求选项引起的,但我似乎无法找出选择哪些选项以获得最佳性能。 我只是尝试了您的代码,没有进行任何更改。您的代码的平均响应时间为 545 毫秒,多次 curl 调用的平均响应时间为 550 毫秒。 为了提供更多细节,我正在运行多个工作进程,用于通过 RabbitMQ 支持的分布式 RPC 系统向公共 API 发出 http 请求。这意味着每个节点进程可以有数百个并发的“打开”请求。我看到在较高工作负载下的延迟,简单(少量)请求工作正常......所以,我猜可能需要对请求选项进行一些调整...... 那你可能想看看hyperquest它可能对你有帮助。个人而言,当我必须做很多这样的请求时,我会使用一些带有 X 个工作人员的作业队列来确保我保持低于 X 个并发请求。 【参考方案1】:

鉴于我从您的架构中了解到的情况,您需要解决几个潜在问题。它们没有特定的顺序:

使用request 总是比直接使用http 慢,因为正如智者所说:“抽象成本”。 ;) 事实上,为了充分发挥性能,我会直接使用节点的 net 模块处理所有 HTTP 请求。对于 HTTPS,不值得重写 https 模块。需要说明的是,由于需要握手加密密钥以及对有效负载进行加密/解密工作,HTTPS 始终比 HTTP 慢。 如果您的要求包括从任何单个服务器检索多个资源,请确保这些请求是按照 http KeepAlive 集的顺序发出的,这样您就可以从已经打开的套接字中受益。与在已打开的套接字上发出请求相比,握手新 TCP 套接字所需的时间巨大。 确保禁用 http 连接池(请参阅Nodejs Max Socket Pooling Settings) 确保您的操作系统和外壳不限制可用套接字的数量。有关提示,请参阅 How many socket connections possible?。 如果您使用的是 linux,请查看Increasing the maximum number of tcp/ip connections in linux,我还强烈建议您微调内核套接字缓冲区。

我会在遇到更多建议时添加它们。

更新

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

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

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

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

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

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

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

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

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

为了改善这种情况,我会让您的工作人员保持一个与“已知”目的地的开放连接池,然后他们会将这些连接池通告回主管进程,以便它可以将请求定向到具有“实时”连接的负载最少的服务器到目标服务器。

【讨论】:

哇,非常感谢您的广泛回答。我会尝试在周末对此进行测试。 再次感谢您的回答。我认为 Keep-Alive 标头可能会产生最大的影响,除了从 Node.js 禁用 http 连接池。不幸的是,我不能真正按请求的端点对请求进行分区,因为我想平均分配负载,因此通过 RabbitMQ 对我的 RPC 工作人员进行循环。但无论如何都是好主意!【参考方案2】:

实际上,我有一些新元素足以打开一个真正的答案。看看路requestuses the HTTP agent可以试试下面的:

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

这将禁用连接池,应该会更快。

【讨论】:

谢谢,我试试看! 你是对的,这是对他提出的大量请求的整体速度的权衡,这就是为什么我只要求他尝试一下,看看效果如何表现在他的特定用例中

以上是关于与浏览器相比,通过 Node.js 的 HTTP 请求延迟的主要内容,如果未能解决你的问题,请参考以下文章

node.js中HTTP请求与响应处理

与 Apache 相比,Node.js 的性能如何?

Firebase Admin SDK - 与Node.JS相比,Java版本缺乏什么?

使Node.js中的http.request适用于浏览器

node.js HTTP模块URL 模块

Node.js Web 模块