理解Node.Js事件循环,写出高性能代码。

Node.Js事件循环

文章首发于个人Githubnode

事件循环是什么

事件循环执行各阶段解析

首先让咱们看看事件循环是如何设计的,作了什么,才能更好的理解它。上图(来源Node官网)解释了事件循环的6个阶段及执行顺序。下面咱们开始探索各阶段都作了什么?git

  • timers: 此阶段执行由setTimeout()和setInterval()注册的callback。
  • pending callbacks: 此阶段执行某些系统操做(如TCP错误类型)的回调。例如,若是尝试链接时TCP套接字收到ECONNREFUSED,则错误回调在这个阶段执行。
  • idle, prepare: 只在内部使用(忽略)
  • pool: 检索新的IO事件,执行相关IO的callback,node会在适当的时候阻塞。
  • check: 此阶段执行由setImmediate() 注册的callbacks
  • close callbacks: 该阶段执行close 类型的callback,好比 socket.on('close', ...)。

每一个阶段执行本身独立的FIFO队列里面注册的callback。每种类型的回调会注册到各自的阶段中。github

简单的说:JS 代码中的回调最终会到event loop 中注册为事件,event loop 把任务分配给 OS | thread pool (如今操做系统都提供的异步的接口,会优先选择此选项),最后 event loop 轮询OS任务完成的事件,取出结果,而后去event queue 中执行对于的callback。后端

事件循环的理解误区

  • 一个主线程执行用户代码,另一个线程执行事件循环。每个异步操做发生时,主线程把工做交给事件循环线程,一旦工做完成,事件循环线程将会通知主线程执行回调。

事件循环的真实状况

  • 仅仅只有一个线程来执行用户代码和事件循环(即事件执行循环的线程来执行用户代码),其实Node.Js中的每一个用户代码都是在事件循环中被执行的。
  • 经过下面案例来证实现实的运行状况:
    const fs = require('fs');
    
    fs.readFile('testFile1.txt', function (err, data) {
        console.log('data read of testFile0.txt');
    
        // 模拟CPU密集型任务
        computing()
    });
    
    fs.readFile('testFile2.txt', function (err, data) {
        console.log('data read of testFile1.txt');
    });
    
    // CPU密集型任务
    function computing() {
        for (let i = 0; i < 10000; i++)//
        {
            for (let j = 0; j < 10000; j++) {
                for (let k = 0; k < 100; k++) { }
            }
        }
    }
    // 输出结果:
    // 11:55:30 AM  data read of testFile0.txt
    // 11:55:56 AM  data read of testFile1.txt
    复制代码
  • 经过运行结果,发现第二个回调比第一个回调延时了26秒。
  • 那是由于第二个回调函数在第一个回调后面执行,由于computing函数占用的CPU26秒,阻断了事件循环的进行,所以它延时了。
  • 也说明他们共用一个线程,回调函数一个一个(在事件循环中)被顺序执行的。

libuv thread pool

  • libuv 默认会建立一个线程池,包含4个线程。用于处理异步任务。可是如今的操做系统已经为I/O任务提供了异步的接口。因此回会优先使用异步接口,避免使用线程池,除非没法避免的时候。

经过指标分析事件循环是否健康

  • Tick Frequency tick频率(event loop 轮询频率)。
  • Tick Duration tick 间隔时间(event loop 两次轮询间的隔时间)。
  • Event Loop Tick 的频率越高,说明集群越健康。
  • 若是后端IO处理速度很慢,将会形成event loop 轮询频率下降。
  • Event Loop Latency 事件循环延时。
  • 事件循环延时若是明显增高,说明但代码中存在CPU密集型任务,须要优化。

总结

  • 事件循环的回调函数不会占用太多 CPU 的计算能力. 由于一旦发生了上述状况, 则意味着事件循环的执行速度会减慢, 事件得不到及时的处理。
  • Node.js 中同一个时刻只会有一个回调函数被执行。
相关文章
相关标签/搜索