Libuv 架构Libuv 是 NodeJS 的核心组件,是一个跨平台的处理异步 I/O 请求的 C 库,从架构来看,它把各类请求主要分为两大类:网络 I/O 相关请求,以及文件 I/O、DNS Ops 以及 User code 组成的请求 。
对于网络 I/O 相关请求,根据 OS 平台的不同,分别采用了 linux 的 epoll、OSX 和 BSD 类 OS 的 kqueue、SunOS 的 event ports 以及 windows 的 IOCP 等 I/O 读写机制 。
对于 File I/O 为代表的请求,则使用线程池实现异步请求处理,具有更好的跨平台特性 。
事件循环 event loop在 Node 应用启动后,就会进入 Libuv 事件循环中,每一轮循环 Libuv 都会处理维护在各个阶段的任务队列的回调节点,在回调节点中可能会产生新的任务,任务可能在当前循环或是下个循环继续被处理 。
以下是 Libuv 的执行流程图:
下面简述一下各个阶段代表的含义:
- 首先判断当前事件循环是否处于 alive 状态,否则退出整个事件循环 。alive 状态表示是否有 active 状态的 handle 和 request,closing 状态的 handle
- 基于系统时间更新时间戳
- 判断由定时器组成的小顶堆中那个节点超时,超时则执行定时器回调
- 执行 pending 回调任务,一般 I/O 回调添加的错误或写数据成功的任务都会在下一个事件循环的 pending 阶段执行
- 执行 idle 阶段的回调任务
- 执行 prepare 阶段的回调任务
- 调用各平台的 I/O 读写接口,最多等待 timeout 时间(定时器最快过期时间),期间如果有数据返回,则执行 I/O 对应的回调
- 执行 check 阶段的回调任务
- 执行 closing 阶段的回调任务
- 重新回到流程 1
// src/unix/core.cint uv_run(uv_loop_t* loop, uv_run_mode mode) {int timeout;int r;int can_sleep;r = uv__loop_alive(loop);if (!r)uv__update_time(loop);while (r != 0 && loop->stop_flag == 0) {uv__update_time(loop);uv__run_timers(loop);can_sleep =QUEUE_EMPTY(&loop->pending_queue) && QUEUE_EMPTY(&loop->idle_handles);uv__run_pending(loop);uv__run_idle(loop);uv__run_prepare(loop);timeout = 0;if ((mode == UV_RUN_ONCE && can_sleep) || mode == UV_RUN_DEFAULT)timeout = uv__backend_timeout(loop);uv__metrics_inc_loop_count(loop);// 动态设置 epoll_wait 的超时时间uv__io_poll(loop, timeout);for (r = 0; r < 8 && !QUEUE_EMPTY(&loop->pending_queue); r++)uv__run_pending(loop);uv__metrics_update_idle_time(loop);uv__run_check(loop);uv__run_closing_handles(loop);if (mode == UV_RUN_ONCE) {uv__update_time(loop);uv__run_timers(loop);}r = uv__loop_alive(loop);if (mode == UV_RUN_ONCE || mode == UV_RUN_NOWAIT)break;}/* The if statement lets gcc compile it to a conditional store. Avoids* dirtying a cache line.*/if (loop->stop_flag != 0)loop->stop_flag = 0;return r;}任务调度了解了 Libuv 的事件循环流程,接下来结合 JS 代码具体看看 NodeJS 是如何进行任务调度的 。image.png
目前,主要有五种主要类型的队列被 Libuv 的事件循环所处理:
- 过期或是定期的时间队列,由 setTimeout 或 setInterval 函数所添加的任务
- Pending 队列,主要存放读写成功或是错误的回调
- I/O 事件队列,主要存放完成 I/O 事件时的回调
- Immediates 队列,采用 setImmediate 函数添加的任务,对应 libuv 的 check 阶段
- Close 任务队列,主要存放 close 事件回调
- Next Ticks 队列,采用 process.nextTick 添加的任务
- 其他微任务队列,例如 promise callback 等
?示例?
// timer -> pending -> idle -> prepare -> poll io -> check -> close// timer phasesetTimeout(() => {Promise.resolve().then(() => {console.log('promise resolve in timeout');process.nextTick(() => {console.log('tick task in timeout promise');});})process.nextTick(() => {console.log('tick task in timeout');process.nextTick(() => {console.log("tick task in timeout->tick");})});console.log('timer task');}, 0);// check phasesetImmediate(() => {process.nextTick(() => {console.log('imeediate->tick task')});console.log('immediate task');});Promise.resolve().then(() => {console.log('promise resolve');});process.nextTick(() => {console.log("tick task");});console.log('run main thread');
推荐阅读
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 十个优质的基于Node.js的CMS 内容管理平台
- Linux系统下如何设置开机自动运行脚本?
- 如何寻找并删除系统里的重复文件,快速释放磁盘空间?
- 用了三年MySQL,还不知道Server层和引擎层是如何交互的?
- 如何提升HTTPS访问速度?网站HTTPS性能优化技巧
- Ngnix如何配置强制HTTPS访问?
- 机器学习系统架构的十个要素
- 从如何更好的监控Oracle共享池谈起
- 如何以及为什么衡量网络安全
- Java 中为什么要设计 throws 关键词,是故意的还是不小心
