IPC创建和实现示意图

IPC通信管道是如何创建的

父进程在实际创建子进程之前,会创建IPC通道并监听它,然后才真正的创建出子进程,这个过程中也会通过环境变量(NODE_CHANNEL_FD)告诉子进程这个IPC通道的文件描述符。子进程在启动的过程中,根据文件描述符去连接这个已存在的IPC通道,从而完成父子进程之间的连接。
Node.js句柄传递
讲句柄之前,先想一个问题,send句柄发送的时候,真的是将服务器对象发送给了子进程?
子进程对象send()方法可以发送的句柄类型
- net.Socket TCP套接字
- net.Server TCP服务器,任意建立在TCP服务上的应用层服务都可以享受它带来的好处
- net.Native C++层面的TCP套接字或IPC管道
- dgram.Socket UDP套接字
- dgram.Native C++层面的UDP套接字
send句柄发送原理分析
结合句柄的发送与还原示意图更容易理解。

send()方法在将消息发送到IPC管道前,实际将消息组装成了两个对象,一个参数是hadler,另一个是message。message参数如下所示:
- {
- cmd:'NODE_HANDLE',
- type:'net.Server',
- msg:message
- }
发送到IPC管道中的实际上是我们要发送的句柄文件描述符。这个message对象在写入到IPC管道时,也会通过JSON.stringfy()进行序列化。所以最终发送到IPC通道中的信息都是字符串,send()方法能发送消息和句柄并不意味着它能发送任何对象。
连接了IPC通道的子线程可以读取父进程发来的消息,将字符串通过JSON.parse()解析还原为对象后,才触发message事件将消息传递给应用层使用。在这个过程中,消息对象还要被进行过滤处理,message.cmd的值如果以NODE_为前缀,它将响应一个内部事件internalMessage,如果message.cmd值为NODE_HANDLE,它将取出message.type值和得到的文件描述符一起还原出一个对应的对象。
以发送的TCP服务器句柄为例,子进程收到消息后的还原过程代码如下:
- function(message,handle,emit){
- var self = this;
-
- var server = new net.Server();
- server.listen(handler,function(){
- emit(server);
- });
- }
这段还原代码,子进程根据message.type创建对应的TCP服务器对象,然后监听到文件描述符上。由于底层细节不被应用层感知,所以子进程中,开发者会有一种服务器对象就是从父进程中直接传递过来的错觉。
Node进程之间只有消息传递,不会真正的传递对象,这种错觉是抽象封装的结果。目前Node只支持我前面提到的几种句柄,并非任意类型的句柄都能在进程之间传递,除非它有完整的发送和还原的过程。
Node.js多进程架构模型
我们自己实现一个多进程架构守护Demo

编写主进程
master.js 主要处理以下逻辑:
- 创建一个 server 并监听 3000 端口。
- 根据系统 cpus 开启多个子进程
- 通过子进程对象的 send 方法发送消息到子进程进行通信
- 在主进程中监听了子进程的变化,如果是自杀信号重新启动一个工作进程。
- 主进程在监听到退出消息的时候,先退出子进程在退出主进程
- // master.js
- const fork = require('child_process').fork;
- const cpus = require('os').cpus();
-
- const server = require('net').createServer();
- server.listen(3000);
- process.title = 'node-master'
-
- const workers = {};
- const createWorker = () => {
- const worker = fork('worker.js')
- worker.on('message', function (message) {
- if (message.act === 'suicide') {
- createWorker();
- }
- })
- worker.on('exit', function(code, signal) {
- console.log('worker process exited, code: %s signal: %s', code, signal);
- delete workers[worker.pid];
- });
- worker.send('server', server);
- workers[worker.pid] = worker;
- console.log('worker process created, pid: %s ppid: %s', worker.pid, process.pid);
- }
-
- for (let i=0; i<cpus.length; i++) {
- createWorker();
- }
-
- process.once('SIGINT', close.bind(this, 'SIGINT')); // kill(2) Ctrl-C
- process.once('SIGQUIT', close.bind(this, 'SIGQUIT')); // kill(3) Ctrl-
- process.once('SIGTERM', close.bind(this, 'SIGTERM')); // kill(15) default
- process.once('exit', close.bind(this));
-
- function close (code) {
- console.log('进程退出!', code);
-
- if (code !== 0) {
- for (let pid in workers) {
- console.log('master process exited, kill worker pid: ', pid);
- workers[pid].kill('SIGINT');
- }
- }
-
- process.exit(0);
- }
工作进程 (编辑:西安站长网)
【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!
|