宝哥软件园

Nodejs监控事件周期异常示例详解

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

序言

最近学习了一些在Node.js中使用libuv的例子,当然本文不会介绍事件循环。毕竟这些东西在各种论坛和技术圈都介绍得不好。介绍了如何正确使用Event循环,即使发现程序异常阻塞。

基础

事件循环的基础必须是所有读者都熟悉的。这里我引用官方数字,简单介绍两句作为前期准备:

事件循环是实现单线程异步的方法之一。简而言之,就是在一个大的while循环中不断地遍历这些阶段,并执行相应的回调。只有这样才能实现真正的异步调用:调用时不用等待响应,调用资源准备好了再给我回电话。以上是基础,然后进入正题:

提出的问题

为了开门见山,我们提出以下问题:

因为js是单线程的,所以总有办法阻塞整个程序。虽然使用了libuv,但它可能会阻塞主程序。正确你怎么知道我们的程序被屏蔽了?问题1的答案是肯定的。任何io密集型计算都会阻塞主进程,调用任何耗时的同步系统api(如同步读取大文件等。),并且还格挡。

对于第二个问题,你需要对libuv有一个基本的了解(想想我前面提到的大while)。事件循环是循环,所以总有循环的概念?想到周期,你能想到周期的数量吗?是的~解决方法是使用循环数。

程序

在这里,我想提出一个想法(不是说我们不写代码):如果在正常逻辑下,我们每秒可以循环事件100W次(数据是基于我自己的机器),那么如果在一段时间内,我每秒钟得到的循环次数只有50W,是不是说明程序中的某个东西稍微被阻塞了?或者夸张一点,从正常的100W次到几次。这是非常严重的。因此,及时监控事件循环非常重要。

代码的第一个版本

//环境准备常量http=require(' http ');const path=require(' path ');const {execFile,exec file sync }=require(' child _ process ');const max=9999const GetComputedValueFromChildProcess=(max)=ExecfileSync(' node ',[path.join(__dirname,')。/childprocess.js ')、max]);http.createServer((req,RES)={ const k=GetComputedValueFromChildProcess(max);RES . write(' origin-text : ' k);RES . end();}).听(8888);//第一个版本实现const MS _ MULTI=1000 * 1000const blockDelta=10 * MS _ MULTI让我们开始;function measure(){ start=process . HR time();setImmediate(function(){ let seconds;[秒,开始]=process.hrtime(开始);if(秒* 1000 * MS _ MULTI start block delta){ console . log(` node . event loop _ blocked $ { secs }秒和${(start/MS_MULTI)。toFixed(2)} ms `);} measure();});} measure();//childprocess.js文件#!/use/env nodecost args=Number(process . argv[2]);函数ComputeIO(args){ let k;for(设I=0;一、参数;i) { for(让j=0;j参数;j){ k=I j;} }返回k;} console . log(ComputeIO(args));环境是一个网络服务器。我们选择了check的阶段作为起点(这里不使用timer阶段的原因是setTimeout的超时至少为1ms,事件循环空闲时可以在1ms内运行多次,本地数据约为100K次/ms)。应用程序在开始时调用meature方法来启动强力测试。要测试从本次检查到下一次检查的时间是否大于10ms:

#有一个15毫秒的测试节点被阻塞. jsnode.eventloop _ blocked持续0秒和15.71毫秒#当我多次执行curl http://localhost:8888#时#有:node。eventloop _ blocked秒和175.60毫秒节点。eventloop _ blocked用于0秒和149.92毫秒的节点。eventloop _ blocked为0秒和147.25毫秒,是的,基本原型已经出来了。根据这些值,您可以报告数据并排除问题。但是!

如果读者尝试过上面的例子,就会发现一个问题:电脑发热,风扇不停转!

我看了一下任务管理器,发现Node进程的cpu利用率大概是100%!当我注释掉meature逻辑后,cpu利用率恢复到了0%左右。看来这个版本不行。我们来修改一下吧~具体原因是因为setImmediate代码的不断执行,回调的不断加入,导致cpu一直在运行!

第二版代码

我们增加一个采样的概念:每10秒,采样一个周期数至少2秒(为什么至少2秒?因为setTimeout的超时定义至少是duck,哈哈哈哈)

const EVERY _ SEC _ MIN _ LOOPS=1000000;//定义最小每秒周期数let times=0;//一次采样中的周期数让nowShowIncreaseTimes=false//目前是否应该增加time let start=date . now();const CD=10 * 1000//interval function me status(回调=()={ }){ settimeout(function(){ start=date。now();nowShowIncreaseTimes=true_ inter();setTimeout(()={ end measure();measure();//开始预约下一次采样},2000);},光盘);} function _ inter(){ setImmediate(()={ if(nowshowinchetimes){ times;return _ inter();} });}函数end measure(){ const now=date . now();nowShowIncreaseTimes=falseconst TotalMSSPan=now-start;const everySecLoops=(times/(totalMsSpan/1000))。toFixed(0);if(every seclops every _ sec _ min _ loops){ console . log(`当前每秒循环数$ { every seclops } `);}次=0;返回everySecLoops } measure();测试结果:

#当我保持:curl http://localhost:8888#测试节点blocked.js出现,当前每秒循环数为777574,当前每秒循环数为890565#当我们做事:a B- C 10-N 200 http://localhost 3360888/

#结果如下:测试节点被阻塞. js电流周期每秒843594电流周期每秒913329电流周期每秒2电流周期每秒2

修订到第二版后,电脑不再发热,风扇也不再运转。只有采样的时候,cpu才会上升到30或者40,这就不错了。

但与此同时,它也发现了一个问题:每秒只有两个周期!这个时候,基本上就是一个开关。为什么呢?

因为我们的请求处理是同步的!同步生成一个子流程,等待子流程结束后返回结果。可见在服务器项目中启用耗时的同步操作是多么的冒险!

让我们尝试将同步变成异步:

//non-blocked . jsconst max=9999;const getcomputedvaluefromcchildprocess=(max)=new Promise((RES,rej)={ execFile('node ',[path . join(_ dirname,')。/childprocess.js '),max],(err,stdout)={ const value from child process=Number(stdout);RES(ValueFromChildProcess);});});http.createServer(async (req,RES)={ const k=wait GetComputedValueFromChildProcess(max);RES . write(' origin-text : ' k);RES . end();}).听(8888);为了演示同步和异步的区别,本文使用了子过程。事实上,更好的方法是使用worker_thread,或者切片计算。让我们用相同的ab进行测试,并得出结果:

测试节点未阻塞。js电流周期/秒239920电流周期/秒242286

可以看出,虽然空闲时也低于100瓦,但也不是一点点。但是和同步模式相比,这个数量级几乎无法相比!

摘要

到现在,大家应该对监控事件循环有了基本的了解。本来想弄个npm包,但是最近比较忙,只好先扔砖头,大家都使劲扔。

好了,这就是本文的全部内容。希望本文的内容对大家的学习或工作有一定的参考价值。谢谢你的支持。

更多资讯
游戏推荐
更多+