开场白
最近在学习 libuv,也了解了一些 Node.js 中使用 libuv 的例子。当然,这篇文章不会去介绍 event loop,毕竟这些东西在各个论坛、技术圈里都被介绍烂了。本文介绍如何正确使用 Event loop,以及即使发现程序是否异常 block。
基础
event loop 的基础想必各位读者都比较熟悉了。这里我引用官方的图,简单介绍两句,作为前置准备:
event loop是作为单线程实现异步的方式之一。简而言之,就是在一个大的 while 循环中不断遍历这些 phase,执行对应的 callbacks。这样才实现了真正的异步调用:调用时不必等着响应,等调用的资源准备好了,回调我。
以上就是基础,接下来进入正题:
问题提出
开门见山,我们提出以下问题:
对于问题1,答案是肯定的。任何 io 密集计算都会 block 主进程,调用任何耗时的同步系统 api(比如同步读取大文件等),也会 block。
对于第2个问题,就需要对 libuv 有个基本认识了(想想我前面说的一个大 while)。event loop 既然是 loop,那么总有循环的概念吧?想到循环,能联想到循环次数吧?对~解决方案就是使用循环次数。
方案
这里我提一个思路(并不是说不写代码😄):如果我们正常逻辑下,一秒钟能进行100W 次事件循环(数据基于我本机),那么如果有一段时间,我得到的1秒钟时间循环次数只有50W,那么是不是说明程序中有哪些地方稍微 block 住了?或者夸张地说,由正常的100W 次变为了个数次。这就很严重了。因此及时监控event loop 非常重要。
第一版代码
// 环境准备 const http = require('http'); const path = require('path'); const {execFile, execFileSync} = require('child_process'); const max = 9999; const 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(); }).listen(8888); // 第一版实现 const MS_MULTI = 1000 * 1000; const blockDelta = 10 * MS_MULTI; let start; function meature() { start = process.hrtime(); setImmediate(function() { let seconds; [seconds, start] = process.hrtime(start); if (seconds * 1000 * MS_MULTI + start > blockDelta) { console.log(`node.eventloop_blocked for ${seconds}secs and ${(start / MS_MULTI).toFixed(2)}ms.`); } meature(); }); } meature(); // childprocess.js 文件 #!/use/env node const args = Number(process.argv[2]); function computeIo(args) { let k; for (let i = 0; i < args; ++i) { for (let j = 0; j < args; ++j) { k = i + j; } } return k; } console.log(computeIo(args));
另外有需要云服务器可以了解下创新互联scvps.cn,海内外云服务器15元起步,三天无理由+7*72小时售后在线,公司持有idc许可证,提供“云服务器、裸金属服务器、高防服务器、香港服务器、美国服务器、虚拟主机、免备案服务器”等云主机租用服务以及企业上云的综合解决方案,具有“安全稳定、简单易用、服务可用性高、性价比高”等特点与优势,专为企业上云打造定制,能够满足用户丰富、多元化的应用场景需求。