Files
dsh_shenxian/dsh-server-docs/交接单/覆盖网络-序24-内容分发块级寻址.md
T
admin 09ce76f3af feat(overlay): 内容块级寻址 + 实例逐步拉起 + 骨干选路 + 组密钥加密(序24–㉛ 累积同步)
代码
- 内容分发块级寻址:新增 src/net/relay/content/{chunker,store,runtime,source,peer,crypto}.ts
- 组密钥(C 档)确定性加密:AES-256-GCM,块 id β′ = sha256(密文) 前 32 hex;双 epoch 过渡窗口
- 实例生命周期:三处 teardown() 不再杀实例(local/remote/leased-spawner);启动认领 + TCP 探活判孤儿
- 骨干选路:jitter 选路 + endpoint-target;relay client/server/wire/identity/directory/rendezvous/switcher 调整
- 工作台 src/web/server.ts、src/worker/relay-tunnel.ts 装配与候选链观测

脚本与测试
- scripts/overlay-{probe,keyring,jitter}.cjs 更新
- 探针新增 OBS-21(每连接候选数)/ OBS-22(teardown 静态守卫 + 认领面)/ OBS-23(组密钥加密)
- 新增 test/{orchestrator-teardown,orchestrator-rehydrate,overlay-content,overlay-jitter}.test.mjs;relay 两例更新

文档
- 新增交接单:覆盖网络-序24-内容分发块级寻址 / 序25-实例逐步拉起 / 序26-骨干稳定选路与加密
- INDEX.md、交接单/README.md、skills/dsh-auto-handoff-chain/SKILL.md 同步

验收(零回归,2026-09-18 08:0x 复核)
- npm test           201 tests / 200 pass / 0 fail / 1 skipped
- overlay-failover-drill --scene all --table   12 PASS / 0 SKIP / 0 FAIL
- overlay-probe --table                        23 PASS / 0 SKIP / 0 FAIL (rc=0)
2026-09-18 08:08:51 +08:00

11 KiB
Raw Blame History

交接单 · 内容分发(块级内容寻址 · 同网段 peer 优先)

  • 序号:覆盖网络线 序 ㉔ · 规划棒
  • 立单:2026-09-17 20:3x
  • 用户拍板:「B 内容分发(块级内容寻址)是否立项:做」(2026-09-17 20:2x)
  • 上游依据:覆盖网络_瓶颈落地方案_20260916.md §3(照抄"内容分发三件套"+验收判据)/覆盖网络_应用场景与待完善清单_20260916.md §五 第 7 步/覆盖网络_千台全场景推演_20260916.md
  • 状态:待执行(执行棒按本单 §4 顺序开工,⛔ 无须再出规划单)
  • ⚠️ 用户新增口径(见 §7):传输必须"连接稳定高效 + 数据可加密" ⇒ 本单的传输面按此定档

§0 摘要

把 10.8 GB/次的首屏包冷启动(瓶颈榜第 3 项,也是版本发布风暴的第 5/9 项成因)从「每台设备各自回源」改成「块级内容寻址 + 同网段 peer 优先」。一次投资同时治 #3 / #5 / #9 三项瓶颈(上游口径:三件套覆盖最大三个瓶颈,且都不需要改传输协议)。

核心判断(一句话):必须做"块级 + 内容寻址",⛔ 不做"包级"。 这是 BranchCache(块级)vs Peer Cache(包级)的分水岭 —— 包级必须完整下载完才能当 peer 源,版本一变全部 peer 源同时失效 ⇒ 客户端集体回源 = "版本一发就全量重拉"的风暴成因(本线已实证过一次)。

验收判据(单值可测):版本发布时回源字节数 ≈ 1 份 × 组数(而不是 1000 份)—— 局域网内多台设备只回源一次。


§1 目标(可判定"做完了没有")

# 判据 期望 怎么测
E1 回源放大比 ≈ 1 份 × 组数(同组 N 台设备只回源 1 份) 同组内 4 台本地多实例同时拉同一版本 ⇒ 统计源侧出向字节数
E2 只拿到一部分也能开始共享 下载 30% 即产生可服务的块 中断后另一台从该 peer 能取到已持有块
E3 版本更新只传变化块 变化 5% 的版本 ⇒ 传输量 ≤ 变化块量 × 组内台数 改一个文件重打包 ⇒ 对比传输字节数
E4 客户端校验哈希 篡改块 ⇒ 丢弃并回源,⛔ 不落盘 故意改一个块的字节 ⇒ 断言 hash mismatch 且回源
E5 分组隔离 跨组不互相穿透(⛔ 不出现 A 组从 B 组取块) 两组并存 ⇒ 断言跨组 0 命中
E6 内容源优先级可观测 每次取块点名来源档位(local / peer / edge / origin) 判别器计数(⛔ 不许只写日志)
E7 零回归 npm test 176/175/0/1、--scene all --table 12P/0S/0F、overlay-probe --table 16P/0F/0S 三件套跑同值

§2 只读前置(执行前必须先核实的 5 条;⛔ 不许靠推断)

# 要核实什么 命令 期望
P1 首屏包实际形态与大小 ls -l 目标产物 + du -sh 确认 10.8 GB/次 的口径(是"一份包"还是"N 个文件")⇒ 直接决定块大小
P2 当前回源路径(几台设备各自回源) 平台侧出向流量 + 实例启动日志 现状 = 每台各自回源(这是本单的 Before 基线)
P3 现有"同网段"判定手段 47 / 106 的内网地址段 当前只有 2 台真机(47 / 106,跨云不同网段)⇒ 同网段 peer 只能在"本机多实例"上验证(用户已拍板"本机内存大可以模拟多台")
P4 已加载走 304 的能力 平台代理层 上游已确认"平台已有此能力"(瓶颈落地方案 §3.6)⇒ ⛔ 不重做
P5 存储与内存预算 df -h / 空闲内存 块存储放哪、peer 缓存上限多少(⚠️ 参考 MEM_PER_HOST_MB 现测 0.06,块缓存要另算)

§3 范围

3.1 在册文件集(⚠️ 超出此集必须先停下报告 — R7)

面 文件 说明
新增 src/net/relay/content/chunker.ts 块级切分(固定块 + 内容哈希)
新增 src/net/relay/content/store.ts 内容寻址存储(哈希 → 块;含校验)
新增 src/net/relay/content/peer.ts 同网段 peer 发现与取块
新增 src/net/relay/content/source.ts 内容源优先级链
新增 test/overlay-content.test.mjs 单测(⛔ 不改 package.json 的 test 列表 ⇒ 需与既有 13 个测试文件一致的追加方式)
改动 src/net/relay/index.ts 仅导出
改动 src/web/server.ts 仅装配(⛔ 不动 presence / 端点翻译既有逻辑)
改动 scripts/overlay-probe.cjs 新增观测项 + 阈值键
改动 参数表_覆盖网络_20260917.md 新增 CONTENT_* 键

⛔ 不在本单范围:房间层、打洞实现、游戏服、改传输协议、改 switcher.ts 冷却语义、删 /status 兜底路径。

3.2 顺带治好的既有瓶颈(⛔ 但不扩范围)

瓶颈榜 项 本单如何覆盖
#3 首屏包冷启动 10.8 GB 主目标
#5 (同族:版本发布风暴) 块级 ⇒ 只传变化块
#9 版本碎片 与 #3 共用一套内容寻址机制(版本包走同一条链)

§4 步骤 S0–S7(每步自带一次可执行的验证)

⚠️ 每步做完立即验证,⛔ 不许"全部写完再验"。

步 做什么 验证(一次可执行) 回滚
S0 只读前置 P1–P5 + 基线采样(零改动) 输出 Before 回源字节数(= E1 的分母) 无(只读)
S1 块级切分 + 内容寻址存储(chunker.ts / store.ts) 对同一份内容切两次 ⇒ 块哈希序列完全一致;改 1 字节 ⇒ 只有 1 块变化 删新增文件
S2 客户端校验(E4) 篡改块 ⇒ 断言 hash mismatch + 丢弃 + 回源 同上
S3 内容源优先级链(source.ts)+ 判别器计数(E6) 夹具:四档各命中一次 ⇒ 计数逐档递增 同上
S4 同网段 peer 发现与取块(peer.ts)+ 分组(E5) 本机多实例(N ≥ 4):先让 A 拉完,再让 B 拉 ⇒ B 从 peer 取 关 peer 开关 ⇒ 回 S3 行为
S5 装配 + 观测(参数表 CONTENT_* + 探针新项) 探针出一项 PASS/FAIL(⛔ 阈值不许是脚本魔数) 还原装配
S6 真机验收:E1(回源 ≈ 1 份 × 组数)+ E2(部分即可共享)+ E3(只传变化块) 本机多实例 4 台同组同时拉同一版本 同 S4
S7 收口(部署属 lane 内,直接做)+ 零回归三件套(E7) 三件套跑同值 产物回滚点

§5 验收判据 E1–E7

见 §1。⚠️ E1 是唯一的主判据:断它一句话 —— "回源字节数 ≈ 1 份 × 组数"。


§6 回滚(两层,均秒级)

  1. 装配级(推荐):src/web/server.ts 的 content 装配块整段移除 → npm run build → scp lib/ → restart dshs ⇒ 回到"每台各自回源"的旧行为(新模块文件留着不加载 ⇒ 零副作用)。
  2. 产物级:scp 回滚点 /opt/dsh/backups/seq24-<ts>/ 的 lib/{web/server.js,net/relay/*.js} → systemctl restart dshs。 🔴 ⛔ 回滚路径里不许出现 RELAY_FAILOVER_COOLDOWN_MS=0(该值看似合法、实际自锁)。

§7 传输面定档(用户本棒新增口径:连接稳定高效 + 数据可加密)

用户原话(2026-09-17 20:2x):「按照连接稳定高效的方式 数据安全可加密传输」 ⇒ 本单的传输面按此定档,属已定项、⛔ 不再上抛:

档 我选什么(可推翻) 理由
传输载体 复用既有自研 relay 通道(via='relay' 回环落点 + wss 为主、443/TCP 兜底),⛔ 不新开公网端口 已端到端验收(序㉓ E2E 通过);"稳定"靠已有的失败自动切流 + 冷却语义 + 一跳豁免
传输加密 peer 取块走既有 wss(TLS);跨机块传输再加一层内容级校验(哈希) ⇒ 机密性 + 完整性双保 「数据安全可加密」= ① 传输加密(TLS 已在)② 内容完整性由哈希兜住(E4)⇒ 即使中间节点被控也改不了块
端到端加密 本阶段不做(⛔ 不引入第二套密钥体系) 会与"内容寻址 + 跨 peer 共享"直接冲突(端到端加密 ⇒ 每个接收者密文不同 ⇒ 无法按哈希共享块)⇒ 要加密就失去共享,属真取舍;本阶段取"共享优先 + TLS + 哈希校验",把 E2E 登记为待评估(见 §9)
稳定性指标 选路按 jitter 排序⛔ 不按 RTT(上游 §4 口径)+ 每连接保 2–3 条候选路径 「连接稳定高效」的机器判据 = 每连接 jitter 直方图(超阈值切路径)

§8 回报格式(执行会话必须回填)

### 8.x 序 ㉔ 执行棒回报(YYYY-MM-DD HH:MM–HH:MM)

#### ① 各步结果(S0–S7,逐条给"命令 + 原文输出 + 判定")

#### ② 主判据 E1:回源字节数 ≈ 1 份 × 组数
- 组内台数 = N;实测回源字节 = X;放大比 = X / 一份大小 = ?

#### ③ 先红后绿(原文级)
- (去掉某一档 ⇒ 哪条用例变红;改回 ⇒ 转绿)

#### ④ 零回归三件套(均带 `--table`)
| 项 | 结果 | 基线 |

#### ⑤ 边界自证
⛔ 未改任何生产值 / ⛔ 未新增公网监听口 / ⛔ 未改 nft·nginx / 🔴 `COOLDOWN_MS=0` 计数 = ? / ⛔ 未 commit·未 push

#### ⑥ 指纹
参数表 = ?(前值 `42238175d84319ada99afa56d583f9db`)|探针 = ?|代码面各文件 md5 = ?

§9 回头条件(一出现必须回头;⛔ 不许自行扩范围、⛔ 不许调生产值去凑)

  1. 要做"包级"(而非块级)⇒ 立即停下 —— 那正是版本风暴的成因(本线已实证)。
  2. 需要超出 §3.1 文件集 ⇒ 停下报告(R7)。
  3. 要新增公网监听口 / 改 nft·nginx ⇒ 停下报告(R5)。
  4. 要动任何生产值(RELAY_FAILOVER_* / HB_SEC / burst / PRESENCE_*)⇒ 停下报告。
  5. E1 放大比跑不出"≈ 1 份 × 组数"(例如 ≥ 2 份)⇒ 停下报告,⛔ 不许放宽判据凑绿。
  6. 要求"端到端加密" ⇒ 属真取舍(加密即失去按哈希共享块)⇒ 停下上抛,⛔ 不许自行取舍。
  7. 零回归三件套任一退化 ⇒ 停下报告。
  8. 出现静默放行(判别器不计数、只写日志)⇒ 停下报告。
  9. 🔴 ⛔ 不许把 RELAY_FAILOVER_COOLDOWN_MS=0 写进任何回滚 / 演练 / 夹具路径。

§10 指纹与状态

项 值
本单 §7 之前正文前缀指纹 49d0f405e08c909e18ba6825d1442b9d(口径 = sed '/^## §7 /,$d' 交接单_内容分发块级寻址_20260917.md | md5sum)
参数表指纹(立单时) 42238175d84319ada99afa56d583f9db
代码仓 HEAD(立单时) 04776af(工作区 10 处未提交改动)
本单全文件 md5(立单时) 3198288c8b0b102d43c87ca9a3a31cd2(166 行)
本单状态 待执行 ⇒ 交序 ㉔ 执行棒