fix: avoid duplicate container stdout logging

This commit is contained in:
whyour
2026-08-16 17:03:15 +08:00
parent 276dfc2382
commit 02e02491c4
3 changed files with 36 additions and 5 deletions
@@ -68,6 +68,37 @@
- P0-4 已完成:系统设置提供运行实例/任务统计保留天数,默认 0(禁用);清理前展示记录数与依赖缓存字节数,清理接口要求明确确认。依赖缓存和 SQLite `VACUUM` 均为手动勾选,不在启动或升级时自动执行。
- P0-5 已完成:Debian 镜像构建后清理 npm cache 和 apt lists。
## 优化后实测(提交 `276dfc23`,同机 Node 24 / Docker Desktop arm64
### 指标内存
| 压力 | 基线 | 优化后 | 结果 |
| --- | ---: | ---: | --- |
| 50,000 条保留样本 | 50,000 | 1,000 | 减少 98% |
| 50,000 条 GC 后堆增量 | 8.76 MiB | 0.16 MiB | 减少约 98.2% |
| 500,000 条 GC 后堆增量 | 88.33 MiB | 0.11 MiB | 减少约 99.9% |
| 500,000 条写入耗时(5 轮平均) | 149.6 ms | 74.4 ms | 约快 50% |
| 500,000 条统计查询 | `RangeError` 栈溢出 | 正常 | 消除故障 |
真实容器用并发 50 发出 50,000 次 `/api/health`:两者空闲均约 161 MiB;基线负载后即时增加约 108.5 MiB、稳定后仍增加约 17.6 MiB,优化版分别约 81.0 MiB 和 8.4 MiB,改善约 25% 和 52%。三轮 10,000 请求耗时基线平均 3.86 秒、优化版 4.05 秒,差约 4.8% 且落在轮次波动内,因此不宣称 HTTP 吞吐提升。
### 大日志读取
对 100 MiB 日志做三轮读取:
| 指标 | 基线整文件读取 | 优化后 tail 读取 | 结果 |
| --- | ---: | ---: | --- |
| 返回数据 | 100 MiB | 256 KiB | 减少 99.75%400 倍) |
| 平均耗时 | 56.1 ms | 1.34 ms | 约快 41.9 倍 |
| 堆峰值增量 | 100.11 MiB | 0.317 MiB | 减少约 99.7% |
| RSS 峰值增量 | 115.18 MiB | 0.349 MiB | 减少约 99.7% |
### 日志写入、SQLite 与镜像
- PM2 实际容器测试中,基线为约 7,288 行输出额外生成了 888,026 bytes 的 `qinglong-out.log`;优化后 PM2 日志目录没有文件,Docker stdout 每行只出现一次。测试同时发现 `/proc/1/fd/1` 会造成 Docker stdout 重复,最终改为容器中 `out_file/error_file=/dev/null`,由 `pm2-runtime` 单独转发 stdout。
- 合成 100,000 条运行实例和 100,000 条任务统计的 SQLite 数据库为 24,481,792 bytes。删除 90% 历史记录耗时 0.26 秒,文件大小不会立刻变化;可选 `VACUUM` 耗时 0.05 秒,文件降至 2,465,792 bytes,回收约 89.9%。
- 当前 Debian 镜像 `/var/lib/apt/lists` 实测 19,527,172 bytes18.62 MiB),新 Dockerfile 会在同一个安装层删除。Dockerfile `--check` 无警告;完整镜像构建在 Debian 包升级网络等待阶段停止,因此不虚报最终镜像总大小。
### P1:轻量架构调整
1. 主进程只加载 cluster/bootstrap 必需模块,把 Express、监控、HTTP 中间件和业务容器全部延迟到 HTTP worker。目标是先回收主进程约 3040 MiB private memory。
+2 -2
View File
@@ -10,8 +10,8 @@ module.exports = {
listen_timeout: 5000,
source_map_support: true,
time: !isContainer,
out_file: isContainer ? '/proc/1/fd/1' : undefined,
error_file: isContainer ? '/proc/1/fd/2' : undefined,
out_file: isContainer ? '/dev/null' : undefined,
error_file: isContainer ? '/dev/null' : undefined,
script: 'static/build/app.js',
env: {
http_proxy: '',
+3 -3
View File
@@ -13,10 +13,10 @@ function loadConfig(containerValue) {
return require(configPath).apps[0];
}
test('container logging goes to the container standard streams', () => {
test('container logging relies on pm2-runtime stdout without persistent copies', () => {
const app = loadConfig('true');
assert.equal(app.out_file, '/proc/1/fd/1');
assert.equal(app.error_file, '/proc/1/fd/2');
assert.equal(app.out_file, '/dev/null');
assert.equal(app.error_file, '/dev/null');
assert.equal(app.time, false);
});