单线程的Node.js是如何实现高并发的
作者:互联网
nodejs单进程单线程事件驱动
Node遵循的是单线程单进程的模式,node的单线程是指js的引擎只有一个实例,且在nodejs的主线程中执行,同时node以事件驱动的方式处理IO等异步操作。node的单线程模式,只维持一个主线程,大大减少了线程间切换的开销,但是会有多个worker线程,用于执行异步操作。
但是node的单线程使得在主线程不能进行CPU密集型操作,否则会阻塞主线程。对于CPU密集型操作,在node中通过 child_process 可以创建独立的子进程,父子进程通过IPC通信,子进程可以是外部应用也可以是node子程序,子进程执行后可以将结果返回给父进程。
Node.js的运行机制
- V8引擎解析JavaScript脚本。
- 解析后的代码,调用Node API。
- libuv库负责Node API的执行。它将不同的任务分配给不同的worker线程,形成一个Event Loop(事件循环),以异步的方式将任务的执行结果返回给V8引擎。
- V8引擎再将结果返回给用户。
- 应用层: 即 JavaScript 交互层,常见的就是 Node.js 的模块,比如 http,fs
- V8引擎层: 即利用 V8 引擎来解析JavaScript 语法,进而和下层 API 交互
- NodeAPI层: 为上层模块提供系统调用,一般是由 C 语言来实现,和操作系统进行交互 。
- LIBUV层: 是跨平台的底层封装,实现了 事件循环、文件操作等,是 Node.js 实现异步的核心
Node.js 事件循环
Node.js 通常情况下是单进程的。
- 主线程运行 V8 和 Javascript
- 多个子线程通过 事件循环 被调度
事件循环: 事件循环是一种编程构造,用于等待和分派程序中的事件或消息, 主线程从"任务队列"中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为Event Loop(事件循环)
事件队列: 当用户的网络请求或者其它的异步操作到来时,node都会把它放到Event Queue之中,此时并不会立即执行它,代码也不会被阻塞,继续往下走,直到主线程代码执行完毕。
任务队列: 任务队列"是一个事件的队列(也可以理解成消息的队列),IO设备完成一项任务,就在"任务队列"中添加一个事件,表示相关的异步任务可以进入"执行栈"了。主线程读取"任务队列",就是读取里面有哪些事件。
事件驱动: 实质是通过主循环加事件触发方式运行程序 node Node.js 不是一门语言也不是框架,它只是基于 Google V8 引擎的 JavaScript 运行时环境,是对 js 功能的拓展。提供了网络、文件、dns 解析、进程线程等功能。
libuv libuv是专门为Node.js开发的一个封装库,提供跨平台的异步I/O能力。
注意:
- 一个事件循环有一个或多个任务队列。一个任务队列是一组的任务
- Libuv 主要是,利用系统提供的事件驱动模块解决网络异步 IO,利用线程池解决文件IO。另外还实现了定时器,对进程、线程等使用进行了封装。
其实这里的事件循环和js在浏览器的事件循环是一样的,主线程允许同步代码,异步代码放到对应的工作线程中执行,回调执行结果后放进事件队列,待主线程执行事件队列的任务。
事件驱动+事件循环实现高并发
具体执行顺序:
1、每个Node.js进程只有一个主线程在执行程序代码,形成一个执行栈(execution context stack)
2、主线程之外,还维护了一个"事件队列"(Event queue)。当用户的网络请求或者其它的异步操作到来时,node都会把它放到Event Queue之中,此时并不会立即执行它,代码也不会被阻塞,继续往下走,直到主线程代码执行完毕。
3、主线程代码执行完毕完成后,然后通过Event Loop,也就是事件循环机制,开始到Event Queue的开头取出第一个事件,从线程池中分配一个线程去执行这个事件,接下来继续取出第二个事件,再从线程池中分配一个线程去执行,然后第三个,第四个。主线程不断的检查事件队列中是否有未执行的事件,直到事件队列中所有事件都执行完了,此后每当有新的事件加入到事件队列中,都会通知主线程按顺序取出交EventLoop处理。当有事件执行完毕后,会通知主线程,主线程执行回调,线程归还给线程池。
- 我们所说的node.js单线程只是一个js主线程与ui渲染共享一个线程,本质上的异步操作还是由线程池完成的,node将所有的阻塞操作都交给了内部的线程池去实现,本身只负责不断的往返调度,并没有进行真正的I/O操作,从而实现异步非阻塞I/O,这便是node单线程和事件驱动的精髓之处了。
总结:
1、libuv 线程池默认打开 4 个,最多打开 128个 线程。(例如:以前 web 服务器同一时间比如说最多只能接收 100 个请求,多的就无法接收了,服务器就挂掉了。nodejs 所谓的高并发是指可以同时接收 1000、10000 个请求,只不过以排队的方式在等待。)
2、主线程执行js,是单线程的,js代码在做大量计算就是cpu密集了。主线程不空闲出来也没法处理 io 的事,所以就会阻塞了。
3、回调只能保证某个请求按照顺序执行,不能保证多个请求访问一个资源的先后顺序,多个请求访问一个资源是要加锁的,用事务加锁就行。
标签:Node,node,单线程,主线,js,线程,事件 来源: https://www.cnblogs.com/axl234/p/16293687.html