使用libuv开发C++ Addon实现Nodejs子进程同步通信

Electron

使用过Electron的同志们都知道,Electron中的渲染进程(renderer process) 和主进程间,可以使用ipc模块同时使用两种方法进行通信:

  1. 异步通信,渲染进程不阻塞,使用监听进行返回:
  2. 同步通信,渲染进程阻塞,从主进程得到返回值后将返回值直接作为该通信调用的返回值

而nodejs的源生childProcess模块fork出的非阻塞性子进程只

支持异步通信(注:曾经0.12版本以前只支持同步通信),要类似与Electron实现非阻塞型子进程和主进程间的同步通信,需要使用其他方案。

阻塞子进程

为了达成这个目标,第一步就是使用阻塞住子线程(停止js代码的运行,或者使他空运行)。

显然类似于使用while,for等循环体空运行结合异步通信的方案不可行,它无法在运行空循环体时调用process.on内定义的回调函数来接受来自主进程的信息。

因此,考虑到js实际上是单线程运行的脚本,并且js中也不存在能在同步运行时获取主进程信息的方法(实际上这正是我们要实现的!),我们需要更进一步,进入C++的运行层面,使用C++ Addon来完成这个功能。

Node C++ Addon

为了更好的扩展性,Node.js 提供了使用C++ Addon来拓展js功能的方案。而今天我们就要使用者一方案来实现阻塞子进程并且同时和主进程通信的功能。

关于C++ Addon的详细介绍,可以参考官方文档及NAN(https://github.com/nodejs/nan)。

libuv和pipe管道

libuv是v8团队开发的跨平台的事件驱动I/O库,也是nodejs运行的核心之一,它提供了很多有用的功能,而其中之一就是用于进程间通信的uv_pipe_t handle。它可以创建一条用于进程间通信的管道,并且完成全/半双工的数据交换。

功能模型

有了这些准备工具,我们要做的事情就很清楚的分为了这么几件事:

  1. 在主进程中创建pipe的server,监听从子进程到来的请求,并由js绑定回调函数告诉c++层如何向子进程发送数据。
  2. 当子进程运行至同步通信代码时,使用libuv创建新的loop用来阻塞js的主loop,同时连接到主进程的pipe并写入请求。
  3. 当主进程的c++层获得请求内容时,调用js的回调函数,将数据写入子进程
  4. 子进程获取数据后结束阻塞用的loop,返回数据并继续运行js程序。

最终代码

最终实现的代码可参见https://github.com/MMhunter/node-sync-ipc

发表评论