Published on

Express 利用多进程及其安全性问题

Authors
  • avatar
    Name
    Shelton Ma
    Twitter

Express 本身是单线程的,但可以通过 Node.js 的 cluster 模块 或 pm2 等工具来利用多核 CPU,从而提升性能.

多进程的引入虽然提高了性能,但也可能带来数据一致性、资源竞争等潜在的进程不安全问题.以下是详细的讲解.

1. 利用 cluster 模块实现多进程

  1. cluster 模块简介

    • Node.js 自带的原生模块
    • 通过 fork() 创建多个子进程,每个子进程独立运行 Express 实例
    • 主进程负责负载均衡(默认基于 round-robin 轮询机制)
  2. 优势

    • 提高性能:充分利用多核 CPU,提高并发能力
    • 进程隔离:某个子进程崩溃不会影响其他进程,稳定性更高
  3. cluster 多进程实现

    import cluster from 'cluster';
    import os from 'os';
    import express from 'express';
    
    if (cluster.isPrimary) {
      const numCPUs = os.cpus().length;
      console.log(`主进程 ${process.pid} 正在运行`);
    
      // 创建与 CPU 核心数相同的子进程
      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.send(`响应来自进程 ${process.pid}`);
      });
    
      app.listen(3000, () => {
        console.log(`工作进程 ${process.pid} 启动,监听 3000 端口`);
      });
    }
    

2. 利用 pm2 实现多进程

  1. pm2 更适合生产环境,提供更丰富的功能:

    • 自动负载均衡
    • 进程守护,自动重启
    • 日志管理、性能监控
  2. 使用 pm2 快速启动

    1. 安装npm install -g pm2

    2. 启动多进程 pm2 start server.js -i max

    3. 其他命令:

      pm2 restart server           # 重启服务
      pm2 list                     # 查看所有进程
      pm2 logs                     # 查看日志
      pm2 delete server            # 停止并删除服务
      

3. 多进程的潜在风险及解决方案

风险 1:进程间的数据共享问题

每个子进程拥有独立的内存空间,无法直接共享数据.

解决方案: 使用共享存储 (如 Redis), 使用 Redis 等外部缓存来共享数据,确保多进程环境下数据一致性.

示例: 使用 Redis 共享 Session 数据

import session from 'express-session';
import RedisStore from 'connect-redis';
import { createClient } from 'redis';

const redisClient = createClient({ url: 'redis://localhost:6379' });

app.use(
  session({
    store: new RedisStore({ client: redisClient }),
    secret: 'your-secret-key',
    resave: false,
    saveUninitialized: false,
  })
);

风险 2:文件锁、日志冲突

多个进程写入同一日志文件时,可能导致数据覆盖、内容错乱.

解决方案: 使用 pino-http、winston、logrotate 等工具

  • pino-http 具有流式日志输出,支持多进程环境
  • logrotate 自动管理日志切割,防止单个日志文件过大

风险 3:负载均衡问题

cluster 自带的轮询机制并不总是最优,尤其在 CPU 密集型任务时可能失衡.

解决方案: 使用 Nginx 进行更高级的负载均衡 Nginx 提供基于 IP Hash、权重、最少连接数等策略 提高请求分发的灵活性和稳定性

风险 4:任务锁 (Redlock)

当多个进程同时执行需要锁机制的操作时,可能导致竞争条件或数据错乱.

解决方案: 使用 Redlock 机制确保分布式锁, 比如: Redis + Redlock

4. 关键点总结

  • cluster 提供轻量级多进程支持,适合快速实现多核利用
  • pm2 是更完善的生产级解决方案,包含守护进程、自动重启、性能监测等功能
  • 数据共享、日志、负载均衡、锁机制等问题需单独考虑,避免潜在的并发问题