简介

NodeJS的JavaScript运行在单个进程的单个线程上,一个JavaScript执行进程只能利用一个CPU核心,而如今大多数CPU均为多核CPU,为了充分利用CPU资源,Node提供了child_process和cluster模块来实现多进程以及进程管理。

child_process

child_process创建子进程的方法有如下四个

  • spawn():启动一个子进程来执行命令
  • exec():启动一个子进程来执行命令,并且通过回调函数通知子进程状况
  • execFile():启动一个子进程来执行可执行文件
  • fork():是 spawn() 的一个特例,创建子进程只需要指定要执行的 javascript 文件模块即可,子进程返回一个 child_process 对象,并附加父子进程间的通信渠道
  • exec() 与 execFile() 创建时可以指定 timeout 属性设置超时时间,一旦创建的进程运行超过设定的时间将会被杀死

开始

这里使用fork()方法来创建子进程,fork()方法只需指定要执行的JavaScript文件模块,即可创建Node子进程

//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.js
console.log('Worker-' + process.pid + ': Hello world.')

图片说明

父子进程通信

通信的方法主要有以下两个

  • on('message') --接收
  • send() --发送
//master.js
const childProcess = require('child_process')
const worker  = childProcess.fork('./worker.js')
worker.send('你好 worker')
worker.on('message',msg=>{
    console.log(`我收到了来自子进程的消息:${msg}`)
})
//worker.js
process.on('message', msg => {
    console.log(`我收到了来自master的消息:${msg}`)
    process.send('你好 master')
})

图片说明

处理HTTP服务

//master.js
const { createServer } = require('net')
const childProcess = require('child_process')
const cpuNum = require('os').cpus().length
let workers = []
for (let i = 0; i < cpuNum; ++i) {
    workers.push(childProcess.fork('./worker.js'))
    console.log('Create worker-' + workers[i].pid)
}
//创建tcp服务器
const server = createServer()
server.listen(8888, () => {
    console.log(`server listen 8888`)
    for (let i = 0; i < cpuNum; ++i) {
        workers[i].send('server', server)
    }
    // 关闭主线程服务器的端口监听
    server.close()
})
//worker.js
const http = require('http')
const httpServer = http.createServer((req, res) => {
    // 利用setTimeout模拟处理请求时的操作耗时
    setTimeout(() => {
        res.writeHead(200, { 'Content-Type': 'text/plain' })
        res.end('Request handled by worker-' + process.pid)
    }, 10)
})
process.on('message', (msg, server) => {
    if (msg === 'server' && server) {
        server.on('connection', (socket) => {
            // 提交给HTTP服务器处理
            httpServer.emit('connection', socket)
        })
    }
})

cluster

Node提供了cluster模块,该模块提供了更完善的API,除了能够实现多进程充分利用CPU资源以外,还能够帮助我们更好地进行进程管理和处理进程的健壮性问题。

const cluster = require('cluster')
const express = require('express')
const app = express()
if (cluster.isMaster) {
    const cpuNum = require('os').cpus().length
    for (let i = 0; i < cpuNum; ++i) {
        cluster.fork()
    }
    // 创建进程完成后输出提示信息
    cluster.on('online', (worker) => {
        console.log('Create worker-' + worker.process.pid)
    })
    // 子进程退出后重启
    cluster.on('exit', (worker, code, signal) => {
        console.log('[Master] worker ' + worker.process.pid + ' died with code: ' + code + ', and signal: ' + signal)
        cluster.fork()
    })
} else {
    app.get('/', (req, res) => {
        res.end(`worker:${process.pid}`)
    })
    app.listen(8888, () => {
        console.log(`app listen 8888`)
    })
}

测试脚本

//client.js
var request = require('request');
var mock = function () {
    request({
        url: 'http://127.0.0.1:8888',
        method: "get",
    }, function (error, response, body) {
        if (!error && response.statusCode == 200) {
            console.log(response.body)
        }
    });
}
for (let index = 0; index < 10; index++) {
    mock()
}

pm2

https://blog.nowcoder.net/n/b9c44257e37c4f0ea67966dcf105ca96