You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Express.js如何使用多线程/异步处理并发HTTP请求及扩容

核心结论先给你
  • Node.js 13+ 稳定的worker_threads特性默认完全不会自动生效,Node不会因为请求并发高就主动开新线程处理请求,所有Express路由的回调默认都跑在主线程的事件循环里。
  • 10000个并发请求同时打过来,只要你的接口逻辑没有阻塞主线程的CPU密集操作,默认单线程的异步IO模型就能正常调度处理,根本不需要额外开线程;一旦有CPU重逻辑堵了事件循环,哪怕100个并发也会把整个服务卡到超时。
落地worker_threads的正确姿势

首先先搞清楚适用场景,别上来就瞎用:

  • 如果你的接口是常规CRUD(查库、读缓存、调第三方接口这类IO密集逻辑),完全不需要用worker_threads,Node默认的异步IO模型扛几万QPS都没问题,瓶颈一般在下游依赖,不在Node本身。
  • 只有当接口里存在CPU密集逻辑(大文件加解密、复杂计算、大体积JSON序列化/反序列化、图片处理、容易触发回溯的正则匹配等),才需要用worker_threads把这部分逻辑剥离到子线程,避免堵死主线程。

具体实现注意这几点:

  1. 绝对不要请求来了才临时创建worker:创建线程的开销非常大,高并发下临时创建线程会直接打满内存把服务搞崩,服务启动时就要预先创建固定大小的线程池,线程数一般和服务器CPU核心数对齐就行,开多了会因为CPU上下文切换反而降低性能。
  2. 线程和主线程内存隔离,不能直接共享变量,只能通过postMessage传递数据,默认传参会做结构化克隆,大对象传输开销不低,如果传超大Buffer可以用转移所有权的方式避免拷贝。
  3. 不要在worker线程里起Express/HTTP服务,worker只用来跑剥离出来的CPU密集任务,多进程起HTTP服务的活交给cluster模块或者PM2集群模式做。

给个最小可运行的示例:
首先是主线程Express服务逻辑:

const express = require('express');
const { Worker } = require('worker_threads');
const os = require('os');
const app = express();

// 初始化固定大小线程池
const WORKER_POOL = [];
const POOL_SIZE = os.cpus().length;
for (let i = 0; i < POOL_SIZE; i++) {
  const worker = new Worker('./cpu-task.js');
  WORKER_POOL.push({ instance: worker, busy: false });
}

// 取空闲线程
const getIdleWorker = () => WORKER_POOL.find(w => !w.busy);

app.get('/heavy-calc', async (req, res) => {
  const worker = getIdleWorker();
  // 没有空闲线程直接返回繁忙,不要无限排队把进程拖死
  if (!worker) return res.status(429).send('Server busy, try later');
  
  worker.busy = true;
  worker.instance.postMessage({ calcNum: Number(req.query.n) });
  // 监听子线程返回结果
  worker.instance.once('message', (calcResult) => {
    worker.busy = false;
    res.send({ code: 0, data: calcResult });
  });
});

app.listen(3000, () => console.log('Service running on :3000'));

然后是子线程任务文件cpu-task.js:

const { parentPort } = require('worker_threads');

// 接收主线程派发的任务
parentPort.on('message', (task) => {
  // 这里放CPU密集逻辑,示例为大循环累加
  let sum = 0;
  for (let i = 0; i < task.calcNum * 10_000_000; i++) {
    sum += i;
  }
  // 计算完把结果发回主线程
  parentPort.postMessage(sum);
});
大规模请求的扩容方案

worker_threads只能解决单进程内CPU阻塞事件循环的问题,扛大规模流量要做多层扩容:

  • 单机层面:用Node自带的cluster模块或者PM2集群模式启动服务,按照CPU核心数起对应数量的Node进程,充分利用多核性能,避免单进程跑满一个核心其他核心闲置的问题。
  • 多机层面:前面挂负载均衡(比如Nginx),把流量分发到多台部署了服务的机器,做水平扩容,扛住超过单机上限的流量。
  • 运维层面:给接口加限流、降级逻辑,避免流量突增把整个集群打垮。

内容的提问来源于stack exchange,提问作者efoc

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 09:33:28