NodeJS的JavaScript运行在单个进程的单个线程上,一个JavaScript执行进程只能利用一个CPU核心,而如今大多数CPU均为多核CPU,为了充分利用CPU资源,Node提供了child_process和cluster模块来实现多进程以及进程管理。本文将根据Master-Worker模式,搭建一个简单的服务器集群来充分利用多核CPU资源,探索进程间通信、负载均衡、进程重启等知识。
下图是Master-Worker模式,进程分为master进程和worker进程,master进程负责调度或管理worker进程,worker进程则负责具体的业务处理。在服务器层面,worker可以是一个服务进程,负责处理来自客户端的请求,多个worker便相当于多个服务器,从而构成一个服务器集群。master则是负责创建worker,将来自客户端的请求分配到各个服务器上去处理,并监控worker的运行状态以及进行管理等操作。
本文将从child_process模块开始,熟悉该模块的基本用法。后面再继续进入cluster模块的学习。本文所用的代码示例可以从该仓库中找到–【multi-process】。
一、child_process
1.1、Hello world
child_process模块提供了spawn()
、exec()
、execFile()
、fork()
这4个方法用于创建子进程,本文将使用fork()
方法来创建子进程,fork()
方法只需指定要执行的JavaScript文件模块,即可创建Node的子进程。下面是简单的HelloWorld示例,master进程根据CPU数量创建出相应数量的worker进程,worker进程中利用进程ID来标记自己。
以下是master进程代码,文件名为master.js。
const childProcess = require('child_process')
const cpuNum = require('os').cpus().length
for (let i = 0; i < cpuNum; ++i) {
childProcess.fork('./worker.js')
}
console.log('Master: Hello world.')
以下是worker进程的代码,文件名为worker.js。
console.log('Worker-' + process.pid + ': Hello world.')
执行node master.js
,得到如下结果,master创建4个worker后输出HelloWorld信息,每个worker也分别输出自己的HelloWorld信息。
1.2、父子进程间的通信
创建worker之后,接下来实现master和worker之间的通信。Node父子进程之间可以通过on('message')
和send()
来实现通信,on('message')
其实是监听message
事件,当该进程收到其他进程发送的消息时,便会触发message
事件。send()
方法则是用于向其他进程发送信息。master进程中调用child_process
的fork()
方法后会得到一个子进程的实例,通过这个实例可以监听来自子进程的消息或者向子进程发送消息。worker进程则通过process
对象接口监听来自父进程的消息或者向父进程发送消息。
下面是简单示例,master创建worker之后,向worker发送信息,worker在收到master的信息后将信息输出,并回复master。master收到回复后输出信息。
master.js
const childProcess = require('child_process')
const worker = childProcess.fork('./worker.js')
worker.send('Hello world.')
worker.on('message', (msg) => {
console.log('[Master] Received message from worker: ' + msg)
})
worker.js
process.on('message', (msg) => {
console.log('[Worker] Received message from master: ' + msg)
process.send('Hi master.')
})
执行node master.js
,结果如下,master和worker可以正常通信。