微信号:infoqchina

介绍:有内容的技术社区媒体

【深度】Node.js异步处理CPU密集型任务的新思路

2014-06-18 16:30 InfoQ

Node.js擅长数据密集型实时(data-intensivereal-time)交互的应用场景。然而数据密集型实时应用程序并不是只有I/O密集型任务,当碰到CPU密集型任务时,比如要对数据加解密(node.bcrypt.js),数据压缩和解压(node-tar),或者要根据用户的身份对图片做些个性化处理,在这些场景下,主线程致力于做复杂的CPU计算,I/O请求队列中的任务就被阻塞。


Node.js主线程的event loop在处理所有的任务/事件时,都是沿着事件队列顺序执行的,所以在其中任何一个任务/事件本身没有完成之前,其它的回调、监听器、超时、nextTick()的函数都得不到运行的机会,因为被阻塞的event loop根本没机会处理它们,此时程序最好的情况是变慢,最糟的情况是停滞不动,像死掉一样。


一个可行的解决方案是新开进程,通过IPC通信,将CPU密集型任务交给子进程,子进程计算完毕后,再通过ipc消息通知主进程,并将结果返回给主进程。


和创建线程相比,开辟新进程的系统资源占用率大,进程间通信效率也不高。如果能不开新进程而是新开线程,将CPU耗时任务交给一个工作线程去做,然后主线程立即返回,处理其他的I/O请求,等到工作线程计算完毕后,通知主线程并将结果返回给主线程。那么在同时面对I/O密集型和CPU密集型服务的场景下,Node的主线程也会变得轻松,并能时刻保持高响应度。


因此,和开进程相比,一个更加优秀的解决方案是:

  1. 不开进程,而是将CPU耗时操作交给进程内的一个工作线程完成。

  2. CPU耗时操作的具体逻辑支持通过C++JS实现。

  3. JS使用这个机制与使用I/O库类似,方便高效。

  4. 在新线程中运行一个独立的V8 VM,与主线程的VM并发执行,并且这个线程必须由我们自己托管。


为了实现以上四个目标,我们在Node中增加了一个backgroundthread线程,文章稍候会详细解释这个概念。在具体实现上,为Node增加了一个pt_c的内建C++模块。这个模块负责把CPU耗时操作封装成一个Task,抛给backgroundthread,然后立即返回。具体的逻辑在另一个线程中处理,完成之后,设定结果,通知主线程。这个过程非常类似于异步I/O请求。具体逻辑如下图:


Node提供了一种机制可以将CPU耗时操作交给其他线程去做,等到执行完毕后设置结果通知主线程执行callback函数……


总结

这篇文章提出了backgroundjs这个新的概念,扩展了Node.js的能力,解决了Node在处理CPU密集任务时的短板。这个解决方案使得使用Node的开发人员只需要关注backgroundjs中的函数。比起多开进程或者新添加模块的解决方案更高效,通用和一致。


更多精彩内容,请点击阅读原文。

 
InfoQ 更多文章 Facebook如何实现PB级别数据库自动化备份 学术派Google软件工程师Matt Welsh谈移动开发趋势 Spotify为什么要使用一些“无聊”的技术? 妹纸们放假了,汉纸们做啥? 大多数重构可以避免
猜您喜欢 【Cocos游戏】《怪物弹珠》1000万传奇:讲述背后的故事 35行代码搞定事件研究法(下) 来自于编程大师的职业建议:别老想着写码 2016年John Chambers统计软件奖揭晓 【VSTS 日志】TFS 2015 Update 1 发布 – Git和TFVC代码库可以混合使用了