Node.js 面试题详细答案 - Q11
Q11: cluster 模块是做什么的?它是如何利用多核 CPU 的?
Cluster 模块概述
Cluster 模块允许创建共享服务器端口的子进程,充分利用多核 CPU 的性能。
基本使用
const cluster = require('cluster')
const http = require('http')
const numCPUs = require('os').cpus().length
if (cluster.isMaster) {
console.log(`主进程 ${process.pid} 正在运行`)
// 创建工作进程
for (let i = 0; i < numCPUs; i++) {
cluster.fork()
}
cluster.on('exit', (worker, code, signal) => {
console.log(`工作进程 ${worker.process.pid} 已退出`)
cluster.fork() // 重启工作进程
})
} else {
// 工作进程共享同一个端口
http
.createServer((req, res) => {
res.writeHead(200)
res.end('Hello World')
})
.listen(8000)
console.log(`工作进程 ${process.pid} 已启动`)
}
工作原理
1. 主进程管理
const cluster = require('cluster')
if (cluster.isMaster) {
// 主进程负责:
// 1. 创建工作进程
// 2. 监听工作进程状态
// 3. 处理进程间通信
const worker = cluster.fork()
worker.on('message', (msg) => {
console.log('收到工作进程消息:', msg)
})
worker.send({ type: 'task', data: 'Hello Worker' })
}
2. 工作进程处理
if (cluster.isWorker) {
// 工作进程负责:
// 1. 处理实际业务逻辑
// 2. 与主进程通信
process.on('message', (msg) => {
console.log('收到主进程消息:', msg)
process.send({ type: 'result', data: 'Task completed' })
})
}
负载均衡
默认负载均衡策略
const cluster = require('cluster')
const http = require('http')
if (cluster.isMaster) {
// 创建多个工作进程
for (let i = 0; i < 4; i++) {
cluster.fork()
}
// 监听工作进程消息
Object.values(cluster.workers).forEach((worker) => {
worker.on('message', (msg) => {
console.log(`工作进程 ${worker.process.pid}: ${msg}`)
})
})
} else {
// 每个工作进程处理请求
http
.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'application/json' })
res.end(
JSON.stringify({
pid: process.pid,
message: 'Hello from worker',
})
)
})
.listen(3000)
}
自定义负载均衡
const cluster = require('cluster')
const http = require('http')
if (cluster.isMaster) {
const workers = []
// 创建工作进程
for (let i = 0; i < 4; i++) {
workers.push(cluster.fork())
}
// 自定义负载均衡
let currentWorker = 0
const server = http.createServer((req, res) => {
const worker = workers[currentWorker]
currentWorker = (currentWorker + 1) % workers.length
// 转发请求到工作进程
worker.send({ type: 'request', data: req.url })
})
server.listen(3000)
} else {
process.on('message', (msg) => {
if (msg.type === 'request') {
console.log(`工作进程 ${process.pid} 处理请求: ${msg.data}`)
}
})
}
进程间通信
主进程与工作进程通信
const cluster = require('cluster')
if (cluster.isMaster) {
const worker = cluster.fork()
// 主进程发送消息
setInterval(() => {
worker.send({ type: 'ping', timestamp: Date.now() })
}, 1000)
// 接收工作进程消息
worker.on('message', (msg) => {
if (msg.type === 'pong') {
console.log('收到 pong 响应')
}
})
} else {
// 工作进程处理消息
process.on('message', (msg) => {
if (msg.type === 'ping') {
process.send({ type: 'pong', timestamp: Date.now() })
}
})
}
工作进程间通信
const cluster = require('cluster')
if (cluster.isMaster) {
const workers = []
// 创建多个工作进程
for (let i = 0; i < 2; i++) {
workers.push(cluster.fork())
}
// 转发消息到其他工作进程
workers.forEach((worker) => {
worker.on('message', (msg) => {
if (msg.type === 'broadcast') {
workers.forEach((otherWorker) => {
if (otherWorker !== worker) {
otherWorker.send(msg)
}
})
}
})
})
} else {
// 工作进程发送广播消息
setInterval(() => {
process.send({
type: 'broadcast',
data: `Hello from ${process.pid}`,
from: process.pid,
})
}, 2000)
// 接收广播消息
process.on('message', (msg) => {
if (msg.type === 'broadcast' && msg.from !== process.pid) {
console.log(`工作进程 ${process.pid} 收到: ${msg.data}`)
}
})
}
错误处理和重启
自动重启工作进程
const cluster = require('cluster')
const http = require('http')
if (cluster.isMaster) {
console.log(`主进程 ${process.pid} 启动`)
// 创建工作进程
for (let i = 0; i < 4; i++) {
cluster.fork()
}
// 监听工作进程退出
cluster.on('exit', (worker, code, signal) => {
console.log(`工作进程 ${worker.process.pid} 退出,代码: ${code}`)
if (code !== 0 && !worker.exitedAfterDisconnect) {
console.log('重启工作进程...')
cluster.fork()
}
})
// 优雅关闭
process.on('SIGTERM', () => {
console.log('主进程收到 SIGTERM,关闭所有工作进程')
for (const id in cluster.workers) {
cluster.workers[id].kill()
}
})
} else {
// 工作进程处理请求
http
.createServer((req, res) => {
// 模拟随机错误
if (Math.random() < 0.1) {
throw new Error('随机错误')
}
res.writeHead(200)
res.end(`Hello from worker ${process.pid}`)
})
.listen(3000)
console.log(`工作进程 ${process.pid} 启动`)
}
性能监控
监控工作进程性能
const cluster = require('cluster')
const http = require('http')
if (cluster.isMaster) {
const workers = []
// 创建工作进程
for (let i = 0; i < 4; i++) {
workers.push(cluster.fork())
}
// 监控工作进程
setInterval(() => {
Object.values(cluster.workers).forEach((worker) => {
const usage = process.memoryUsage()
console.log(`工作进程 ${worker.process.pid}:`)
console.log(` 内存使用: ${Math.round(usage.heapUsed / 1024 / 1024)}MB`)
console.log(` 请求数: ${worker.requestCount || 0}`)
})
}, 5000)
} else {
let requestCount = 0
// 工作进程处理请求
http
.createServer((req, res) => {
requestCount++
process.send({ type: 'requestCount', count: requestCount })
res.writeHead(200)
res.end(`Request ${requestCount} from worker ${process.pid}`)
})
.listen(3000)
}
实际应用场景
Web 服务器集群
const cluster = require('cluster')
const express = require('express')
const numCPUs = require('os').cpus().length
if (cluster.isMaster) {
console.log(`启动 ${numCPUs} 个工作进程`)
for (let i = 0; i < numCPUs; i++) {
cluster.fork()
}
cluster.on('exit', (worker, code, signal) => {
console.log(`工作进程 ${worker.process.pid} 退出`)
cluster.fork()
})
} else {
const app = express()
app.get('/', (req, res) => {
res.json({
message: 'Hello from cluster',
pid: process.pid,
timestamp: new Date().toISOString(),
})
})
app.get('/heavy', (req, res) => {
// 模拟 CPU 密集型任务
let result = 0
for (let i = 0; i < 1000000; i++) {
result += Math.random()
}
res.json({ result, pid: process.pid })
})
app.listen(3000, () => {
console.log(`工作进程 ${process.pid} 监听端口 3000`)
})
}
总结
- Cluster 作用:创建多进程应用,充分利用多核 CPU
- 工作原理:主进程管理工作进程,工作进程处理实际业务
- 负载均衡:默认使用轮询策略,支持自定义
- 进程通信:通过 IPC 进行主进程与工作进程通信
- 错误处理:自动重启崩溃的工作进程
- 性能监控:监控工作进程状态和性能指标
- 适用场景:Web 服务器、API 服务、CPU 密集型应用