当前位置: 首页 > 后端技术 > Node.js

Node.js中Event_pool的使用场景

时间:2023-04-03 16:04:36 Node.js

作者:bugall微信:bugallFEmail:769088641@qq.comGithub:https://github.com/bugall1.单线程异步呢?我们都知道Node是单线程和异步的。其实这相对于Node层来说是没有问题的。但是整体来看的话,其实是有一个thread_pool的概念的。我更愿意将Node视为将libuv和v8粘合在一起的粘合层。作为js执行的引擎,v8封装了一些c++代码来实现一些内核调用,同时抽象出类似的功能接口来满足跨平台的需求。我觉得写Node有两条主线,一条是js语法的使用和理解,比如[1]==[1]//false,另一条主线是libuv的理解,因为libuv中的event_loop实现也是Node在某些场景下表现出色的原因。2.什么是IO多路复用(max-Multiplexing)通常如果我们创建一个socket请求,内核会为这个socket创建一个标记,我们通常称之为文件描述符(filedescriptor),而文件描述符实际上是一个指标值。索引值对应的实际存储的是文件的一些元数据的数据结构,比如文件的操作权限,创建时间,文件是否可读可写等。总之,这个结构存储了一个文件.所有源信息(元数据)。如果我们现在有10个socket请求进来,首先我们的内核需要分配10个文件描述符,那么这些创建的文件描述符必须有一个地方来存放它们。在Linux中,有一个文件描述符表来管理这些文件描述符信息,为了方便理解,我们把这个文件描述符表想象成一个数组(这里需要补充一下,通常硬件相对于内核是异步的),此时我们想知道哪些套接字有数据到达?笨办法是枚举每个文件描述符对应的源数据,然后查看它们对应的状态。如果是可读的,我们就可以从对应的buffer中读取数据,但是有一个问题。如果我们的socket请求是线性增加的,那么遍历一次数组的时间也会线性增加。这就是poll和select的实现。后来为了解决这个问题,出现了epoll3.为什么epoll效率更高?这里补充一点。epoll的IO多路复用和iocp的IO多路复用虽然功能看起来是一样的,但是底层的实现原理是完全不同的。epoll之所以效率高,是得益于linux内核基于事件的实现机制。比如网卡加载数据时,不会占用CPU,因为硬件与内核实现是异步的。当网卡加载完数据后,会发送一个中断给内核。如果你代表一个线程,当你在看电视时,你的妈妈正在厨房做饭。饭做好了,妈妈会说:饭好了可以吃了。现在你可以选择是过去吃饭还是继续看电视。至少你知道饭已经准备好了。当你看电视时,你会认为这顿饭还没吃完。对于我们的应用来说,未来某个时间可能会发生很多事件。我们需要将这些事件存储起来,并一一处理。Windows内核实现不是基于事件的,所以iocp使用的是多线程round-robin训练实现的IO多路复用,所以这也是不推荐使用Windows运行Node服务的原因。epoll中主要有3个方法,epoll_create、epoll_ctl、epoll_wait。create是创建一个epoll实例,ctl是准备epoll在哪个文件描述符上监听哪些事件,等待获取激活的事件。根据上面的例子来理解:create相当于我现在要看电视,准备一个笔记本记录以后要发生的事情和未处理的事情,ctl就是在笔记本上记录:whenmotheriscooking,Please告诉我这件事是否在妈妈准备好时发生。等待就是看哪些事件被激活,看看妈妈的饭菜做好了没有。epoll_wait中存储的激活事件列表就是我们常说的event_pool4.既然有了IO多路复用,为什么还需要thread_pool?上面说了libuv中的文件操作和DNS解析都是用线程池实现的。接下来我们看一下原因1、为什么需要使用thread_pool进行文件操作?这个问题一开始我也很郁闷,也在stackoverflow上发了个帖子。文档。我自己的想法是:其实大部分的磁盘IO时间都花在了磁盘寻道上。数据一旦被读取,读写量就会非常大。不同于socket网络的高延迟,每次接收的都是经过IP分段后的数据包。小,当缓冲区满时,会通知内核进行处理,所以socket可以用epoll来实现。但是一旦开始读取文件,缓冲区就会瞬间被填满,epoll中的激活事件列表中总会有这个文件可读事件。如果是非常大的文件,会导致epoll的event_pool阻塞。2、为什么要用thread_pool做DNS解析?通常我们在发送http请求的时候,首先需要对host进行解析,获取域名对应的IP地址,这样才能真正发送请求。所以这个过程一定是一个同步过程,因为在DNS解析不成功的情况下发送http请求是没有意义的。http中默认会调用dns.lookup方法。此方法将读取/etc/hosts文件。我想检查本地DNS缓存。如果没有,则向DNS解析服务器发送请求。这个查询有超时时间,如果dns解析失败那么请求也会失败。因为我负责的项目每天外网http请求3000万,文件读写3亿(我用Node写的时候也是醉了)。每天早上docker重启容器时,本地缓存的DNS会失效,然后大量的外网http请求需要通过DNS解析。Node默认设置libuv的thread_pool为3。导致重启完成后几分钟内会报很多错误。后来把线程调到128,明显好很多。5、对于Node使用多进程是否有意义的问题,我并没有明确的认知和测试结果。稍后我会尽快完成它。可以肯定的是,多进程对epoll没有影响。对thread_poll影响不大,因为thread_poll使用的场景是文件操作和DNS解析。因为文件操作的性能取决于磁盘性能,即使你有16核16进程,当磁盘达到性能阈值时,那么多核也没有意义。唯一提高性能的地方是主线程。也就是v8执行到这里,因为我们从event_loop中获取到的激活事件,就是要做一些事件对应的逻辑,也就是我们所说的回调。我们会将这些被激活的事件对应的回调放到一个任务队列中。在主线程处理任务队列的地方,如果我们把一个任务队列中需要处理的代码分布到4个进程4个任务队列中,性能会有所提升。