Node.js Event Loop 处理的几大周期如下图所示:
-
Timer:通过 setTimeout() 或 setInterval() 安排的一切都将在这里处理。
-
IO 回调:这里将处理大部分回调。 由于 Node.js 中的所有用户态代码基本上都在回调中(例如,对传入 http 请求的回调会触发级联回调),这就是用户态代码。
-
IO轮询:轮询下一次运行要处理的新事件。
-
Set Immediate:运行通过 setImmediate() 注册的所有回调。
-
close:这里处理了所有 on('close') 事件回调。
实际上 Node 应用程序中发生的一切都通过事件循环运行。这意味着,如果我们可以从中获取指标,它们应该会为我们提供有关应用程序整体健康状况和性能的宝贵信息。
如果应用程序处于空闲状态,这意味着没有待处理的任务(定时器、回调等),全速运行这些阶段是没有意义的,因此事件循环将适应这种情况并在 等待新的外部事件进入的轮询阶段。
这也意味着,无负载下的指标与高负载下与慢速后端通信的应用程序相似(低频率、高持续时间)。
Event Loop Latency
事件循环延迟衡量在使用 setTimeout(X) 安排的任务真正得到处理之前还需要多长时间。
高事件循环延迟表示事件循环忙于处理回调。
Node.js 应用程序在单个线程上运行。 在多核机器上,这意味着负载不会分布在所有内核上。 使用 Node 附带的集群模块,可以很容易地为每个 CPU 生成一个子进程。 每个子进程维护自己的事件循环,主进程透明地在所有子进程之间分配负载。
如前所述,libuv 将创建一个大小为 4 的线程池。可以通过设置环境变量 UV_THREADPOOL_SIZE 来覆盖池的默认大小。
虽然这可以解决 I/O 密集型应用程序的负载问题,但在生产系统上进行这项改动之前,务必进行负载测试,因为更大的线程池可能仍会耗尽内存或 CPU.