@eggjs/cluster 集群管理器全解析:Egg 多进程架构演进、启动模式与配置实战
2026/9/20 13:43:23 网站建设 项目流程
  • 后端
  • Web框架

【免费下载链接】egg

🥚🥚🥚🥚 Born to build better enterprise frameworks and apps with Node.js & Koa. https://307.run/eggcode

项目地址:https://gitcode.com/gh_mirrors/eg/egg
点击查看免费下载

导读

@eggjs/cluster(仓库路径 packages/cluster)是 Egg 框架的官方集群管理器,负责把一次startCluster()调用展开为 master / agent / 多 app worker 的完整进程树,并处理 fork、监听、消息路由、健康检查与优雅退出。本文以该包的 CHANGELOG.md 为主线,结合 README.md 与src/下的源码实现,梳理从 1.x 到 4.x 的关键演进(TypeScript 迁移、worker_threads 启动模式、sticky 会话保持、HTTPS 支持等),并给出完整可复用的选项说明与启动示例。读完你将掌握 Egg 多进程模型的核心原理,以及如何在生产环境中正确配置集群启动参数。

一、包定位:Egg 的进程编排层

@eggjs/cluster早期名为egg-cluster,自 3.0.0 起更名为@eggjs/cluster并全面迁移到 TypeScript(见 CHANGELOG.md 中 3.0.0 一节)。它不在 Egg 运行时内部,而是站在应用之外,通过startCluster()一次性拉起所有进程。入口源码 src/index.ts 中给出了清晰启动流程:

[startCluster] -> master -> agent_worker -> new [Agent] -> agentWorkerLoader `-> app_worker -> new [Application] -> appWorkerLoader
  • master:唯一的控制进程,负责 fork 与管理子进程、端口检测、消息转发、健康检查;
  • agent_worker:常驻的 Agent 实例进程,承载与业务无关的后台任务(如日志切割、定时调度协调);
  • app_worker:若干 Application 实例进程,真正对外提供 HTTP(S) 服务。

包名的两次代际切换

CHANGELOG 记录了两个关键转折点,理解它们有助于排查升级问题:

版本变更影响
3.0.0包名egg-cluster@eggjs/cluster,改为 ES Module 导出,Node 最低版本提到 18.19.0旧的require('egg-cluster')需要同步迁移,且低版本 Node 不再受支持
4.0.0+移除 Node.js < 22.18.0 支持,仅支持 egg@4对应 package.json 中"engines": { "node": ">=22.18.0" },属于硬性破坏性变更

CHANGELOG 顶部还注明:后续版本的变更记录已统一迁移到 GitHub Releases 页面,仓库内 CHANGELOG 仅保留历史快照,因此下文以源码实现为准来验证各版本累积下来的能力。

二、核心 API:startCluster 与 Master

2.1 最小的启动代码

CommonJS:

const { startCluster } = require('@eggjs/cluster'); startCluster({ baseDir: '/path/to/app', framework: '/path/to/framework', });

ESM 与 TypeScript:

import { startCluster } from '@eggjs/cluster'; startCluster({ baseDir: '/path/to/app', framework: '/path/to/framework', });

startCluster返回一个 Promise,应用完全启动后 resolve(src/index.ts):

startCluster(options).then(() => { console.log('started'); });

2.2 Master 内部做了什么

Master继承自get-readyReadyEventEmitter(src/master.ts)。从源码看,构造函数启动后依次执行:

  1. parseOptions()解析并校验全部选项(src/utils/options.ts);
  2. 启用 Node 编译缓存(设置NODE_COMPILE_CACHEbaseDir/.egg/compile-cache);
  3. 初始化WorkerManager(worker 登记与健康检查)与Messenger(消息总线);
  4. 读取 framework 的package.json,打印 Node / 框架版本信息;
  5. 注册agent-exitagent-startapp-exitapp-startreload-worker事件处理器,以及SIGINT/SIGQUIT/SIGTERM信号处理;
  6. 写入pidFile(若配置);
  7. detectPorts()探测 cluster 通信端口与 sticky worker 端口;
  8. startMode选择 process 或 worker_threads 实现,然后forkAgentWorker()——agent 启动成功后才 fork app workersthis.once('agent-start', this.forkAppWorkers.bind(this)))。

这一"先 agent 后 app"的顺序是 Egg 多进程模型的关键:Agent 失败时 master 直接以 code 1 退出,避免出现"应用已启动但依赖的后台进程异常"的半健康状态。

三、完整 Options 说明(README + 源码双重校验)

下表继承自 README.md 的 Options 表,并依据 src/utils/options.ts 补充默认值与约束:

参数类型默认值说明
baseDirStringprocess.cwd()应用目录,目录下必须存在package.json,否则parseOptions会直接 assert 失败
frameworkStringbaseDir/package.json推导框架路径,支持绝对路径或 npm 包名;customEgg已废弃,请改用本项
pluginsObject-单元测试用自定义插件
workersNumberos.cpus().lengthapp worker 数量,也接受数字字符串(内部parseInt
stickyBooleanfalse是否开启 sticky 会话保持模式
portNumberhttp 默认7001,https 默认8443监听端口;传null或字符串均可,字符串会被解析为数字
debugPortNumber-仅 http 协议监听的调试端口
httpsObject-启动 HTTPS 服务;key/cert/ca必须是指向文件的完整路径
requireArray\|String-注入到 worker / agent 进程的模块,字符串会被自动转成数组
pidFileString-master PID 写入的文件路径,退出时自动清理
startModeString'process'可选'worker_threads',用 worker_threads 启动 app 与 agent worker
portsArray-每个 app worker 的启动端口,如[7001, 7002, 7003],仅worker_threads模式生效
envStringprocess.env.EGG_SERVER_ENV自定义运行环境

3.1 HTTPS 配置的三种写法

源码 src/utils/options.ts 显示:

  • 推荐写法https: { key, cert, ca },key/cert 必填,三者均校验文件存在;
  • 兼容写法https: true+ 顶层key/cert(已废弃,会打印 deprecation 警告,最终被归一化为https: { key, cert });
  • 旧版顶层key/cert直接传参的方式(CHANGELOG 1.14.0 引入的 "https options")仍被保留兼容。

证书路径由 src/app_worker.ts 在真正 listen 前读取为 Buffer,再创建createHttpsServer。这解释了为什么文档要求"必须是完整路径"——相对路径会在 worker 进程里解析错位。

3.2 环境变量

环境变量含义
EGG_APP_CLOSE_TIMEOUTapp worker 优雅退出(关闭)的超时毫秒数
EGG_AGENT_CLOSE_TIMEOUTagent worker 优雅退出的超时毫秒数
EGG_MASTER_CLOSE_TIMEOUT两者共同的兜底超时,默认'5000'

关闭顺序与超时读取逻辑见 src/master.ts:先向所有 app worker 发SIGTERM,等待EGG_APP_CLOSE_TIMEOUT;再向 agent worker 发SIGTERM,等待EGG_AGENT_CLOSE_TIMEOUT。整体 15 秒内未完成则以 code 2 强制退出。

四、两种启动模式:process 与 worker_threads

startMode决定 worker 的底层载体,这是 2.0.0 引入的核心能力(CHANGELOG 2.0.0 一节 "feat: support worker_threads (#101)")。

4.1 process 模式(默认)

基于 Node 原生cluster+cfork。实现见 src/utils/mode/impl/process/app.ts:

  • cforkworkers数量 fork 子进程;
  • refork: this.isProduction——仅生产环境自动拉起崩溃的 worker,本地开发不自动重启;
  • windowsHide: process.platform === 'win32'(对应 1.24.0 的windowsHide支持);
  • worker 的exit事件统一转为app-exit消息回传 master,由onAppExit决定是 refork 还是退出(src/master.ts)。

4.2 worker_threads 模式

通过new ThreadWorker()线程而非独立进程里运行 Application/Agent(src/utils/mode/impl/worker_threads/app.ts),消息走parentPort.postMessage。此模式下可用ports为每个 worker 指定独立端口:

startCluster({ baseDir: '/path/to/app', startMode: 'worker_threads', workers: 3, ports: [7001, 7002, 7003], });

与 process 模式的本质区别在于:

  • worker 的标识由threadId承担,而非 PID(get workerId(): number { return this.instance.threadId; });
  • 共享同一进程内的资源,启动更轻量;
  • 兼容性注意:测试用例 test/worker_threads.test.ts 中明确标注--import=tsx/esm这类 Node 启动参数在 worker_threads 模式下不受支持,测试被it.skip跳过。

4.3 端口监听差异

process 模式下所有 worker 共享同一port(由 master/cluster 负载均衡),sticky 模式则不同(见下节);worker_threads 模式要求ports数组与workers一一对应,否则回退逻辑由 src/app_worker.ts 处options.port || listenConfig.port兜底。此外 2.1.1 曾修复server.address()返回null时自动补端口的问题(CHANGELOG 2.1.1 一节),说明 worker_threads 模式对端口的解析路径与 process 模式并不完全一致。

五、sticky 模式:会话保持的负载均衡

普通 cluster 模式下,同一客户端的多个请求可能落到不同 worker,导致本地 session 丢失。sticky 模式(1.4.0 引入,CHANGELOG 1.4.0 一节)把"同一 IP 固定路由到同一 worker":

  1. master 自己 listen 真实端口(src/master.ts),用pauseOnConnect: true挂起连接;
  2. 根据客户端remoteAddress的 IP 数字部分对workers取模,选出固定 worker(stickyWorker(ip),src/master.ts);
  3. 通过sticky-session:connection消息把 socket 转交给对应 worker,worker 端在stickyWorkerPort上监听127.0.0.1server.emit('connection', connection)接管(src/app_worker.ts)。

实现细节上有两个值得注意的修复历史:

  • 1.23.2:三次握手后收到 RST 的 socket 会被直接connection.destroy(),防止空连接破坏路由;
  • 1.23.3:sticky 模式下不应在server.listen()时过早 ready,必须等 master socket server 真正启动(对应 src/master.ts 中startMasterSocketServer回调后才ready(true)的逻辑)。

开启方式:

startCluster({ baseDir: '/path/to/app', sticky: true, });

启用后 master 日志会额外打印with STICKY MODE!(src/master.ts)。

六、消息通信:Messenger 拓扑

@eggjs/cluster自己实现了一套跨进程消息总线Messenger(src/utils/messenger.ts),拓扑为:

┌────────┐ │ parent │ /└────────┘\ / | \ / ┌────────┐ \ / │ master │ \ / └────────┘ \ / / \ \ ┌───────┐ ┌───────┐ │ agent │ ------- │ app │ └───────┘ └───────┘
  • 消息体含actiondatatofromreceiverWorkerId等字段;
  • 不指定to时有默认路由:agent -> appapp -> agentparent -> master
  • 通过receiverWorkerId可精确投递到指定 worker;
  • 在应用代码里可用process.send({ action: 'xxx', data, to: 'agent' })与 master/agent/app 通信(详见 src/utils/messenger.ts 的注释示例)。

master 侧重要的消息动作包括egg-ready(回传给 parent,携带实际port/debugPort/address/protocol)、egg-pids(同步当前存活 worker 列表给 agent/app)与agent-worker-died/app-worker-died等。app worker 启动后还会回传realport,因为真实端口可能由app.config.cluster.listen覆盖(src/master.ts)。

七、健康检查与容错机制

7.1 WorkerManager 巡检

生产环境下(env非 local/unittest 或NODE_ENV === 'production'),master 会启动周期巡检(src/utils/worker_manager.ts):

  • 每 10 秒检查一次 agent 与 worker 是否都存活;
  • 若某次检查异常计数exception++,连续 3 次异常则触发exception事件;
  • master 收到后抛出ClusterWorkerExceptionErrorprocess.exit(1)(src/master.ts)。

7.2 退出与重启策略

  • agent 退出:启动期间失败 → master 以 code 1 退出;运行期间崩溃 → 1 秒后自动重新 fork(onAgentExit,src/master.ts);
  • app worker 退出:启动期间失败 → master 以 code 1 退出;运行期间崩溃 → 生产环境由cfork自动 refork(src/utils/mode/impl/process/app.ts);
  • 调试模式:若以--inspect/--debug启动且 worker 被SIGKILL(常见于调试器强制终止),master 判定为调试器所为,10ms 后整体退出(onAppExitthis.options.isDebug && signal === 'SIGKILL'分支);
  • 启动超时:app worker 监听startTimeout事件,超时以 code 1 退出(src/app_worker.ts)。

7.3 优雅关闭

收到SIGINT/SIGQUIT/SIGTERM后,master 依次 kill app workers 与 agent worker,并保证agent 在 master 之前退出(kill 顺序见 src/master.ts 注释 "make sure Agent Worker exit before master exit")。2.2.1 曾修复关闭时 worker 收不到SIGTERM的问题,1.9.1 也曾加入 100ms 延迟确保信号送达,这些都是为了优雅关闭的可靠性所做的修补。

八、版本演进中的关键修复清单(从 CHANGELOG 提炼)

版本类型内容对应源码痕迹
4.0.0+破坏性移除 Node < 22.18.0,仅支持 egg@4package.jsonengines
3.0.1修复require 支持 paths(模块注入可解析)parseOptions/importModulepaths: [options.baseDir]
3.0.0特性tshy 同时产出 CJS 与 ESM,TypeScript 化package.jsonexports指向src/index.ts
2.4.0特性升级 detect-port v2detectPorts()动态探测端口
2.2.0特性新增debugPort,可同时监听 http 与 httpssrc/app_worker.ts 双 server
2.1.0特性options.env自定义运行环境isProduction判定优先读options.env
2.0.0特性worker_threads 支持src/utils/mode/impl/worker_threads/
1.27.0特性agent/app 启动错误格式化为FrameworkErroragent_workerstartErrorHandler
1.26.0特性启动时打印process.env.HOSTgetAddress()中读取process.env.HOST
1.25.0特性支持config.cluster.httpsapp worker 合并clusterConfig.httpsoptions.https
1.24.0特性windowsHide支持cfork({ windowsHide })
1.23.0特性保存 pid 文件masterpidFile写入/清理
1.22.0特性退出时 kill 全部子进程_doClose的关闭链
1.21.0特性启动失败时优雅退出各 workerstart error, exiting with code:1分支
1.13.0特性新增 worker manager 并检查 worker/agent 状态WorkerManager.startCheck()
1.12.3修复EADDRINUSE时 master 应退出app workerserver.once('error')exitProcess()
1.12.2修复禁用 worker 自动 reforkdisableRefork/refork: this.isProduction
1.6.4修复master 被SIGKILL时 agent 也应退出agent 的disconnect事件处理

这些修复条目并非孤立补丁,而是逐步沉淀出当前 master / worker 状态机(state: 'listening'disableReforkisAllWorkerStarted等字段)的演进痕迹,阅读 src/utils/worker_manager.ts 与 src/master.ts 可以看到它们的最终形态。

九、从零启动一个生产集群

综合上文,一个接近生产配置的启动脚本如下:

const path = require('node:path'); const { startCluster } = require('@eggjs/cluster'); startCluster({ baseDir: path.join(__dirname, 'app'), // framework: 'egg', // 或指向本地框架的绝对路径 workers: process.env.WORKERS ? Number(process.env.WORKERS) : require('node:os').cpus().length, port: 7001, sticky: false, // 有本地会话依赖时开启 pidFile: path.join(__dirname, 'run/master.pid'), env: process.env.EGG_SERVER_ENV, // startMode: 'worker_threads', // 需要更轻量的启动时启用,并配合 ports // ports: [7001, 7002, 7003], // require: ['tsx/register'], // 注入需要的模块 }).then(() => { console.log('egg cluster started'); });

部署注意事项:

  1. Node 版本:4.x 要求 Node >= 22.18.0(package.json);
  2. HTTPS:证书必须为文件绝对路径,https: { key, cert, ca }优先;
  3. 关闭超时:通过EGG_MASTER_CLOSE_TIMEOUT/EGG_APP_CLOSE_TIMEOUT/EGG_AGENT_CLOSE_TIMEOUT调节优雅退出窗口,默认 5000ms,master 整体兜底 15s;
  4. 健康检查:生产环境会自动每 10s 巡检,连续 3 次异常即整体退出,配合进程守护工具(如 systemd / docker restart)即可实现故障自愈。

十、总结

@eggjs/cluster从 2016 年的egg-cluster一路演进至今,其 CHANGELOG 本身就是一部"Node.js 集群进程管理最佳实践"的浓缩史:从 0.x 的master 不加载配置、到 1.x 的 sticky/https/pidFile/健康检查、再到 2.x 的 worker_threads 轻量启动与 3.x 的 TypeScript + 双模块格式、最终在 4.x 收敛到 Node 22+ 与 egg@4 单一目标。对使用者而言,掌握本文的 Options 语义、两种 startMode 的取舍、sticky 的适用场景以及 master 的容错与优雅退出策略,就足以在生产环境中稳定地编排 Egg 的多进程应用。

如需深入源码,推荐按以下路径阅读:

  • 启动入口与整体流程:src/index.ts、src/master.ts
  • 选项解析与默认值:src/utils/options.ts
  • worker 进程实体:src/app_worker.ts、src/agent_worker.ts
  • 消息通信与健康检查:src/utils/messenger.ts、src/utils/worker_manager.ts
  • 两种启动模式的实现对照:src/utils/mode/impl/process、src/utils/mode/impl/worker_threads
  • 测试样例:test/worker_threads.test.ts、test/options.test.ts
  • 后端
  • Web框架

【免费下载链接】egg

🥚🥚🥚🥚 Born to build better enterprise frameworks and apps with Node.js & Koa. https://307.run/eggcode

项目地址:https://gitcode.com/gh_mirrors/eg/egg
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询