之前断断续续开发过一些 Node.js 的项目,但只仅限于使用它实现一些功能,没有过多对底层深入的研究。现在因为公司大前端组内的服务端渲染直出、BFF(Backend For Frontend) 等需求会越来越多,组内需要对服务端技术有更深刻的理解,如果对 Node.js 仅仅停留在如何写业务代码的层面,那恐怕是没有底气保证以后服务的稳定性。

  本文会基于 node-v12.13.0 版本的源码,对核心模块代码做一些阅读和理解,以窥探 Node.js 服务高效的秘诀。在研究源码之前,首先带着几个疑问,看接下来是否能一一解开:

  1. EventLoop 的任务调度方式是什么样的?一次 Loop 取一个任务还是多个任务?
  2. 主循环在没有任务处理的空闲时,如何休眠的?是像 iOS 的 runloop 调用系统进程挂起吗?

  事件循环的实现是属于 libuv 核心库的一部分,而在 node 项目中,他的位置是在 deps/uv/src/ 目录下,打开 core.c 文件,其中的 uv_run() 就是主循环的入口函数,整个函数的实现也基本和官方文档给出的事件循环阶段一致。

源码

 当要写这篇博客的时候,实际上已经有很多介绍这些内容的文章了,所以本篇就直接略过一些 loop 阶段,直接讲最重要的:

1.计算超时时间

超时时间直接决定了 poll 阶段是阻塞还是非阻塞的直接执行,所以他决定着任务调度的实际执行时机和执行方式,主要实现在 uv_backend_timeout() 函数里

展开函数的实现:

int uv_backend_timeout(const uv_loop_t* loop) {
  // loop 将要停止时,返回0
if (loop->stop_flag != 0)
return 0;
 // 没有活跃的 handles 时,返回0
if (!uv__has_active_handles(loop) && !uv__has_active_reqs(loop))
return 0;
 // idle不为空时,返回0
if (!QUEUE_EMPTY(&loop->idle_handles))
return 0;
 // pending_queue(执行任务已完成待回调队列)不为空时,返回0
if (!QUEUE_EMPTY(&loop->pending_queue))
return 0;
 // closeing_handles 不为空时,返回0
if (loop->closing_handles)
return 0; return uv__next_timeout(loop);
}

接下来是 uv_next_timeout() 这个函数,它是实现在 deps/uv/src/time.c 文件里:

int uv__next_timeout(const uv_loop_t* loop) {
const struct heap_node* heap_node;
const uv_timer_t* handle;
uint64_t diff; heap_node = heap_min(timer_heap(loop));
 // timer 队列为空时,返回-1(-1的含义在下面介绍)
if (heap_node == NULL)
return -1; /* block indefinitely */ handle = container_of(heap_node, uv_timer_t, heap_node);
 // 如果 timer 超时了,返回0
if (handle->timeout <= loop->time)
return 0;

 // 将超时时间设为最早要超时的 timer 的所剩余时间
diff = handle->timeout - loop->time;
if (diff > INT_MAX)
diff = INT_MAX; return (int) diff;
}

从上面的函数计算好的 timeout 将以参数的形式传入 uv__io_poll() 这个函数,这个函数内就是 poll 阶段的实现了,我已经迫不及待的要一探究竟了。uv__io_poll() 依赖的操作系统提供的功能,具有平台相关性,所以不同的平台会有不同的实现,本文主要讨论linux-core.h这个文件,即linux平台的实现。

  代码比较长我就不贴了,总之看完并理解下来,uv_io_poll 就是对 epoll 的一个封装,但uv给我们提供了一个很值得思考和借鉴的方法,那就是 timeout 的使用,根据这个 timeout 来动态决定一次 loop 将要处理的任务量。

首选来解析一下 epoll_pwait 这个函数的作用

.....
......
nfds = epoll_pwait(loop->backend_fd,
events,
ARRAY_SIZE(events),
timeout,
psigset);
......
......

所以从任务队列取并不是简单的只取一条,在这个超时时间内,可能会在一次 loop 的 poll 阶段完成多个任务,完成后会立即回调【阻塞式的完成】。

(未完待续)

p.p1 { margin: 0; font: 12px "Helvetica Neue" }
li.li1 { margin: 0; font: 12px ".PingFang SC" }
span.s1 { font: 12px "Helvetica Neue" }
ol.ol1 { list-style-type: decimal }

Node.js 源码解读 EventLoop的更多相关文章

  1. js便签笔记(10) - 分享:json2.js源码解读笔记

    1. 如何理解“json” 首先应该意识到,json是一种数据转换格式,既然是个“格式”,就是个抽象的东西.它不是js对象,也不是字符串,它只是一种格式,一种规定而已. 这个格式规定了如何将js对象转 ...

  2. js便签笔记(10) - 分享:json.js源码解读笔记

    1. 如何理解“json” 首先应该意识到,json是一种数据转换格式,既然是个“格式”,就是个抽象的东西.它不是js对象,也不是字符串,它只是一种格式,一种规定而已. 这个格式规定了如何将js对象转 ...

  3. fastclick.js源码解读分析

    阅读优秀的js插件和库源码,可以加深我们对web开发的理解和提高js能力,本人能力有限,只能粗略读懂一些小型插件,这里带来对fastclick源码的解读,望各位大神不吝指教~! fastclick诞生 ...

  4. Linux 安装node.js ---- 源码编译的方式

    一 : 普通用户: 安装前准备环境: 1.检查Linux 版本 命令: cat /etc/redhat-release 2.检查 gcc.gcc-c++ 是否安装过 命令: rpm -q gcc rp ...

  5. prototype.js 源码解读(02)

    如果你想研究一些比较大型的js框架的源码的话,本人建议你从其最初的版本开始研读,因为最初的版本东西少,易于研究,而后的版本基本都是在其基础上不断扩充罢了,所以,接下来我不准备完全解读prototype ...

  6. prototype.js 源码解读(01)

    prototype.js是一个设计的非常优雅且很有实用价值的js基础类库,其源码非常值得研究.研究它的源码不仅能提升个人水平,而且对你打下坚实的js基础也很有帮助.因本人技术水平有限,该解读仅供参考. ...

  7. json2.js源码解读记录

    相关内容:json详细用法.js语法.unicode.正则   json特点--最简单.最小巧的经典js库.   json作者:道克拉斯.克劳福德(Douglas Crockford)--js大牛 出 ...

  8. require.js 源码解读——配置默认上下文

    首先,我们先来简单说一下,require.js的原理: 1.载入模块
 2.通过模块名解析出模块信息,以及计算出URL
 3.通过创建SCRIPT的形式把模块加载到页面中.
 4.判断被加载的脚本,如 ...

  9. Red hat Linux 安装Node.js 源码安装

    1. 下载源码包 http://nodejs.org/dist/v0.10.29/node-v0.10.29.tar.gz 2.准备安装环境,>python2.6, gcc, g++ pytho ...

  10. 亚马逊左侧菜单延迟z三角 jquery插件jquery.menu-aim.js源码解读

    关于亚马逊的左侧菜单延迟,之前一直不知道它的实现原理.梦神提到了z三角,我也不知道这是什么东西.13号那天很有空,等领导们签字完我就可以走了.下午的时候,找到了一篇博客:http://jayuh.co ...

随机推荐

  1. 关于Powerlink和EtherCAT的对比

    https://www.amobbs.com/thread-5679636-1-2.html 转发自:http://book.2cto.com/201508/55093.html 这个问题经常会被人问 ...

  2. 阅读openfoam框图

    看完of的帮助文档,会非常怀念fluent的帮助文档或是matlab的帮助文档 比如我要解决一个matlab问题,基本上看帮助文档一分钟就知道我要如何取用我想要的东西,of帮助文档不光做不到,还给你炫 ...

  3. CTF之WEB学习路线规划

    前言 受大佬Lzer0Kx(公众号:LK安全)启发,打算整一篇关于CTF-web的学习路线的分享.当然也算是给一些刚入门的小白的一些学习方向,不至于不知道学啥.废话少说,开整! 以下的所有内容属于我个 ...

  4. 【KAWAKO】deepface-在基于m1芯片的macbook上进行安装

    ​目录 前言 本机配置 安装miniforge tips 安装tensorflow 安装deepface及相关库 Reference 前言 deepface是一款"即插即用"的人脸 ...

  5. 【模板】AC自动机(二次加强版)

    模板 \(Problem:\) 求 \(n\) 个模式串在文本串中出现的次数 \(templete:\) \(Luogu5357\) \(Code\) #include<cstdio> # ...

  6. 如何在VC++ 6.0中实现拖动指令改变执行路径?

    前文提要: 在VC6.0之后出现的VS系列开发工具都具有的调试功能:移动指针更改执行流,VC6不支持这个UI操作. 调试程序暂停时,源代码或"反汇编"窗口边距处的黄色箭头标记要运行 ...

  7. LeetCode-2038 如果相邻两个颜色均相同则删除当前颜色

    来源:力扣(LeetCode)链接:https://leetcode-cn.com/problems/remove-colored-pieces-if-both-neighbors-are-the-s ...

  8. Vscode报错: error:0308010C:digital envelope routines::unsupported错误记录解决

    Vscode报错: error:0308010C:digital envelope routines::unsupported错误记录解决 因为安装了新版本的node才报的错误:node版本: v18 ...

  9. 框架和Nginx

    分布式:micorservice.framework. spring security Nginx 方向代理:正向代理就是客户端通过代理访问不同的服务器例如访问外网,反向代理就是客户端直接访问反向代理 ...

  10. leetcode 30. 串联所有单词的子串 【时间击败 90.28%】 【内存击败 97.44%】

    这道题让我从早做到晚-3--- 设len=words[0].length(). 一开始我按照words的顺序扩大区间,发现这样就依赖words的顺序.之后改成遍历s的所有长度为len*words.le ...