Node.js源码解读-EventLoop

 之前有一些机会断断续续接触过一些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阶段完成多个任务,完成后会立即回调【阻塞式的完成】。

原文地址:https://www.cnblogs.com/liujixin/p/11966095.html

时间: 2024-07-29 02:25:54

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

分享:json2.js源码解读笔记

1. 如何理解"json" 首先应该意识到,json是一种数据转换格式,既然是个"格式",就是个抽象的东西.它不是js对象,也不是字符串,它只是一种格式,一种规定而已. 这个格式规定了如何将js对象转换成字符串.以及转换成怎样的字符串--序列化 -- JSON.stringify 接口: 以及如何将一个有效字符串转换成js对象--反序列化-- JSON.parse 接口: 2. 关于作者 json作者是 道格拉斯.克劳福德 ,是一位js大牛,写过一本<java

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

1. 如何理解“json” 首先应该意识到,json是一种数据转换格式,既然是个“格式”,就是个抽象的东西.它不是js对象,也不是字符串,它只是一种格式,一种规定而已. 这个格式规定了如何将js对象转换成字符串.以及转换成怎样的字符串——序列化 —— JSON.stringify 接口: 以及如何将一个有效字符串转换成js对象——反序列化—— JSON.parse 接口: 2. 关于作者 json作者是 道格拉斯.克劳福德 ,是一位js大牛,写过一本<javascript语言精粹>,相信不少朋

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

1. 下载源码包 http://nodejs.org/dist/v0.10.29/node-v0.10.29.tar.gz 2.准备安装环境,>python2.6, gcc, g++ python redhat自带了不用装, gcc:  yum install -y gcc gcc-c++ 3. tar -zxf node-v0.10.29.tar.gz cd node-v0.10.29 ./configure && make && sudo make install

fastclick.js源码解读分析

阅读优秀的js插件和库源码,可以加深我们对web开发的理解和提高js能力,本人能力有限,只能粗略读懂一些小型插件,这里带来对fastclick源码的解读,望各位大神不吝指教~! fastclick诞生背景与使用 在解读源码前,还是简单介绍下fastclick: 诞生背景 我们都知道,在移动端页面开发上,会出现一个问题,click事件会有300ms的延迟,这让用户感觉很不爽,感觉像是网页卡顿了一样,实际上,这是浏览器为了更好的判断用户的双击行为,移动浏览器都支持双击缩放或双击滚动的操作,比如一个链

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

一 : 普通用户: 安装前准备环境: 1.检查Linux 版本 命令: cat /etc/redhat-release 2.检查 gcc.gcc-c++ 是否安装过 命令: rpm -q gcc rpm -q gcc-c++ 3.安装 gcc.gcc-c++ 命令: yum -y install gcc gcc-c++ kernel-devel 注意 :  如果是第一个安装,有可能 会报 错误:xxx is not in the sudoers file. This incident will

prototype.js 源码解读(02)

如果你想研究一些比较大型的js框架的源码的话,本人建议你从其最初的版本开始研读,因为最初的版本东西少,易于研究,而后的版本基本都是在其基础上不断扩充罢了,所以,接下来我不准备完全解读prototype.js的源码了,而是拿它一些常见的API来解读. //定时器类,比起window.setInterval函数,该类能够使得回调函数不会被并发调用 var PeriodicalExecuter = Class.create();//Class类创建的定时器类 PeriodicalExecuter.pr

prototype.js 源码解读(01)

prototype.js是一个设计的非常优雅且很有实用价值的js基础类库,其源码非常值得研究.研究它的源码不仅能提升个人水平,而且对你打下坚实的js基础也很有帮助.因本人技术水平有限,该解读仅供参考. 定义全局对象Prototype,包括属性版本号,属性版本号的定义有利于版本号的检测 9 var Prototype = { 10 Version: '1.5.0', 11 BrowserFeatures: { 12 XPath: !!document.evaluate 13 }, 14 15 Sc

Node.js源码解析----自己实现一个Node.js的难点与思路

前言: 最近在看Node.js,看了一段时间后便想着自己实现一个Node.js现在已经实现了个大概(绝大部分是模仿人家,不过自己实现一遍基本上就理解Node.js的原理了)下面便说说这个过程中的坑,以及一些需要注意的地方: Node.js需要一定C++基础,建议看完C++Primer再看,否则V8的好多表达方式,指针,引用,模板之类的会看不懂: 代码已上传GitHub地址:   https://github.com/sven36/cNode 编译:我用的win10的环境,具体编译请参考Node.

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

首先,我们先来简单说一下,require.js的原理: 1.载入模块? 2.通过模块名解析出模块信息,以及计算出URL? 3.通过创建SCRIPT的形式把模块加载到页面中.? 4.判断被加载的脚本,如果发现它有依赖就去加载依赖模块.如果不依赖其它模块,就直接执行factory方法 ?5.等所有脚本都被加载完毕就执行加载完成之后的回调函数. 从今天起,我们跟着我们简单的例子,通过跟踪代码,来了解require.js的源码. 1 <!DOCTYPE html> 2 <html lang=&q