Nodejs单线程为什么能支持高并发?

Nodejs运行机制

Nodejs单线程为什么能支持高并发?_第1张图片
2017-07-03-2.png
  1. V8引擎解析JavaScript脚本
  2. 解析后的代码,调用Node API
  3. libuv库负责Node API的执行。它将不同的任务分配给不同的线程,形成一个Event Loop(事件循环),以异步的方式将任务的执行结果返回给V8引擎
  4. V8引擎再将结果返回给用户

Node.js的单线程指的是主线程是“单线程”,由主要线程去按照编码顺序一步步执行程序代码,假如遇到同步代码阻塞,主线程被占用,后续的程序代码执行就会被卡住。实践一个测试代码:

const http = require('http');
function sleep(time) {    
  const _exit = Date.now() + time * 1000;    
  while( Date.now() < _exit ) {}    
  return ;
}
const server = http.createServer(function(req, res){
    sleep(10);
    res.end('server sleep 10s');
});

server.listen(8080);

先将index.js的代码改成这样,然后打开浏览器,你会发现浏览器在10秒之后才做出反应,打出Hello Node.js。

这是主线程时序图:

JavaScript是解析性语言,代码按照编码顺序一行一行被压进stack里面执行,执行完成后移除然后继续压下一行代码块进去执行。上面代码块的堆栈图,当主线程接受了request后,程序被压进同步执行的sleep执行块(我们假设这里就是程序的业务处理),如果在这10s内有第二个request进来就会被压进stack里面等待10s执行完成后再进一步处理下一个请求,后面的请求都会被挂起等待前面的同步执行完成后再执行。

那么我们会疑问:为什么一个单线程的效率可以这么高,同时处理数万级的并发而不会造成阻塞呢?就是我们下面所说的--------事件驱动。

事件驱动/事件循环/线程池

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

单线程的好处:

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

单线程的劣势:

  • CPU密集型任务占用CPU时间长(可通过cluster方式解决)
  • 无法利用CPU的多核(可通过cluster方式解决)
  • 单线程抛出异常使得程序停止(可通过try catch方式或自动重启机制解决)

你可能感兴趣的:(Nodejs单线程为什么能支持高并发?)