node.js - 触发并行 1k HTTP 请求会卡住

标签 node.js event-loop

问题是当您触发 1k-2k 传出 HTTP 请求时实际发生了什么?
我看到它可以通过 500 个连接轻松解决所有连接,但是从那里向上移动似乎会导致问题,因为连接保持打开状态并且 Node 应用程序将卡在那里。使用本地服务器 + 示例 Google 和其他模拟服务器进行测试。

因此,对于一些不同的服务器端点,我确实收到了原因:读取 ECONNRESET 很好,服务器无法处理请求并抛出错误。在 1k-2k 请求范围内,程序将挂起。当您使用 lsof -r 2 -i -a 检查打开的连接时你可以看到有一些 X 数量的连接一直卡在那里 0t0 TCP 192.168.0.20:54831->lk-in-f100.1e100.net:https (ESTABLISHED) .当您向请求添加超时设置时,这些可能最终会出现超时错误,但为什么否则连接会永远保持下去并且主程序最终会处于某种不确定状态?

示例代码:

import fetch from 'node-fetch';

(async () => {
  const promises = Array(1000).fill(1).map(async (_value, index) => {
    const url = 'https://google.com';
    const response = await fetch(url, {
      // timeout: 15e3,
      // headers: { Connection: 'keep-alive' }
    });
    if (response.statusText !== 'OK') {
      console.log('No ok received', index);
    }
    return response;
  })

  try {
    await Promise.all(promises);
  } catch (e) {
    console.error(e);
  }
  console.log('Done');
})();

最佳答案

为了确切了解发生了什么,我需要对您的脚本进行一些修改,但这里有。

首先,您可能知道如何node和它的 event loop有效,但让我快速回顾一下。运行脚本时,node运行时首先运行它的同步部分,然后调度 promisestimers在下一个循环中执行,当检查它们被解析时,在另一个循环中运行回调。这个简单的要点很好地解释了它,归功于@StephenGrider:


const pendingTimers = [];
const pendingOSTasks = [];
const pendingOperations = [];

// New timers, tasks, operations are recorded from myFile running
myFile.runContents();

function shouldContinue() {
  // Check one: Any pending setTimeout, setInterval, setImmediate?
  // Check two: Any pending OS tasks? (Like server listening to port)
  // Check three: Any pending long running operations? (Like fs module)
  return (
    pendingTimers.length || pendingOSTasks.length || pendingOperations.length
  );
}

// Entire body executes in one 'tick'
while (shouldContinue()) {
  // 1) Node looks at pendingTimers and sees if any functions
  // are ready to be called.  setTimeout, setInterval
  // 2) Node looks at pendingOSTasks and pendingOperations
  // and calls relevant callbacks
  // 3) Pause execution. Continue when...
  //  - a new pendingOSTask is done
  //  - a new pendingOperation is done
  //  - a timer is about to complete
  // 4) Look at pendingTimers. Call any setImmediate
  // 5) Handle any 'close' events
}

// exit back to terminal

Note that the event loop will never end until there is pending OS tasks. In other words, your node execution will never end until there is pending HTTP requests.



在您的情况下,它运行 async函数,因为它总是返回一个 promise ,它会安排它在下一次循环迭代中执行。在你的异步函数上,你安排了另一个 1000 立即 promise (HTTP 请求) map迭代。在那之后,您正在等待所有然后解决完成该程序。它肯定会起作用,除非您在 map 上使用匿名箭头函数不扔任何 错误 .如果你的一个 promise 抛出了一个错误而你没有处理它,一些 promise 将不会调用它们的回调,从而使程序运行到 结束 但不是到 退出 ,因为事件循环会阻止它退出,直到它解决所有任务,即使没有回调。正如 Promise.all 上所说的那样docs :它会在第一个 promise 拒绝后立即拒绝。

所以,你在 ECONNRESET错误与 Node 本身无关,与您的网络有关,它使提取引发错误,然后阻止事件循环结束。通过这个小修复,您将能够看到异步解决的所有请求:

const fetch = require("node-fetch");

(async () => {
  try {
    const promises = Array(1000)
      .fill(1)
      .map(async (_value, index) => {
        try {
          const url = "https://google.com/";
          const response = await fetch(url);
          console.log(index, response.statusText);
          return response;
        } catch (e) {
          console.error(index, e.message);
        }
      });
    await Promise.all(promises);
  } catch (e) {
    console.error(e);
  } finally {
    console.log("Done");
  }
})();

关于node.js - 触发并行 1k HTTP 请求会卡住,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59778880/

相关文章:

javascript - 无法将 jwt token 添加到 node.js 响应

node.js - Node 的事件循环阶段回调

arrays - Mongoose - 填充二维数组

javascript - 如何找出阻止 Node.js 退出的原因?

windows - 为什么在 asyncio 事件循环运行时我无法捕获 SIGINT?

javascript - Process.nextTick 和 Promise 回调

javascript - 这个 JavaScript 示例是否创建 “race conditions” ? (只要它们可以存在于 JavaScript 中)

node.js - 安装项目时"Error: %1 is not a valid Win32 application .. Failed to load c++ bson extension"

javascript - Graphql 和 Relay 可扩展性

javascript - 如何在特定的 URL 中启动 Node.js?