修复主站通道引用被误清空导致任务状态永久卡住

问题:主站重连期间节点先后认证多条通道,引用被最后一条覆盖;那条通道断开后
引用被清空,而更早建立、仍然可用的通道继续发送可用性探测。节点因引用为空不再
上报任何任务状态,主站又能在旧通道上收到探活响应,双方都判定连接正常。于是
未完成任务永久停在「已提交」,只有人工触发重连或重启主站才会恢复。

现场证据:gid 4203383 于 19:31 创建、19:32 归档完成,节点此后每 5 秒扫到
status=4,却再未打印过「任务状态发送完成」;主站侧 17:23 之后一条「下载进度」
都没有。20:28/20:29 两次收到任务下发时节点判定 server 为空而静默丢弃。

修复:

- 新增 PrimaryChannelTracker 统一管理主站通道引用,并保留全部已认证通道。
  首选通道失效时立即回退到其它仍可用的已认证通道;引用被清空时,已认证通道上
  的下一条消息即可恢复它。认领口径严格等于主站实际会发的消息类型(身份/任务下发/
  删除/探活/订阅快照),备机身份与节点自身出站类型都不算证据。
- 新增 RekickPolicy:有待上报任务却无可用通道时,以 30 秒为限流间隔主动重新
  唤起主站,不再干等最长半小时的探测周期。阻塞式探测投递到独立线程,避免占用
  5 秒调度线程拖住下载扫描与压缩。
- 通道不可用时的告警与重连判断放在 downloadCheck 之前,但不提前返回,
  本地下载与压缩必须继续推进;否则正在下载、进度无变化的任务会走「无需上报」
  的提前返回路径,通道失联后既不告警也不重连,正是历史事故的成因。

验证:新增 13 项测试(通道引用自愈 6、事故场景复现 3、重连限流 4),
节点侧合计 28 项全绿。已部署到存储节点并与主站重新握手,双向报文正常,
未完成任务数归零。
This commit is contained in:
root
2026-09-21 22:17:14 +08:00
parent 6a97e403ba
commit 77f817d402
7 changed files with 503 additions and 22 deletions
+15 -2
View File
@@ -45,13 +45,15 @@ storageNode/
│ │ │ └── ResponseMessage.java # 通用响应(type=0)
│ │ └── Service/
│ │ ├── DeleteService.java # 删除画廊目录
│ │ └── DownloadCheckService.java # 下载监控与压缩服务
│ │ ├── DownloadCheckService.java # 下载监控与压缩服务
│ │ ├── PrimaryChannelTracker.java # 主站通道引用登记与自愈
│ │ └── RekickPolicy.java # 主动重新唤起主站的限流判定
│ └── resources/
│ ├── config.properties # DouNai 订阅地址配置
│ ├── simplelogger.properties # SLF4J 日志配置(输出到 run.out)
│ └── reflect-config.json # GraalVM 反射配置(Jackson 序列化)
└── test/
└── java/ # 订阅快照与下载/压缩恢复测试
└── java/ # 订阅快照、下载/压缩恢复与主站通道自愈测试
```
---
@@ -130,6 +132,17 @@ storageNode/
- 按名称删除画廊目录
- 失败时返回 `ErrorCode.IO_ERROR` 或 `ErrorCode.FILE_NOT_FOUND`
### PrimaryChannelTracker
- 登记「哪条通道是主站」,供任务状态上报与心跳使用
- 只有主站会发的消息类型(身份/任务下发/探活/订阅快照)才可用于认领引用;
备机的身份消息与节点自己的出站类型都不算证据
- 首选通道断开时立即回退到其它仍可用的已认证通道;引用被清空时,
已认证通道上的下一条消息即可恢复它,避免任务状态静默停止上报
### RekickPolicy
- 判定「有待上报任务、却无可用通道」时是否应主动重新唤起主站
- 以 30 秒为最小间隔限流,既能快速自愈,又不会在主站确实离线时形成重连风暴
---
## 配置说明