From 7758b4961bf1d14da472752240da067e79ce63f2 Mon Sep 17 00:00:00 2001 From: KodouWZF <3092554823@qq.com> Date: Tue, 3 Nov 2026 22:43:59 +0800 Subject: [PATCH] =?UTF-8?q?docs(=E4=BC=A0=E8=BE=93):=20=E8=A1=A5=205.2=20?= =?UTF-8?q?=E5=AE=9E=E6=96=BD=E6=83=85=E5=86=B5=E4=B8=8E=E5=AE=9E=E6=B5=8B?= =?UTF-8?q?=E8=AE=B0=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 三个坏包/坏标记样本的实测结果、每轮结构化日志原文、单轮耗时 17-41ms。 明确记录认领改名与并发解包未实施及理由(单实例 + Spring cron 不重叠, 已实测触发间隔精确 5.00 秒),以及改用 mtime 静止阈值区分「还在搬」与「坏包」。 同步修正 5.2 的实施状态、第 8 节配置项(去掉 concurrency、 max-unpack-size-mb 默认值由建议 1024 改为已实施 512、新增 stale-after-seconds)、 第 9 节实施顺序表的收益口径(31 倍理论值改为实测 8.2 倍/17.6 倍)。 --- backend/docs/传输链路性能优化方案.md | 82 ++++++++++++++++++++++------ 1 file changed, 64 insertions(+), 18 deletions(-) diff --git a/backend/docs/传输链路性能优化方案.md b/backend/docs/传输链路性能优化方案.md index 1798ac0..b6106b9 100644 --- a/backend/docs/传输链路性能优化方案.md +++ b/backend/docs/传输链路性能优化方案.md @@ -1,8 +1,8 @@ # 跨网传输链路性能优化方案 -> 版本:v1.1(2026-11-03) +> 版本:v1.2(2026-11-03) > 适用后端:s-file(alita-bjy-uygur) -> 状态:一期(5.1/5.3/5.4/5.5)已实施并本地验证 → 待现场验证;二期(5.2)待实施 +> 状态:5.1/5.2/5.3/5.4/5.5 已实施并本地验证 → 待现场验证;5.6 视现场文件大小分布决定 > 实施与实测记录见第 10 节 > 关联代码:`TransferUserFileServiceImpl`(发送/组包)、`TransferFileMonitorServiceImpl`(接收/解包)、`TransferFileMonitorJob`(调度)、`FileServiceImpl` / `LocalFileStorage`(物理文件读写) @@ -182,6 +182,14 @@ docker logs --since 30m 2>&1 | grep -E "开始解包文件|文件解 ### 5.2 单轮处理上限:从「个数」改为「时间预算」 +> **实施情况(2026-11-03,提交 `06852c5`)**:下面 (1) 时间预算、(2) 失败让位、(4) 积压可观测、 +> `.finish` 优先 **均已实施并实测通过**(见 §10.5)。 +> **(3) 认领改名与并发解包未实施**——两侧都是单实例部署,Spring 的 cron 任务要等上一轮跑完才排下一轮 +> (已实测触发间隔精确 5.00 秒),不存在同实例重叠;认领机制只会引入 `kill -9` 后遗留 `.processing` +> 这类新故障模式,换不到任何吞吐。**若将来改为多实例共享同一摆渡目录,必须补认领与 Redisson 互斥。** +> (2) 的实现方式也与原设计不同:不用改名认领,而是用 mtime 是否静止超过 `stale-after-seconds` +> 来区分「光闸还在搬」与「包本身坏了」,只对后者标 `.error`。 + **现状**:`TransferFileMonitorServiceImpl:87-99` 与 `:109-120` ```java @@ -386,10 +394,11 @@ CREATE INDEX idx_file_record_md5_code ON xs_file_record(md5_code); | 配置项 | 类型 | 默认值 | 现状值 | 说明 | |---|---|---|---|---| | `FILE_TRANSFER_MONITOR_CRON` | env | `0/5 * * * * ?` | `0 */1 * * * ?` | 监听周期,5.1 | -| `file.transfer.monitor.round-budget-seconds` | yml/env | 4 | — | 单轮时间预算,5.2 | -| `file.transfer.monitor.round-max-files` | yml/env | 500 | 硬编码 10 | 单轮兜底个数上限,5.2 | -| `file.transfer.monitor.concurrency` | yml/env | 2 | 1(串行) | 并发解包线程数,5.2 | -| `file.transfer.monitor.max-unpack-size-mb` | yml/env | 建议 1024 | 无 | 单文件大小上限,超限标 `.error`,5.3 注意事项 | +| `file.transfer.monitor.round-budget-seconds` | yml | 4 | — | 单轮时间预算,5.2【已实施】 | +| `file.transfer.monitor.round-max-files` | yml | 500 | 硬编码 10 | 单轮兜底个数上限,5.2【已实施】 | +| `file.transfer.monitor.max-unpack-size-mb` | yml | 512 | 无 | 包内单文件大小上限,超限标 `.error`,5.3 注意事项【已实施】 | +| `file.transfer.monitor.stale-after-seconds` | yml | 300 | 无 | 静止超过该秒数仍校验不过即判坏包,5.2【已实施】 | +| ~~`file.transfer.monitor.concurrency`~~ | — | — | — | 未实施,理由见 5.2 实施情况 | 涉及文件:`deploy/offline/config/application.yml`、`deploy/offline/.env.iw`、`deploy/offline/.env.ow`、`deploy/config/application-iw.yml`、`deploy/config/application-ow.yml`、`deploy/offline/docker-compose.yml`、`deploy/docker-compose.yml`。 @@ -397,24 +406,24 @@ CREATE INDEX idx_file_record_md5_code ON xs_file_record(md5_code); ## 9. 实施顺序与风险 -| 顺序 | 项 | 改动量 | 风险 | 收益 | -|---|---|---|---|---| -| 1 | 5.3 缓冲区 64 KB | 5 处,各 1 行 | 极低 | 大文件组包 31× | -| 2 | 5.1 监听周期 5 秒 | 配置 | 低 | 端到端省约 55 秒 | -| 3 | 5.5 md5 索引 | 4 个 SQL 文件 | 极低 | 多文件解包明显加速 | -| 4 | 5.4 level 0 | 小 | 低 | 已压缩格式再省 CPU | -| 5 | 5.2 时间预算 + 认领 + 并发 | 中 | 中(涉及并发与文件改名状态机,需完整测试) | 高峰期吞吐一个数量级 | -| 6 | 5.6 异步组包 | 大 | 中 | 仅超大文件场景需要,二期评估 | +| 顺序 | 项 | 改动量 | 风险 | 收益(实测口径见 §10) | 状态 | +|---|---|---|---|---|---| +| 1 | 5.3 缓冲区 64 KB | 5 处,各 1 行 | 极低 | 组包 8.2× | 已实施 `4114c1f` | +| 2 | 5.1 监听周期 5 秒 | 配置 | 低 | 端到端省约 55 秒 | 已实施 `06fb35b` | +| 3 | 5.5 md5 索引 | 4 个 SQL 文件 | 极低 | 多文件解包明显加速 | 已实施 `c5d19f4` | +| 4 | 5.4 level 0 | 小 | 低 | 叠加后合计 17.6×,包体 +5.1% | 已实施 `456b1ea` | +| 5 | 5.2 时间预算 + 坏包隔离 | 中 | 中 | 高峰期吞吐一个数量级 | 已实施 `06852c5`(认领/并发未做,见 5.2) | +| 6 | 5.6 异步组包 | 大 | 中 | 仅超大文件场景需要 | 待现场文件大小分布决定 | -建议 1–4 作为一期(都是低风险、可独立验证的改动),5 作为二期,6 视现场文件大小分布决定。 +1–5 已全部实施。**每一项单独提交**,现场出问题时可按提交号逐项回退定位。 **每一项都必须单独验证并记录数字**,不要合并提交,否则现场出问题时无法定位是哪一项引起的。 --- -## 10. 一期实施与验证记录(2026-11-03,本地环境) +## 10. 实施与验证记录(2026-11-03,本地环境) -一期 4 项已实施,每项独立提交: +一期 4 项 + 二期 5.2 已实施,每项独立提交: | 项 | 提交 | 内容 | |---|---|---| @@ -422,6 +431,7 @@ CREATE INDEX idx_file_record_md5_code ON xs_file_record(md5_code); | 5.1 | `06fb35b` | 监听周期 `0 */1 * * * ?` → `0/5 * * * * ?`,Java 默认值 + 4 个部署文件 | | 5.5 | `c5d19f4` | `idx_file_record_md5_code`,init 双库 + upgrade 脚本 + 交付包副本 | | 5.4 | `456b1ea` | 按后缀对已压缩格式用 `Deflater.NO_COMPRESSION` | +| 5.2 | `06852c5` | 单轮上限改 4 秒时间预算、坏包 `.error` 隔离、`.finish` 优先、每轮结构化日志、单文件大小上限 | 编译:JDK 8 + `mvn package -Dmaven.test.skip=true`,BUILD SUCCESS。 (`-DskipTests` 不行:surefire 2.22.2 的传递依赖 `plexus-utils:1.1` 本地仓库没有,离线模式解析失败,需联网补一次。) @@ -480,7 +490,43 @@ CREATE INDEX idx_file_record_md5_code ON xs_file_record(md5_code); **结论:预测方向正确、量级偏高。** 现场汇报口径建议用实测的 **8 倍(仅改缓冲)/ 17 倍(含 level 0)**,不要用 31 倍。第 3.4 节的表格在现场按 4.2 的系数重算后再对外给数。 -### 10.4 尚未验证的部分 +### 10.4 5.2 时间预算与坏包隔离 — 已实测通过 + +在内网侧接收目录(`gate/ow2iw`)投放三个测试样本,从 `sfile-iw-app` 日志与目录状态验证: + +| 样本 | 构造 | 预期 | 实测 | +|---|---|---|---| +| `1_900001.zip` | 5000 字节随机内容,mtime 为当前 | 校验不过但未静止够久 → 保留待下一轮 | 每轮计入「未就绪=1」,文件保留 ✅ | +| `1_900002.zip` | 同上,mtime 回拨 10 分钟 | 静止超过 300 秒 → 判坏包改名 `.error` | 首轮即改名,reason=「校验持续不通过且文件已静止超过 300 秒,判定为坏包」✅ | +| `bogus.zip.finish` | 文件名无法解析出 `userId_folderId` | 永久性失败 → 改名 `.error` 停止重试 | 改名 `bogus.zip.finish.error`,后续轮次「完成标记=0/0」不再出现 ✅ | + +每轮结构化日志(真实输出): + +``` +22:37:25.046 传输监听本轮结束: 完成标记=0/0, 传输包处理=2(成功=0,坏包=1,未就绪=1), 耗时=41ms, 预算=4000ms, 目录剩余待处理=0, 已搁置坏包=0 +22:37:30.032 传输监听本轮结束: 完成标记=0/1, 传输包处理=1(成功=0,坏包=0,未就绪=1), 耗时=27ms, 预算=4000ms, 目录剩余待处理=0, 已搁置坏包=1 +22:37:35.029 传输监听本轮结束: 完成标记=0/0, 传输包处理=1(成功=0,坏包=0,未就绪=1), 耗时=23ms, 预算=4000ms, 目录剩余待处理=0, 已搁置坏包=2 +``` + +- 「已搁置坏包」随 `.error` 文件累积单调上升(0 → 1 → 2),运维可直接从日志看出目录里堆了多少坏包 +- 空轮 + 1 个待处理包的单轮耗时 **17–41 ms**,远低于 4000 ms 预算 +- `.finish` 优先于 `.zip` 处理,顺序符合设计 + +#### 顺带修掉一个 5.1 引入的日志副作用 + +`verifyFileMD5` 打不开 zip 时原来记 **ERROR 带完整堆栈**,而「光闸还没搬完」本来就是这个状态。 +周期改成 5 秒后,每个在途文件会被刷 **60 条 ERROR/5 分钟**(原来只有 5 条)。 +已降为 debug;真正的坏包由改名 `.error` 时记一条带 reason 的 ERROR,信息量更高且不重复。 + +实测确认:投放 1 个在途坏包后连续观察 70 秒(14 轮),`文件大小校验失败` 的 ERROR 条数 **0**,全容器 ERROR 条数 **0**。 + +#### 未验证的部分 + +时间预算的「超预算即结束本轮」分支未在本地触发——需要单轮塞进足够多的大包才能耗尽 4 秒, +本地没有这个数据量。现场验收时按 §7.2 的方法:一次性提交 30 个传输,观察日志中每轮「传输包处理=N」 +与「目录剩余待处理」是否单调下降。 + +### 10.5 尚未验证的部分 - [ ] **应用内真实组包**:需要登录后发送一次同一个文件夹,从 `ControllerLogAspect` 的 `useTime` 与「传输文件打包完成」日志确认应用侧数字(基准只覆盖 I/O 环节,未含 SQL 与元数据序列化) - [ ] **解包侧**:64KB `ZipInputStream` 的改动只做了编译验证,未跑通一次真实解包(同样受登录限制)