手机版

Nodejs libuv工作原理详解

时间:2021-08-20 来源:互联网 编辑:宝哥软件园 浏览:

前言

这应该是Nodejs的第七个共享操作原理。这次分享后,短时间内不会分享Nodejs的工作原理,而是会停一会儿。PS:不多,但会挖新坑。最近在研究RPG Maker MV、区块链和云计算,可能会更新一些相关文章或者相关教学。

回到主题,异步编程的难点在于请求和响应不是按顺序发生的。以http服务器为例,异步编程赋予了服务器高并发的品质,能够以较小的资源代价不断接受和处理请求。然而,快速处理请求并不意味着快速返回请求。高并发不等于快速反馈。

在Nodejs中,libuv提供了异步编程的可能性。Libuv为内置模块提供API,用于支持请求和数据返回的异步处理。

本文主要从两个角度讨论libuv的工作原理:

libuv的架构

2)案例中,从细节上看,libuv如何对待不同的I/O请求,以不同的方式完成异步请求和数据返回。

利布夫的建筑

从左到右,可以分为两部分,网络输入输出的相关请求,文件输入输出的另一部分,DNS操作和用户代码。

上图展示了libuv细节的流程,图中的代码非常简单,包括两部分:

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

2.fs.open()以异步方式打开文件。

选择两个例子很简单,因为可以看到libuv模式图:libuv对网络I/O和文件I/O使用不同的机制.

上图右半部分主要分为两部分:

1.主线程:当节点启动时,主线程已准备好执行。节点启动时,会完成一系列初始化动作,启动V8引擎,进入下一个循环。

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

网络输入输出

V8引擎执行从server.listen()调用内置模块Tcp_wrap的过程。

在创建TCP链接的过程中,libuv直接参与Tcp_wrap.cc函数中的TCPWrap:listen(),调用uv_listen()完成执行uv_io_start()。这个看似短暂的过程实际上是一个类似于linux内核的中断处理机制。

uv_io_start()加载将句柄插入已处理的水队列。这样做的好处是可以立即处理请求。中断处理机制的下部类似于数据处理操作,交给主线程完成处理。

代码逻辑很简单,检查循环是否包含句柄,如果包含,遍历默认循环。

文件输入输出

这里我们研究文件输入输出

与网络输入/输出一样,我们的应用程序所依赖的文件系统模块由一个内置模块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。嗯,这也是一个包裹。趁你还没疯,看看图6。这里充分展示了FSReqWrap类继承关系。

除了FSReqWrap,还有其他的包装器,比如PipeConnectWrap、TCPConnectWrap等等。每个Wrap都是一个请求类型服务。但是这些包装都是节点本身的行为,和libuv有什么关系呢?上图展示了FSReqWrap的关键数据结构uv _ fs _ s req _ _。

让我们回顾一下uv_fs_open()。调用此函数时,req__作为一个重要参数传递。在uv_fs_open()中,req__被添加到工作队列的末尾。图3。线程池中线程将提取这些请求进行处理。每个请求就像一个粘贴板,它绑定了事件循环、工作队列、每个请求的处理函数(work())和请求结束处理函数(done())。绑定操作在uv _ work _ submit()中完成。例如,对于这里的req__来说,绑定到它的work()是UV _ _ fs _ work(),done()是uv__fs_done()。

这里有一个有趣的问题,值得格外关注。我们的线程池是什么时候建立的?

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

每个线程的入口函数是Threadpool.c中的worker().工作逻辑简单,依次取出工作队列中的请求,执行绑定到请求的work()函数。绑定到前面提到的请求的done()函数在哪里执行?这也是一个有趣的操作。libuv通过uv_async_send()通知事件循环执行相应的回调函数,也就是我们绑定到请求的done()函数。Uv__work_done()用于完成此类操作。

Uv_async_send()通过PIPE与主线程通信。

在本节中,我以一个FSReqWrap和Open()函数为例来描述libuv处理这个文件I/O请求所涉及的各种操作:

设置线程池(仅一次)并将相关的事件循环、工作队列、work()、done () threadworker()绑定到工作队列中的每个请求,并执行work()以通过uv_async_send()通知event loop执行done()

以上是关于相关的知识点。谢谢你的支持。

版权声明:Nodejs libuv工作原理详解是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。