~/Downloads/download-server.js)| # | 位置 | 问题 | 后果 |
|---|---|---|---|
| 1 | handleUploadComplete 合并落盘 | fs.readFileSync(64MB) + fs.writeSync 逐块全同步合并 | 合并 6.9GB 文件 = 6.9GB 同步读写,事件循环被独占十几秒;多个文件同时 complete 叠加成数分钟完全无响应 |
| 2 | 合并后的 fs.rmSync(dir,{recursive:true}) | 同步递归删除含 GB 级 .part 的会话目录 | 每个文件合并后都要阻塞一次,量大时持续卡顿 |
| 3 | 无并发闸门 | 45 个文件同时上传,合并/删除/写盘互相叠加 | 峰值压力不可控,磁盘 IO 与内存被抢 |
| 4 | server.requestTimeout 默认 300s | 合并排队时响应可能超 5 分钟 | 请求被 Node 误杀(408) |
| 5 | keepAliveTimeout 默认 5s | 几十个大文件反复重建连接 | 连接churn,加重握手与调度负担 |
| 措施 | 实现 | 效果 |
|---|---|---|
| 流式异步合并 | createReadStream → createWriteStream(pipe(..., {end:false}))替代 readFileSync/writeSync | 事件循环不再被合并独占 |
| 合并闸门 | 同时最多 2 个合并(DL_MERGE_CONCURRENCY,默认 2),其余排队 | 消除多文件并发合并风暴 |
| 异步清会话 | fs.promises.rm 替代 fs.rmSync | 删除 GB 级目录不阻塞 |
| 存活探针 | GET /healthz:不触碰磁盘,返回 pid/uptime/rss/mergeActive/mergeQueued | 供巡检与人工判活 |
| 探活巡检 | dl-healthcheck.timer 每 30s 请求 /healthz,连续 2 次失败 → 自动重启 | 卡死约 60 秒内自愈(进程活着但卡死时唯一有效手段) |
| 事件循环滞后监控 | 单次阻塞 >5s 记 [warn] 事件循环阻塞 Xms | 再发生卡死可追溯现场(DL_LAG_WARN_MS 可调) |
| 顶层异常日志 | uncaughtException(记日志后 exit(1) 交 systemd 拉起)/unhandledRejection(记日志) | 不再出现「日志只剩启动行、无法定位」 |
| 超时放宽 | requestTimeout=30min、headersTimeout=60s、keepAliveTimeout=30s | 合并排队不被误杀 + 连接复用 |
| 前端并发闸门 | 同时最多 3 个文件在传(UP_PARALLEL),其余显示「排队中…」 | 服务端压力可控,单个文件更快传完 |
原计划用 Type=notify + WatchdogSec(原生看门狗,能精确发现事件循环卡死)。实测 Node 的 dgram 只支持 udp4/udp6,不支持 unix_dgram,dgram.createSocket('unix_dgram') 直接抛 ERR_SOCKET_BAD_TYPE;异常被 try/catch 吞掉后 READY=1 永不送达,systemd 报 start operation timed out 启动失败。 → 改为外部探活巡检:效果等价(事件循环卡死时 /healthz 必然超时)、零额外依赖,故最终采用 Type=simple + 巡检定时器双层兜底(systemd Restart=always 负责崩溃重启,巡检负责卡死重启)。
/healthz 探测 16 次:最大延迟 3ms、平均 1.4ms(旧实现会阻塞整个合并时长);mergeActive 峰值 = 2、且出现排队 → 闸门生效;kill -STOP 主进程模拟事件循环永久卡死
/healthz → HTTP 000(超时,符合预期)systemctl status dl-server # 服务状态
systemctl status dl-healthcheck.timer # 巡检定时器
curl -s --noproxy '*' http://127.0.0.1:8899/healthz # 健康检查(含合并队列)
tail -f ~/Downloads/dl-server.log # 日志(现为 append;关注 [warn] 事件循环阻塞 / [fatal])
可调参数(写在 /etc/systemd/system/dl-server.service 的 Environment=):
DL_MERGE_CONCURRENCY(默认 2):同时合并数DL_LAG_WARN_MS(默认 5000):事件循环阻塞告警阈值UP_PARALLEL(默认 3):同传文件数上限,位于 download-server.js 的 UPLOAD_JS~/Downloads/download-server.js(streamMerge/acquireMergeSlot/completeUpload//healthz/watchLoopLag/前端 schedule+pump)~/Downloads/dl-healthcheck.sh、dl-healthcheck.service、dl-healthcheck.timer~/Downloads/dl-server.service(→ /etc/systemd/system/)/tmp/up-test.mjs(功能 23 项)、/tmp/concurrency-test.mjs(并发压测)