微信号:infoqchina

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

Node.js V0.12新特性之在单进程中跑多个实例

2014-04-03 18:59 InfoQ

实例


经常有人提出,希望Node.js能被嵌入到其他程序中,特别是让它能跟其他事件循环整合而且(与此同时)支持多个Node执行情境:也就是说让多个Node实例在同一个进程中和平共处的能力。想象一下,比如有个node-webkit 程序,每个窗口都运行在自己的Node实例上,各窗口彼此相互独立。或者把Node嵌在手机或网络交换机里,处理多连接的路由逻辑,但却只是在单个进程中,并且不久的将来就能实现。


一个客户找到我们,说他们的程序需要这类功能。他们经过调研,肯定了我们在Node核心和libuv上的贡献和专业能力,决定请我们帮忙。这是个相当有挑战性的订单,因为Node一开始就是-并且现在仍然是-一个单线程的程序,围绕着单一事件循环的概念构建,用上百个全局变量存放各种状态。


你可以想象一下,在这样的代码库上加装线程安全是多么容易出错的任务,所以那不是我们要做的-或者说我们还没那么做。我们希望这个修改对客户来说经济实惠,并且向前迈出这一步也能帮到整个社区。顺便说一下,如果你发现自己需要对Node做些修改,可自己又没时间研究该怎么改,我们可以帮忙!


引入多情境


实际上,我们在Node v0.12中实现了在同一个事件循环中使用多个执行情境的能力。别担心:对普通用户来说并没有显性的变化,一切都和以前一样。但如果你是嵌入开发人员,或本地附加组件作者,请继续往下看!


提交756b622中的“多情境”工作是第一次,也是最重要的清理任务:它把所有全局变量审了一遍,首先去掉了那些不需要放在全局中的变量,并将剩下的变成了执行情境的属性。虽然那还不足以保证线程安全,但这第一步很重要。


第二步是让Node内部意识到多执行情境的存在。C++运行时在任何地方进入V8 VM,都要先确保它进入的是正确的情境。下面是一个简单的例子:


function onconnect() {

this.write('GET / HTTP/1.1\r\n' +

'Host: strongloop.com\r\n' +

'\r\n');

this.pipe(process.stdout);

}

require('net').connect(80, 'strongloop.com', onconnect);


Execution-wise,差不多应该是这个样子的:


<enter VM>

connect(80, 'strongloop.com'); // kernel reports EINPROGRESS here

<leave VM>

<wait for TCP handshake to complete>

<enter VM>

onconnect();

<leave vm>


在进入VM的调用之间可能会改变执行情境。因此connect()有必要记住当前情境,并且调用onconnect()能恢复它。如果做不到这一点,追踪bug将会变得极其困难。


好在Node可以自动把这个处理好;嵌入开发人员和本地附加组件作者不需要担心这个,除非他们想用v8::Function::Call()代替node::MakeCallback()进入VM做调用。


如果你是本地附加组件作者,想让你的附件组件具备识别情境的能力,你需要:


* Instead of NODE_MODULE(), use 

NODE_MODULE_CONTEXT_AWARE(). Before:

 void Initialize(v8::Handle<v8::Object> exports) {

// ...

}

NODE_MODULE(foo, Initialize)


之后:


void Initialize(v8::Handle<v8::Object> exports,

v8::Handle<v8::Value> module,

v8::Handle<v8::Context> context) {

// ...

}

NODE_MODULE_CONTEXT_AWARE(foo, Initialize);


能识别情境的初始化方法在嵌入创建的每个情境都会被调用一次。现在还没有各情境的清理函数,最终会由node::AtExit()承担这一职责,但目前还是一个针对进程的事件。如果对你来说这样不行,可以提交bug。


将全局变量变成各情境的属性有几种办法,其中一个是为你自己声明一个嵌入数据索引,并把所有东西都存在那里。


这些索引还没有全局注册表,所以仍有发生冲突的可能。有没有想试着给打个补丁?同时挑一个差不多大的随机数(比如2^10到2^16之间的),但不要太极端:V8用一个非稀疏矩阵作为嵌入索引的内部存储。将索引声明为1 << 29就会消耗很多内存!


还有多少工作要做?


这里还有些粗糙的边界需要打磨:process.chdir()会改变所有情境的工作目录,但实际上应该只改变调用它的情境的工作目录;从多情境中加载附加组件还有些边界情况,等等诸如此类的地方。但那属于修修补补力求尽善尽美的工作了。基本框架已经到位了,并且那家赞助我们开发多情境特性的公司已经成功地用上它了。更重要的是,它为多线程多重租赁铺平了道路。那在Node v0.12之前不太可能发生了,但我们可能会在Node v1.0或另一个版本中见到它-只要我们敢!


***********************************

本文来自InfoQ微信公众账号:infoqchina

1、回复“今日新闻”,查看今天更新的新闻;

2、回复“今日英文”,查看今天英文站的更新;

3、回复“文章 +关键词”,搜索关键词相关内容;

4、回复“QCon”,了解QCon大会相关信息;

5、回复“活动”,了解最近InfoQ组织的线下沙龙;

6、回复“架构师”,获取《架构师》下载地址;

7、回复“投稿”,了解投稿和加入编辑团队的流程。

***********************************

 
InfoQ 更多文章 Facebook如何实现PB级别数据库自动化备份 学术派Google软件工程师Matt Welsh谈移动开发趋势 Spotify为什么要使用一些“无聊”的技术? 妹纸们放假了,汉纸们做啥? 大多数重构可以避免
猜您喜欢 PHP5.6新特性介绍 最反人类的设计集合 别说走就走!跳槽前你应该问自己的两个问题 EF Core 1.0中使用Include的小技巧 围绕着内存数据库的4个流言