diff --git a/app.js b/app.js index 6e98e05..6089a86 100644 --- a/app.js +++ b/app.js @@ -67,8 +67,8 @@ class AppBootHook { // // 🔴 2026-09-21 修正(并发压测结论):原实现用 `os.cpus().length` 估 worker 数, // 这是**误估** —— 真实 worker 数由 egg-scripts 的 --workers / EGG_WORKERS 决定 - // (生产显式设为 4,见 config.default.js「集群 worker 数」段),与 CPU 核数无必然关系。 - // 误估会在 8 核机上把「4×20=80(安全)」误报成「8×20=160(越限)」, + // (生产显式设为 2,见 config.default.js「集群 worker 数」段),与 CPU 核数无必然关系。 + // 误估会在 8 核机上把「2×20=40(安全)」误报成「8×20=160(越限)」, // 既制造假告警、也会掩盖真实的越限,使这条断言失去作用。 // 改为优先读 EGG_WORKERS(worker 进程继承 master 的环境变量),回退才用 CPU 数, // 并在日志中显式标注估算依据,便于排障时判断该行可信度。 @@ -81,10 +81,10 @@ class AppBootHook { const usingExplicit = Number.isFinite(explicitWorkers) && explicitWorkers > 0; // 🔴 2026-09-21 二次修正:egg-bin dev / test 会**显式传** `workers: 1` // (node_modules/egg-bin/scripts/start-cluster.js),此时即便环境里存在 - // EGG_WORKERS(本地 dev 也会加载 deploy/.env.production,其中写着 4), + // EGG_WORKERS(本地 dev 也会加载 deploy/.env.production,其中写着 2), // 也不是真实 worker 数 —— 真实值是 1。故必须先按 app.config.env 判定 // 单 worker 的运行形态,否则本地仍会打出一条假数字日志(实测:dev 下 - // 真 worker=1 却报 4×20=80)。 + // 真 worker=1 却报 2×20=40)。 const envName = (this.app.config && this.app.config.env) || ''; const singleWorkerEnv = envName === 'local' || envName === 'unittest'; let workers; diff --git a/config/config.default.js b/config/config.default.js index 47b26df..2533867 100644 --- a/config/config.default.js +++ b/config/config.default.js @@ -186,15 +186,16 @@ module.exports = (appInfo) => { // 🔴 集群 worker 数(2026-09-21 并发压测结论,必须显式设定) // ----------------------------------------------------------------- // 背景:`egg-scripts start` 未传 --workers 时,egg-cluster 会按 os.cpus().length - // 起同等数量的 app worker —— 本机/多数服务器为 8 核 → 8 个 worker。 + // 起同等数量的 app worker —— 核数越多起得越多(开发机 8 核 → 8 个 worker)。 // 而每个 worker 的连接池上限是 DB_POOL_MAX(默认 20),8 × 20 = 160 // **已经超过** MySQL max_connections(151)。高峰期会出现 connection acquire // timeout,在考生侧表现为「交卷失败」,且服务端不报业务错,极难定位。 // // 落点:worker 数由环境变量 EGG_WORKERS 控制(egg-scripts start 把它作为 // --workers 的默认值,见 node_modules/egg-scripts/lib/cmd/start.js:31)。 - // 生产值写在 deploy/.env.production(EGG_WORKERS=4),并由 start-backend.sh - // 显式传参作双保险。4 × 20 = 80 ≤ 151,为管理后台/统计等其余连接留足余量。 + // 生产值写在 deploy/.env.production(EGG_WORKERS=2,与服务器 2 核匹配; + // 2026-09-29 R40 由 4 校正为 2),并由 start-backend.sh 显式传参作双保险。 + // 2 × 20 = 40 ≤ 151,为管理后台/统计等其余连接留足余量。 // // ⚠️ 不要试图在此处写 config.cluster.workers —— egg-cluster 只读 // startCluster(options.workers)(options.js:24 → master.js:267), @@ -247,8 +248,8 @@ module.exports = (appInfo) => { // 🔴 压测调优(2026-08-11):单 worker 默认 10 连接,在「无带宽限制 + 极端并发」 // 下分片合并/写入会短时占用大量连接。提高到 20(可用 DB_POOL_MAX 覆盖), // 并放宽 acquire 到 45s,避免瞬时峰值触发 "connection acquire timeout"。 - // 注意:这是「每 worker」上限;worker 数由 EGG_WORKERS 显式控制(生产 = 4, - // 见本文件上方「集群 worker 数」段),总连接数 = worker 数 × max = 4 × 20 = 80, + // 注意:这是「每 worker」上限;worker 数由 EGG_WORKERS 显式控制(生产 = 2, + // 见本文件上方「集群 worker 数」段),总连接数 = worker 数 × max = 2 × 20 = 40, // 须 ≤ MySQL max_connections(默认 151)。启动期由 app.js didReady 自举断言 // 按**实际 worker 数**复核(不再用 os.cpus() 误估),超限只 WARN 不阻断。 max: parseInt(process.env.DB_POOL_MAX, 10) || 20,