Published on

是什么造就了Node.js的高并发

  1. 每个 Node.js 进程只有一个主线程在执行程序代码,形成一个执行栈(execution context stack)。
  2. 主线程之外,还维护了一个"事件队列"(Event queue)。当用户的网络请求或者其它的异步操作到来时,node 都会把它放到 Event Queue 之中,此时并不会立即执行它,代码也不会被阻塞,继续往下走,直到主线程代码执行完毕。
  3. 主线程代码执行完毕完成后,然后通过 Event Loop,也就是事件循环机制,开始到 Event Queue 的开头取出第一个事件,从线程池中分配一个线程去执行这个事件,接下来继续取出第二个事件,再从线程池中分配一个线程去执行,然后第三个,第四个。主线程不断的检查事件队列中是否有未执行的事件,直到事件队列中所有事件都执行完了,此后每当有新的事件加入到事件队列中,都会通知主线程按顺序取出交 EventLoop 处理。当有事件执行完毕后,会通知主线程,主线程执行回调,线程归还给线程池。
  4. 主线程不断重复上面的第三步。

异步 I/O

首先,node 的 异步 I/O !== 非阻塞 I/O非阻塞 I/O 调用后会虽然也是立即返回,但是应用层会不断的重复 I/O 操作去轮询系统是否完成数据读取,让 CPU 处理状态判断,对 CPU 造成资源的浪费。

然而在 异步 I/O 里,基于多子线程的方式去解决了 非阻塞 I/O 的问题,主线程发起 I/O 请求后,就不再过问情况了。然后让子线程来完成数据获取,当读写完成后通知主线程。

事件循环

当子线程完成并通知主线程后,事件循环类似于 while(true),在每次循环的时候,都会去检查一下是否有事件待处理,如果有就取出执行,否则继续下一次循环。 每次事件循环中有一个或者多个观察者,按照优先级依次进行询问和处理。 事件循环是一个典型的生产/消费模型,异步 I/O、网络请求等是事件的生产者,这些事件被传递到对应的观察者那里,等待事件循环取出并处理。

单线程

单线程的好处:

  • 多线程占用内存高
  • 多线程间切换使得 CPU 开销大
  • 多线程由内存同步开销
  • 编写单线程程序简单
  • 线程安全

反而基于单主线程的方式,也就是 Node 的异步 I/O+事件循环,无须为每一个请求创建额外的对应线程,可以省掉创建和销毁线程的开销,同时操作系统在调度任务时因为线程较少,上下文切换的代价很低。