Nodejs libuv运行原理详解


Posted in NodeJs onAugust 21, 2019

前言

这应该是Nodejs的运行原理的第7篇分享,这篇过后,短时间内不会再分享Nodejs的运行原理,会停更一段时间,PS:不是不更,而是会开挖新的坑,最近有在研究RPG Maker MV,区块链,云计算,可能会更新一些相关文章,或者相关教学。

回到正题,异步编程的难点在于请求与响应不是按顺序发生的。以http server 为例,异步编程赋予了server 高并发的品质,而且他可以以很小的资源代价,不断地接受和处理请求。但是快速处理请求不表示快速地返回请求=>高并发不等同于快速反馈。

在Nodejs中,libuv则为异步编程的实现提供了可能。libuv为builtin modules 提供了API,这些API用来支撑请求和数据的返回的异步处理方式。

这一篇分享,我们主要讨论libuv的运行原理,从两个角度出发:

1) libuv的架构

2) 案例,从细节的角度看libuv是如何对待不同I/O请求,按照不同的方式来完成异步请求和数据返回的。

Libuv的架构

Nodejs libuv运行原理详解

从左往右可分为两部分,Network I/O的相关请求,另一部分File I/O,DNS Ops和User Code组成。

Nodejs libuv运行原理详解

上图展示了libuv细节的流程,图中代码很简单,包括2个部分:

1. server.listen()是用来创建TCP server时,通常放在最后一步执行的代码。主要指定服务器工作的端口以及回调函数。

2. fs.open()是用异步的方式打开一个文件。

选择两个示例很简单,因为libuv架构图可视:libuv对 Network I/O和 File I/O采用不同的机制。

上图右半部分,主要分成两个部分:

1. 主线程:主线程也是node启动时执行的现成。node启动时,会完成一系列的初始化动作,启动V8 engine,进入下一个循环。

2. 线程池:线程池的数量可以通过环境变量UV_THREADPOOL_SIZE配置,最大不超过128个,默认为4个。

Network I/O

V8 engine执行从server.listen() 开始,调用builtin module Tcp_wrap 的过程。

在创建TCP链接的过程中,libuv直接参与Tcp_wrap.cc函数中的 TCPWrap::listen() 调用uv_listen()开始到执行uv_io_start()结束。看起来很短暂的过程,其实是类似linux kernel的中断处理机制。

uv_io_start()负载将handle插入到处理的water queue中。这样的好处是请求能够立即得到处理。中断处理机制里面的下半部分与数据处理操作相似,交由主线程去完成处理。

Nodejs libuv运行原理详解

代码逻辑很简单,查看loop中是否包含handle,如果有遍历default loop。

Nodejs libuv运行原理详解

File I/O

这里我们研究一下 File I/O。

同Network I/O一样,我们的应用所依赖的fs模块,后面有一个builtin module Node_file.cc作为支撑。 Node_file.cc包含了各种我们常用的文件操作的接口,例如open, read, write, chmod,chown等。但同时,它们都支持异步模式。 我们通过Node_file.cc中的Open()函数来研究一下具体的实现细节。

如果你用类似source insight之类的代码阅读工具跟踪一下代码调用顺序,会很容易发现对于异步模式,Open()函数会在一系列辅助操作之后,进入函数uv_fs_open(),并且传入了一个FSReqWrap的对象。

FSReqWrap(),从名字可以看得出来,这是一个wrap,且是与FS相关的请求。也就是说,它基于某一个现成的机制来实现与FS相关的请求操作。这个现成的机制就是ReqWrap。好吧,它也是个wrap。乘你还没疯的时候,看一下图6吧。这里完整展示了FSReqWrap类继承关系。

Nodejs libuv运行原理详解

除了FSReqWrap,还有其它Wrap,例如PipeConnectWrap,TCPConnectWrap等等。每个Wrap均为一种请求类型服务。 但是这些wrap,都是node自身的行为,而与libuv相关的是什么呢?上图中表示出了FSReqWrap关键的数据结构 uv_fs_s req__。

让我们把目光回到uv_fs_open()。在调用这个函数时, req__作为其一个重要的参数被传递进去。而在uv_fs_open()内部,req__则被添加到work queue的末尾中去。图3 thread pool中的thread会去领取这些request进行处理。 每个request很像一个粘贴板,它将event loop, work queue,每个请求的处理函数(work()),以及请求结束处理函数(done())绑定在一起。绑定的操作在uv__work_submit()中完成。 例如对于这里的req__,绑定在它身上的work()为uv__fs_work(), done()为uv__fs_done()。

这里有一个比较有意思的问题值得额外看一下。我们的thread pool是在什么时候建立的呢?

答案是:在第一次异步调用uv__work_submit()时。

每个thead的入口函数是 Threadpool.c中的worker()。工作逻辑比较简单,依次取出work queue中的请求,执行绑定在该请求上的work()函数。 前面我们提到的绑定在请求上的done()函数在哪里执行呢?这也是一个比较有意思的操作。libuv通过uv_async_send()通知event loop去执行相应的callback函数,也即我们绑定在request上的done()函数。uv__work_done()用于完成这样的操作。

uv_async_send()与主线程之间通过PIPE通信。

我在这一小节以一个FSReqWrap以及Open()函数为例,描述了libuv处理这种File I/O请求时所涉及的各种操作:

建立thread pool(只建立一次) 在每个请求req__上绑定与其相关的event loop, work queue, work(), done() thread worker()用来处理work queue里面的每个请求,并执行work() 通过uv_async_send()通知event loop执行done()

以上就是关于本次相关的知识点内容,感谢大家对三水点靠木的支持。

NodeJs 相关文章推荐
nodejs之请求路由概述
Jul 05 NodeJs
Nodejs中读取中文文件编码问题、发送邮件和定时任务实例
Jan 01 NodeJs
NodeJS学习笔记之Connect中间件模块(一)
Jan 27 NodeJs
NodeJs——入门必看攻略
Jun 27 NodeJs
NodeJS测试框架mocha入门教程
Mar 28 NodeJs
nodejs个人博客开发第六步 数据分页
Apr 12 NodeJs
nodejs 搭建简易服务器的图文教程(推荐)
Jul 18 NodeJs
nodejs 十六进制字符串型数据与btye型数据相互转换
Jul 30 NodeJs
nodejs语言实现验证码生成功能的示例代码
Oct 13 NodeJs
nodejs实现的http、https 请求封装操作示例
Feb 06 NodeJs
如何利用nodejs实现命令行游戏
Nov 24 NodeJs
在nodejs中创建child process的方法
Jan 26 NodeJs
nodejs和react实现即时通讯简易聊天室功能
Aug 21 #NodeJs
Nodejs 识别图片类型的方法
Aug 15 #NodeJs
NodeJs实现简易WEB上传下载服务器
Aug 10 #NodeJs
NodeJs 实现简单WebSocket即时通讯的示例代码
Aug 05 #NodeJs
Nodejs监听日志文件的变化的过程解析
Aug 04 #NodeJs
nodejs对项目下所有空文件夹创建gitkeep的方法
Aug 02 #NodeJs
nodejs读取图片返回给浏览器显示
Jul 25 #NodeJs
You might like
DOTA2 玩家自创拉野攻略 特色英雄快速成长篇
2020/04/20 DOTA
PHP代码网站如何防范SQL注入漏洞攻击建议分享
2012/03/01 PHP
基于PHP选项与信息函数的使用详解
2013/05/10 PHP
php中opendir函数用法实例
2014/11/15 PHP
PHP结合Ffmpeg快速搭建流媒体服务的实践记录
2018/10/31 PHP
php自定义排序uasort函数示例【二维数组按指定键值排序】
2019/06/19 PHP
jQuery 常见操作实现方式和常用函数方法总结
2011/05/06 Javascript
js 操作select与option(示例讲解)
2013/12/20 Javascript
js点击button按钮跳转到另一个新页面
2014/10/10 Javascript
浅析AMD CMD CommonJS规范--javascript模块化加载学习心得总结
2016/03/16 Javascript
教大家轻松制作Bootstrap漂亮表格(table)
2016/12/13 Javascript
BootStrap中的Fontawesome 图标
2017/05/25 Javascript
vue-cli配置环境变量的方法
2018/07/09 Javascript
laydate时间日历插件使用方法详解
2018/11/14 Javascript
vue动态添加路由addRoutes之不能将动态路由存入缓存的解决
2019/02/19 Javascript
vue项目中引入vue-datepicker插件的详解
2019/05/14 Javascript
JS拖动选择table里的单元格完整实例【基于jQuery】
2019/05/28 jQuery
浅谈一个webpack构建速度优化误区
2019/06/24 Javascript
vue-cli2与vue-cli3在一台电脑共存的实现方法
2019/09/25 Javascript
Element Tooltip 文字提示的使用示例
2020/07/26 Javascript
Python类的继承、多态及获取对象信息操作详解
2019/02/28 Python
Pytorch 实现计算分类器准确率(总分类及子分类)
2020/01/18 Python
Python过滤序列元素的方法
2020/07/31 Python
html5的localstorage详解
2017/05/09 HTML / CSS
美国最大的珠宝商之一:Littman Jewelers
2016/11/13 全球购物
施华洛世奇意大利官网:SWAROVSKI意大利
2018/07/23 全球购物
手工制作的意大利太阳镜和光学元件:Illesteva
2019/01/19 全球购物
阿联酋最好的手机、电子产品和家用电器网上商店:Eros Digital Home
2020/08/09 全球购物
仓管员岗位职责范文
2013/11/08 职场文书
公休请假条
2014/04/11 职场文书
中国梦演讲稿教师篇
2014/04/23 职场文书
依法行政工作汇报材料
2014/10/28 职场文书
党的群众路线教育实践活动个人对照检查材料(教师)
2014/11/04 职场文书
2015年化验室工作总结
2015/04/23 职场文书
Python实战之大鱼吃小鱼游戏的实现
2022/04/01 Python
画错魏国疆域啦!《派对咖孔明》动画因作画失误于官网致歉
2022/04/07 日漫