为什么随着循环变长,Node-Fetch 在 600 次重复的 运行 循环中需要越来越长的时间来获取?

Why does Node-Fetch Increasingly Take Longer to Fetch in a Running Loop for 600 Reps As Loop Gets Longer?

我正在测试 node-fetch 并决定在项目中使用它。在这个项目中,我在几分钟内重复获取资源,多达 600 次。然而,当我测试 Node-Fetch 时,我发现了一些奇怪的行为:当我循环获取时,获取所需的时间会越来越长。我通过生成迭代次数 i 的时间戳(参见 here)证明了这一点。这是我的测试代码:

for(let i = 0; i < 600; i++){
    var start_time = new Date().getTime();
    fetch('http://localhost:3000').then(a=>{
        var time = (new Date().getTime() - start_time) + 'ms'
        console.log(time + ' - ' + i)
    })
}

有办法解决这个问题吗?我认为这与线程有关,是否有 http/https 模块解决方法?

此外,i 迭代次数乱序是什么原因?

编辑:

我意识到获取被邮递员严重限制并导致长时间等待的原因,但是,当我托管一个简单的本地主机应用程序并将迭代次数增加到 60000 时,它花费的时间比预期的要长得多, 并减少了大约 30000 次迭代。是否有一个原因?这是结束日志:

15059ms - 15136
15059ms - 15137
15060ms - 15138
15060ms - 15140
15060ms - 15139
15061ms - 15142
15061ms - 15141
15061ms - 15143
15061ms - 15144
15062ms - 15145
15062ms - 15147
15062ms - 15146
15063ms - 15148
15063ms - 15149
15063ms - 15150
15064ms - 15152
15064ms - 15151
15064ms - 15153
15066ms - 15155
15066ms - 15154
15067ms - 15156
15067ms - 15157
15067ms - 15158
15067ms - 15159
15070ms - 15160
15073ms - 15161
15074ms - 15162
15074ms - 15163
15074ms - 15164
15074ms - 15165
15075ms - 15166
15075ms - 15167
15076ms - 15168
15076ms - 15169
15076ms - 15170
15077ms - 15171
15077ms - 15172
15077ms - 15173
15077ms - 15174
15078ms - 15175
15078ms - 15176
15078ms - 15177
15081ms - 15178
15081ms - 15179
15082ms - 15180
15082ms - 15181
15082ms - 15182
15083ms - 15183
15083ms - 15184
15083ms - 15185
15085ms - 15186
15086ms - 15187
15086ms - 15188
15086ms - 15189
15087ms - 34758

是否有以下原因:1) 迭代次数大大低于预期,以及 2) 以毫秒为单位的时间大大高于预期?这是编辑后的测试代码:

for(let i = 0; i < 60000; i++){
    var start_time = new Date().getTime();
    get('http://localhost:3000').text().then(a=>{
        var time = (new Date().getTime() - start_time) + 'ms'
        console.log(time + ' - ' + i)
    })
}

谢谢!

如评论中所述,这可能是由于服务器端的速率限制所致。 运行 在本地进行相同的测试可能会产生不同的结果。

编辑: 一段时间后,您的本地主机服务器将充满请求,并且必须减轻负载,这就是它随着时间的推移变得越来越慢的原因。这里有一个经验法则可以帮助您:http 请求很昂贵,这是您应该避免的第一个瓶颈。

解决这个问题的另一种方法是在服务器前面添加一个负载平衡器,这样您就可以将服务器扩展到多台机器。

最后,您需要在从服务器获取数据的不同方式(可能使用套接字连接,当服务器端发生变化时只允许您接收数据)或扩展您的服务器之间做出决定.

这是一个很多问题,但是..

为什么会这样?

代码看起来不错,很可能您正在攻击的网站有速率限制(在它有意开始减慢您的请求之前,您只能攻击它 x 次 times/minute,以防止 DoS 攻击)。

有解决办法吗?

如果你的速度变慢是由于网站上的速率限制(尝试另一个没有速率限制的网站,也许 google.com 之类的东西会起作用),那么不会。

迭代次数 (i) 乱序是否有原因?

是的。您正在异步发送所有 请求 。记录输出的顺序取决于收到响应的时间。如果它们收到时乱序,您的 is 将乱序。

已编辑问题的答案:

如果不进一步了解您的本地设置,就很难知道。许多事情可能导致减速。

可能的瓶颈:

  • 服务器响应缓慢。可能服务器(您的 HTTP 请求的端点)需要很长时间才能回复。您可以通过查阅服务器日志来确定服务器响应时间,或者尝试不受速率限制的 Web 服务。像 nginx 提供静态文件的本地服务器应该表现得很好。
  • OS 减速。操作系统可以处理的最大同时 TCP 连接数是有限制的。当您或多或少并行打开所有这些连接时,您的 60k 请求接近 65535 个打开连接的最大值。如果您 运行 在同一台机器上连接本地服务器,那么您也会用完一个套接字来响应每个 TCP 连接——您将拥有理论最大值的一半可用套接字。
  • 达到节点 libuv 线程池限制。在 Node 中执行 IO 的异步任务是 运行 来自线程池。该线程池的默认大小为 4,这意味着一次只能 运行 异步执行 4 个 IO 任务。尝试通过将 UV_THREADPOOL_SIZE 设置为更大的数字(例如 100400)来增加此限制,并查看可以发送的请求数量是否按比例增加没有减速,或者减速量减少。
  • 内存垃圾回收。您的代码不包含很多变量,但您使用的库 (get) 可能包含。如果是这种情况,那么更改库或使用 node 内置插件进行 HTTP 提取将导致减速发生明显变化