Q11: cluster 模块是做什么的?它是如何利用多核 CPU 的?

71 阅读1分钟

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 密集型应用