起因:用户 2026-09-17 明确「所有文档都要同步,都放在开发仓库 docs 对应文件夹下」。
判据:工作区根 *.md 中在仓库(git ls-files --quotepath=false)搜不到的那些。
入仓 33 份(一律复制,工作区根原件保留不动,避免引用断链):
- 04-调整方案/113–128(16 份 · 原子占号后落盘):覆盖网络 传输方案取舍 / 应用场景与待完善清单 /
插件化vs改内核 / 问题逐条推演 / 参数表与观测口径;会合中继拆分取证与改造方案;
集群化改造方案 Manager-Worker;跨节点迁移与节点自举;项目代码分层范式与迭代风险评估;
搬运与共享重建方案 guest w47→w106;方案规划方法提炼;文档无效信息审计报告;
会话接续机制复盘与修复;会话接续规范;dsh 客户端化部署方案;dsh 桌面客户端开发方案
- 交接单/archive/交接单-已完成/T09–T21(13 份 · 覆盖网络线已完成单归档)
- ops/(2 份运行态指针:接续入口 / 接续包 · 覆盖网络线)
- archive/(2 份临时与内部简报)
已排除(无需重复入仓):覆盖网络线 10 份方案正文已入档案 103–112(文件名不同)。
登记:INDEX.md §二 新增 04-113–128 共 16 行 + §四 追加 T09–T21 说明 + 机器摘要行刷新
(⛔ 未跑 docs-index-stats.py --write:该脚本会按 \r\n 归一化全文件行尾,故改为字节级单行替换);
README.md 追加 1 条入仓指针;docs-manifest.json 复跑 scripts/docs-manifest.py 刷新。
验收:工作区根 47 份 .md —— 同名已入仓 8 / 本次内容一致 29 / 已知改名映射 10 / 未入仓 0;
git status 待提交清单只含本次新增与登记 3 件(未涉 src/ 与 relay 代码面)。
44 KiB
交接单 · 3–5 台最小形态真机批次(覆盖网络线 · 序 ⑥)
线:覆盖网络线 | 序:⑥(3–5 台最小形态跑通)| 产出:规划棒 2026-09-17 09:5x 唯一来源:
覆盖网络_应用场景与待完善清单_20260916.md§五 第 6 行("3–5 台最小形态跑通,把 3 个关键估值换成实测,依赖 2/3/4")+参数表_覆盖网络_20260917.md§7 待测项汇总 + 同表 §5.2 校验③(需 ≥2 台中继) 本单要做的事:把参数表 §7 的 4 个待测项 + 1 个待校准推导项设计成可执行实测步骤(每项:怎么测 / 样本多大 / 判据 / 写回哪一行),并合批落地 L3 第二中继机。 🔴 本单不改架构、不新增传输能力:性质是「把估值换成实测」,不是"做打洞"(理由见 §4.1-1,这是本单最重要的一个已定项)。
§1 目标
一句话:让
参数表_覆盖网络_20260917.md里 5 个空值单元格全部有实测值或"如实标注取不到"的记录,并让 relay 从 1 台变 2 台(跨机真容灾成立),同时按回头条件把RELAY_MAX_HOSTS重算并重下发到两台中继。
判定"做完了没有":
参数表中HOLE_PUNCH_RATE_LOCAL/PER_PLAYER_BW_LOCAL/WAN_STEADY_THROUGHPUT/JITTER_LINK_MEASURED/MEM_PER_HOST_MB五行的"等级"列都不再是待测(改成实测,或写成取不到 + 原因——⛔ 但不许编数);- 两台中继机各自的
/status都能看到在册会话,且杀掉任一台后客户端能自动切到另一台; - §5 S7 的回头条件已执行或已写明"本轮未触发、因为哪个数没换成实测"。
§2 只读前置(⛔ 只读,不改;P1–P8 逐条核实后才允许进 S 段)
| # | 命令 | 期望输出 / 判据 |
|---|---|---|
| P1 | bash "D:/github/dsh_shenxian/dsh-server-docs/scripts/handoff-guard.sh" --claim-exec "<你的会话名>" |
✓ 已持全局执行锁。抢不到 = 有会话在跑 ⇒ 只报告并立刻停 |
| P2 | "E:/ProgramData/.workbuddy/binaries/python/versions/3.13.12/python.exe" "E:/ProgramData/AI技能/aliyun-dsh-server/state.py" |
[锁] 🔴 被占用(owner = 你)|HEAD = 640813e|[入口] 接续入口_覆盖网络线_20260916.md |
| P3 | cd "E:/ProgramData/AI技能/aliyun-dsh-server" && sed '/^## §10 指纹/,$d' 参数表_覆盖网络_20260917.md | md5sum |
f3e68012698abb352549e2560746d992。⚠️ 不等 ⇒ 参数表已被改过,先去 §7 确认 待测 计数,再按实际情况调整本单行号 |
| P4 | ssh -p 22 bt-server 'systemctl cat dshs-relay | head -25; echo ---; cat /etc/systemd/system/dshs-relay.service.d/capacity.conf 2>&1' |
期望看到 --max-hosts 225(序⑤ 已下发;⚠️ 记忆铁律:主单元 ExecStart 已有显式 --max-hosts 0,CLI 优先于 Environment= ⇒ 只设 env 会被静默忽略)。⛔ 若未下发 ⇒ 停下报告,不要边补边测 |
| P5 | ssh -p 22 test106 'hostname; nproc; free -m | head -2; systemctl is-active nginx dshs-relay 2>&1; ss -lntp | grep -E ":(443|80|20080)\b"; ls -d /opt/dsh-relay /opt/dshs-cluster 2>&1' |
记录:106 有无 nginx / 443 是否被占 / 是否已有 /opt/dsh-relay。这是 S8 的唯一分叉判据(有 nginx ⇒ 复用 443,零新增口;无 ⇒ 按 §4.2-2 自决) |
| P6 | "E:/ProgramData/.workbuddy/binaries/node/versions/22.22.2-3/node.exe" -v && ls "D:/github/dsh_shenxian/lib/net/relay/" | head |
Node 22.x + lib/net/relay/*.js 已 build。缺 ⇒ 先在代码仓 npm run build(Node 22)再继续 |
| P7 | ssh -p 22 bt-server 'ss -lntp | wc -l; nft list ruleset | wc -l; curl -s -o /dev/null -w "%{http_code}\n" --http1.1 -H "Host: alotbuy.com" 127.0.0.1:3080/; curl -s 127.0.0.1:20080/status | head -c 400' |
不退化对照基线:79 / 72 / 200 / capacity:{max:225,used:N}。⛔ 数与本单不一致 ⇒ 先报告差异,别当成 bug 顺手修 |
| P8 | cd "E:/ProgramData/AI技能/aliyun-dsh-server" && "E:/ProgramData/.workbuddy/binaries/node/versions/22.22.2-3/node.exe" "D:/github/dsh_shenxian/scripts/overlay-probe.cjs" |
12 行 + 退出码 0/1/2。FAIL 清单原样记下(S9 要与它逐项对照) |
🔑 P1–P8 全部只读。任何一条需要写动作 ⇒ 说明你走错了,停下回看 §3。
§3 范围
3.1 要改的(且只有这些)
| # | 对象 | 性质 |
|---|---|---|
| 1 | 工作区根 参数表_覆盖网络_20260917.md |
只改值 + 等级 + 指纹(§3.2/§3.3/§3.5/§5.1/§5.2/§7/§10),⛔ 不改结构、不新增键 |
| 2 | 代码仓 scripts/ 新增一次性脚本(nat-probe / loadgen / bw-probe) |
不进 src/、不进产品路径;判据 = git status 里 src/** 零改动 |
| 3 | 47 上 dshs-relay.service.d/capacity.conf |
仅在 S7 触发时重写(drop-in 重写 ExecStart) |
| 4 | 106 上新增 dshs-relay 单元 + /opt/dsh-relay/(build 后 scp) + 节点密钥 |
S8(L3 第二中继机) |
| 5 | 47 / 106 的 seeds 广播(DSHS_OVERLAY_BOOTSTRAP_SEEDS 或签名目录 relays[]) |
加入第二中继项 |
| 6 | 本单自身 §8 执行回报 + 工作区日志 | 收口 |
3.2 ⛔ 不动什么(防顺手扩大)
- ⛔ 不做打洞实现(不在
src/net/relay/加 UDP/dgram/STUN)—— 见 §4.1-1; - ⛔ 不做 presence / 房间层 / 内容分发 / 游戏服(
清单 §五第 7 步,本单毫不触碰); - ⛔ 不引入第三方 relay 或把 STUN 库写进产品依赖(探测脚本可用公网 STUN 作降级手段,但不得进
package.json); - ⛔ 不重做序 ②/③/④/⑤ 的任何一步;
- ⛔ 不改
dshs/dshs-relay主单元的既有参数(只经 drop-in); - ⛔ 不在 106 上开非 443 的新公网口(安全组不动);
- ⛔ 不 commit / 不 push(本线纪律;本单只改工作区与服务器运行态)。
§4 决策点
4.1 已定项(规划棒已拍 —— 执行棒不得自行更改;要改必须回写 §8 并说明理由)
- 🔴 本单只做「把估值换成实测」,不做打洞实现 —— 依据:①
清单 §五 第 6 行原文是"把 3 个关键估值换成实测",不是"新增传输能力";② 实测确认:src/全仓零 UDP/NAT 穿透代码(grep -iE "dgram|createSocket|stun|punch|udp" src/的命中全部是signature/native假阳性),directory.ts:404-421只有 CGNAT 地址判定;③ 引入打洞 = 净新增 UDP 入站面(命中 R5)+需要 STUN/信令设计,属独立方案,不是本序的交付物。 ⇒ 因此HOLE_PUNCH_RATE_LOCAL的口径定为:测「这台设备所在网络能不能打洞(NAT 映射/过滤行为)」,⛔ 不是"我系统打洞成功率"(后者需要先有实现)。测到的数仍然可用:它决定"打洞实现值不值得做"(若可打洞比例低 ⇒ 这项能力可以直接不做)。 --max-hosts的分母 = 单台中继(参数表 §5.3 已写死);FLEET_RELAY_DEMAND(450)只作校验上界。- L3 第二中继机 = 106 升格(依据:
清单 §3 关键决定已定"有公网 IP 的节点自动升格为中继候选"+参数表 §5.2 校验③"需 ≥2 台")。⛔ 不新建机器、不启用"另开一台云主机"这条路(花钱属边界外,且 §4.3 列着)。 - 2 台就落地 2 台:
225 × 2 = 450 = FLEET_RELAY_DEMAND⇒ 恰好达标、零余量。⚠️ 必须把"零余量"这个事实写进参数表,并登记第三台的触发条件(方案里不得出现"余量充足"这类不实表述)。 - 打洞探测的观察面必须是我们自己的机器(首选:47 上一次性 UDP 观察器,测完即停),⛔ 不优先依赖公网 STUN(降级手段,且须在 §8 标注"经第三方")。
- 五项的判定口径一律"实测优先、取不到如实记":⛔ 任何一项都不许用估值顶替;取不到就写"取不到 + 卡在哪 + 什么条件一出现必须回头"。
4.2 交给执行棒自决(⛔ 不上升为提问)
WAN_STEADY_THROUGHPUT怎么造可控载荷(本单给判定顺序:① 复用既有端点声明机制新增一个临时端口 → ② 复用已声明但空闲的端口 → ③ 降级为"经 relay 的实例面链路吞吐"并如实标注口径);- 106 上 relay 的绑定方式(P5 探明后按序:有 nginx ⇒ 复用 443,零新增口 → 无 nginx ⇒ 装 nginx 复用 443(标准组件、不新增监听)→ 都不可行 ⇒ 停下报告,⛔ 不许开新口);
- 合成节点的工程实现(脚本落
scripts/,用 Node 22 内置能力,不引依赖); - 样本点的具体分布(⚠️ 本单给的是下限,允许加密);
- 所有输出文件落在
_中间产物_待清理/下的临时目录,收口时只保留正式产物(任务收尾纪律)。
4.3 真需要用户拍板的(命中才问,且一轮只问这一句)
第 4/5 台真机的来源 —— 本单能用现有资源跑满 3 台(47 / 106 / 本机),打洞分层样本需要"家宽 / CGNAT / 移动 / 企业网"四类环境,云主机给不出。候选(各有优有劣 ⇒ 才上抛):
A · 只用现有 3 台先跑满能跑的部分 —— 优点:零成本、零等待、今天就能开工;缺点:打洞率的样本偏差大(3 台里 2 台是云),该项大概率只能拿"环境可打洞性"的定性结论,写不回一个可信的百分数。
B · 由用户自备设备(手机热点 / 家里的宽带上的一台机器)跑一次性探测脚本 —— 优点:样本最贴近真实目标场景("同一人的几台设备")、零花费、脚本是一次性的(跑完给 JSON 即可);缺点:需要用户动手,且会占用用户设备一小段时间。
C · 新开 1–2 台轻量云主机(跨运营商 / 跨地域) —— 优点:样本可控可复现、随时可扩到 5 台;缺点:要花钱,且云主机的 NAT 行为与家宽/CGNAT 仍不同构(花了钱也解决不了分层问题)。
我的倾向:A 立即开工 + B 并行补样本,C 不动(理由:C 花了钱还解决不了主要的样本偏差;A+B 组合零成本且能覆盖真实场景)。⇒ 执行棒按 A 开工,不必等这一刻(B 是"什么时候给什么时候补")。
4.4 技术实现裁决顺序(⚠️ 与 dsh-decision-method §4.4 一致)
先取"不改结构的方案"→ 再取"改一处、可回滚的" → 最后才考虑"新增面/新增依赖的"。⛔ 不允许用"新引入一个组件"来绕过取证。
§5 步骤(S0–S9;每步自带一次可执行的验证)
S0 · 只读取证(= §2 P1–P8)
验证:P1–P8 全部有原文输出,且 P3 指纹、P7 基线、P8 的 FAIL 清单已落 §8.1。
S1 · 组起 3 台节点(最小形态第一步)
做:47(Manager+relay+w-47)/106(Worker w-106)/本机(第三节点)。
本机节点 = 一个一次性进程(脚本,只拨出,不声明端口或只声明 1 个 echo 口),用于提供"非云的第 3 个网络视角"。
验证:47 上 curl -s 127.0.0.1:20080/status 的 online[] / sessions[] 长度从 2 变 3(新增本机节点),且本机节点 via 非空。
判据:3 台全部在册 ⇒ S2 起可测;若本机节点起不来(Windows 侧限制)⇒ 如实记"3 台降为 2 台",S2/S3 照常跑(照 2 台口径标注),⛔ 不编第 3 台的数。
S2 · WAN_STEADY_THROUGHPUT 实测(写回参数表 §3.5)
为什么这步第一优先:它是 §5.4 明确"当前取不到、必须回来重算"的那个数,且是 §5.2 的潜在绑定约束。
怎么测:两端可控载荷。判定顺序(§4.2-1):
- 首选:在 106 上把一次性 HTTP 大响应服务绑在
127.0.0.1:<PORT>,经既有端点声明机制(relayPORT_ADD)把它纳入 relay 映射;在 47 上经 relay 拉取。 - 次选:复用已声明但空闲的端口(P4 的
/status.endpoints[]里挑一个不在用的)。 - 兜底:直接测"经 relay 的实例面链路"的稳态速率,口径如实写成"经 relay 转发的实例面吞吐",⛔ 不得冒充裸链路吞吐。
样本:≥ 5 次,每次 ≥ 30 s 稳态段(去掉前 3 s 建连/爬升),载荷 ≥ 8 MB;记录 bytes / sec / 速率。
判据:报中位数(⛔ 不用峰值、不用单次最好值);同时报 min/max 说明离散度。
写回:参数表 §3.5 → WAN_STEADY_THROUGHPUT,等级改 实测,来源定位写"本单 §8.x + 完整命令",并在同一行的备注里写清口径(谁到谁 / 是否经 relay / 稳态段长度)。
S3 · JITTER_LINK_MEASURED 实测(写回参数表 §3.5)+ relay RTT 口径校验
做两件事(不要只做第一件):
(a) 链路 jitter 实测
- 命令形态:
ssh -p 22 bt-server 'ping -c 300 -i 0.2 -W 1 <106 公网 IP>'(反向再做一次)。 - 样本:≥ 200 包(300 包余量更稳)。
- 取数:
rtt min/avg/max/mdev+ 自算p95(|ΔRTT|)(相邻包 RTT 差的 95 分位)——⛔ 只用mdev会低估抖动。 - 判据:与参数表
JITTER_LIMIT_MS(20 ms,估值口径)对照给出达标/不达标二元结论。⚠️ 预期不达标(同链路 relay 路径 RTT 已实测 336 ms)——如实写"不达标",并按 §5(a) 的下一条处理。
(b) 🔴 relay rttMs 口径校验(本单新增的关键验证)
- 参数表把
RELAY_RTT_W106 = 336 ms标成"实测",但它是 relay 心跳往返(server.ts:810注释:①②③ = 测 RTT / 察觉半开 / 保 NAT 表项),可能含应用层处理与验签耗时,不一定等于网络 RTT。 - ⇒ 三方对比:
ICMP RTT(ping)|TCP 握手 RTT(ssh -p 22或到 443 的curl -w %{time_connect})|relay 心跳 rttMs。 - 判据:若 relay
rttMs与 ICMP RTT 差距 > 2× ⇒ 在参数表 §3.5给RELAY_RTT_W106加一条口径备注("心跳往返,含应用层,⛔ 不等于网络 RTT"),并在OBS侧登记"relay rttMs 不得当链路 RTT 用"。 - ⚠️ 这条不是可选项 —— 336 ms 目前是"跨云链路很差"的唯一证据,若它其实是口径问题,后面所有关于"跨云不可玩"的结论都要重判。
写回:参数表 §3.5 → JITTER_LINK_MEASURED(等级改 实测;值 = 中位数 + p95 两个数,注明取哪个作为判定值);RELAY_RTT_W106 加口径备注。
S4 · HOLE_PUNCH_RATE_LOCAL 实测(写回参数表 §3.2)
🔴 口径见 §4.1-1:测的是「该网络能不能打洞」,⛔ 不是"本系统打洞成功率"。
首选路径(不依赖第三方):
- 47 上起一次性 UDP 观察器(脚本,绑定一个高位口,仅测期监听、测完立即停):收到包即回显
{from: <对端源 ip:port>, mapping: <看到的源地址>}。 - 各节点向它发 5 包 ⇒ 拿到本节点的 UDP 公网映射。
- 节点两两互发:双方各自向对方映射每 200 ms 发 1 包、共 10 包,同时收包。
- 判定:任一方收到对方 ≥ 1 包 ⇒ 该对"可打洞"。
- 逐对记录,并标注两侧网络类型(家宽 / CGNAT / 移动 / 企业网 / 云)。
降级路径:47 的 UDP 入站不可达 ⇒ 用公网 STUN 做映射/过滤行为判定(RFC 3489 简化版:同服务器不同端口 + 不同服务器对比映射是否变化);⛔ 须在 §8 注明"经第三方"。 兜底:两者都取不到 ⇒ 如实写"取不到" + 卡在哪 + 回头条件(拿到非云环境即可补测)。
样本:3 台 ⇒ 3 对;若第 4/5 台到位 ⇒ 10 对(每对 10 次尝试)。打洞率 = 成功对次 / 总尝试次,并分层分别给(⛔ 不给单一的合并百分数就完事)。
写回:参数表 §3.2 → HOLE_PUNCH_RATE_LOCAL,值列写"分层结果",等级改 实测,注明样本量("n=3 对,云节点占 2/3,不代表家宽场景"这句话必须写进去)。
权限附注:47 上的 UDP 观察口 = 临时入站面 ⇒ 在 §8 显式列出(对象 / 端口 / 开放时长 / 关闭证据),并回填 参数表 §8(新增一行,结论栏按实际)。
S5 · PER_PLAYER_BW_LOCAL 实测(写回参数表 §3.3)
为什么不能"直接测每玩家带宽":仓库里没有游戏/应用层(清单 §五 第 7 步才谈内容分发与游戏)⇒ 没有真实玩家协议可测。如实处理:
测法:合成玩家载荷 —— 参数扫描(消息率 5 / 20 / 50 msg/s × 消息 200 B × 玩家数 10 / 50),在真机间经 relay 跑 60 s。
取数:每档的端到端 p50/p95 单向时延、丢包率、实际吞吐(KB/s)。
判据:PER_PLAYER_BW_LOCAL = "在 p95 时延 ≤ 2× p50 且丢包 = 0 的前提下,每玩家可达的最大上行速率(KB/s)"。
写回:参数表 §3.3,等级改 实测,并在该行备注里明确写出边界:「本值 = 传输层上限;游戏协议的真实需求仍是估值(PER_PLAYER_BW_TEXT/BATTLE/SIEGE)」——⛔ 不得让读者误以为这是"实测出的游戏需求"。
S6 · MEM_PER_HOST_MB 校准(写回参数表 §5.1)
为什么必须放大测:现网 used = 2,RSS 只反映 Node 基座(参数表 §5.1 已注明)⇒ 2 MB/台 是推导值,不是实测。
怎么测(干净方案,⛔ 不污染生产):
- 本机起一个独立 relay 实例(高位回环口,独立于生产的 20080);
- 起 N 个合成 client(一次性脚本)连它,每个声明 1–2 个端口;
- 量 relay 进程 RSS。
样本点:
N = 2 / 10 / 25 / 50 / 100(⚠️ 这是下限,允许加密)。 判据:① 各点记录 RSS;② 线性回归RSS(N) = a + b·N,R² ≥ 0.9方为有效(否则说明有非线性跳跃,须找出跳点并在 §8 如实报告,⛔ 不许硬套斜率);③MEM_PER_HOST_MB= b(KB/台 → MB/台,向上取整 + 20% 余量)。 写回:参数表 §6→RELAY_RSS_MAX_KB哨兵重新核算(若新斜率与 2 MB 差异 > 50% ⇒ 哨兵必须跟着改);参数表 §5.1→MEM_PER_HOST_MB等级改实测,来源定位写"本单 §8.x(N=2..100 斜率)"。 权限附注:纯本机回环、零公网面 ⇒ ⛔ 不动 47 的任何配置。 顺带(同机合批,不额外开步):参数表 §9 第 4 行(relay 无MemoryMax)——只测量、只登记,⛔ 本单不改单元语义(加 cgroup 上限会引入 OOM-kill 新失败模式,属 R11 的"净变差"风险)。
S7 · 🔴 回头条件强制执行(本单写死的硬门)
在 S2 / S6 出数之后立即执行(⛔ 不允许"下次再说"):
触发条件:MEM_PER_HOST_MB 或 WAN_STEADY_THROUGHPUT 从"推导/待测"换成"实测"。
必做七件(逐条落到 §8):
- 重算
C_MEM = floor(MEM_BUDGET_MB / MEM_PER_HOST_MB_new); - 重算
C_RELAY = min(C_MEM, C_FD);并重新判断带宽是否仍不参与min(若新实测吞吐使 225 台的控制面+实例面流量逼近实测吞吐 ⇒ 带宽进min,这是 §5.4 预留的口子); - 重算
RELAY_MAX_HOSTS = floor(C_RELAY × DESIGN_MARGIN); - 重跑 §5.2 三条校验(防自锁
> used×4/ 余量自洽 / 千台需求≤ 225 × 2 = 450); - 重下发:47 上重写 drop-in
capacity.conf(⚠️ 必须重写 ExecStart 而不是只加Environment=—— 主单元已有显式--max-hosts 0,CLI 优先于 env,只设 env 会被静默忽略)→daemon-reload→restart dshs-relay;106 上的第二中继同值同步; - 复验:
overlay-probe.cjs的OBS-02(max = RELAY_MAX_HOSTS且free = max - used)必须 PASS; - 回写:
参数表 §5.2(含变更前后对照:旧值 → 新值 → 为什么变)+§10 指纹+工作区日志记一笔"哪个键从什么换成什么"。
若未触发(某项确实没换成实测)⇒ 在 §8 明写"未触发,因为 X 没换成实测,卡点是 Y,回头条件是 Z" —— ⛔ 不许默认跳过。
S8 · L3 第二中继机落地(106 升格,与上面合批)
依据:清单 §3 关键决定(有公网 IP 的节点自动升格中继候选)+ 参数表 §5.2 校验③(需 ≥2 台)。
顺序:
- 权限影响评估更新(先做,R5):把
参数表 §8第 ④ 行从"只评估不实施"改为实施态,逐项写明:新增监听口(几个、哪个、公网还是回环) / 新增凭据(节点密钥签发) / 是否改变 106 "入站 = 0" 这条已收窄成果。⛔ 只能用「收窄 / 维持」二选一作结论;确有扩大 ⇒ 在 §8 逐条列出(陈述式,不是征询)。 - 铺 relay 到 106:本机
npm run build(Node 22)→ scp 到 106 的/opt/dsh-relay/(⚠️ 与本线既定纪律一致:本机 build 后 scp;relay 代码真身在/opt/dsh-relay/lib/,⛔ 不是/opt/dshs/lib/)。 - 签发 106 的 relay 身份:用既有密钥仪式 CLI
scripts/overlay-keyring.cjs(序③ 产物);relay 侧 keys 表用逻辑名<net>/<hostId>索引。 - 绑定方式:按 §4.2-2 判定顺序(首选复用既有 443,零新增口)。
- 加入 seeds 广播:把第二中继项写进
DSHS_OVERLAY_BOOTSTRAP_SEEDS或 签名目录relays[](⚠️「改一次 seeds 全网刷新」是既定机制;注意relays[]顺序语义 = 主入口首位,⛔ 第二中继不得插到首位)。 - 两台各自设
--max-hosts(值 = S7 重算结果;若 S7 未触发则沿用 225)。 - 端到端验收(见 §6 E9)。
硬约束:⛔ 不新开非 443 公网口;⛔ 不改安全组;⛔ 不动 47 的角色。
S9 · 回填 + 不退化 + 收口
- 参数表回填:§3.2 / §3.3 / §3.5 / §5.1 / §5.2 / §7(
待测行清空或写明取不到)/ §8 / §10 指纹。 - 不退化检查(逐条对照 S0 的 P7/P8):
ss -lntp | wc -l、nft list ruleset | wc -l、门户200、双实例面、npm test(Node 22)。 - 临时产物清理:探测/压测脚本与中间输出按 §4.2-5 处理,只保留正式产物。
- 收口三件(⛔ 缺一即算本条未完成):
①
--release-exec释放锁; ② 登记下一棒(序 ⑥ 执行棒的后续 / 或按实际收口点到的一棒)并用陈述句在回复里告知用户("已登记自动接续,约 N 分钟后自动开新会话,不用你操作;接续点 = X"); ③ 写工作区日志.workbuddy/memory/2026-09-17.md。
§6 验收(判据清单;命令 + 期望输出,可被第三方复现)
| # | 判据 | 期望 |
|---|---|---|
| E1 | 参数表 待测 单元格数量 |
4 → 0(全部换成实测 或 "取不到 + 原因 + 回头条件");MEM_PER_HOST_MB 等级 推导 → 实测 |
| E2 | 每个新填值的"来源等级" | 全部为 实测,且每行都有一条能跑的命令(复现入口) |
| E3 | 参数表 §10 指纹 | 已更新,且新值 + 旧值都记在 §8 |
| E4 | WAN_STEADY_THROUGHPUT |
≥ 5 样本 + 中位数 + 口径三要素(谁到谁 / 是否经 relay / 稳态段长度) |
| E5 | JITTER_LINK_MEASURED |
≥ 200 包 + `p95( |
| E6 | 打洞探测 | 逐对结果 + 分层标注 + 样本量 + "不代表家宽场景"这句在表内 |
| E7 | MEM_PER_HOST_MB |
N = 2/10/25/50/100 五点 + 斜率 + R²;非线性则如实报告跳点 |
| E8 | 🔴 回头条件已执行 | §5.2 三条校验重跑记录 + 两台 capacity.conf 重下发证据 + OBS-02 PASS(或明写"未触发 + 卡点 + 回头条件") |
| E9 | 第二中继可用 | 两台 relay 各自 /status 有在册会话;杀掉任一台 ⇒ 客户端在 DIRECTORY_REFRESH_SECONDS(300 s)内、实测应在 15 s 心跳级切到另一台(附日志片段) |
| E10 | 暴露面 | ss -lntp / nft 行数与 S0 一致(或新增项逐条列出 + 参数表 §8 已回填) |
| E11 | 不退化 | npm test 全绿(Node 22)|双实例面 200/401(∈ PROBE_CODE_SET)|门户 200 |
| E12 | 收口三件 | 锁已释放 + 下一棒已登记并已陈述句告知 + 日志已写 |
§7 回滚
| 对象 | 回滚动作 | 粒度 |
|---|---|---|
47 的 --max-hosts |
删 drop-in capacity.conf → daemon-reload → restart dshs-relay(回 --max-hosts 0 = 不设限) |
秒级 |
| 106 第二中继 | systemctl stop/disable dshs-relay(106)+ 从 seeds / relays[] 移除该项(改一次全网刷新)+ keyring CLI 吊销该节点密钥 |
分钟级 |
| 打洞/压测脚本 | 全在 scripts/,删文件即可(src/** 零改动 ⇒ 产品路径零回滚需求) |
秒级 |
| 本机独立 relay | 进程退出即消失(无持久化、无开机自启) | 秒级 |
| 参数表 | 保留改动前后的值对照表(§8 内),可反向还原;⛔ 不做整文件覆盖式还原(指纹自指,易错) | — |
备份要求(动手前):47 /opt/dsh-relay/ 铺前打包;/etc/systemd/system/dshs-relay.service.d/*.bak-<step>-<ts>;/etc/dshs/relay-keys.json.bak-<ts>;106 侧同理(首铺无旧件 ⇒ 记录"无旧件")。
§8 回报格式(执行棒按此格式收口;沿用序⑤ 单的分节)
## §8 执行回报(执行棒 · 2026-09-17 10:12 → 11:05)
> **证据等级标记(本单统一口径)**:`【实测】`= 本轮现场跑出来的;`【留档缺口】`= 当时未单独留存、只有结论(**不补造**)。
### 8.1 S0 快照(P1–P8:命令原文 + 原文输出 + 判定)
| # | 命令(原文) | 记录 | 判定 |
|---|---|---|---|
| **P1** | `bash …/handoff-guard.sh --claim-exec "覆盖网络线-序6执行棒"` | `✓ 已持全局执行锁`;收口前复核 OWNER = `覆盖网络线-序6执行棒`,起始 `09-17 10:12` | ✅ |
| **P2** | `python state.py` | 锁被占用(owner = 我)|HEAD = `640813e`|入口 = `接续入口_覆盖网络线_20260916.md` | ✅ |
| **P3** | `sed '/^## §10 指纹/,$d' 参数表_覆盖网络_20260917.md \| md5sum` | **`f3e68012698abb352549e2560746d992`** —— 与单内期望**逐字一致** ⇒ 单内行号/§7 计数有效,未偏航 | ✅ |
| **P4** | `ssh bt-server 'systemctl cat dshs-relay …; cat …/capacity.conf'` | 47 relay drop-in 已带 **`--max-hosts 225`**(序⑤ 下发);主单元 ExecStart 确有显式 `--max-hosts 0` | ✅ |
| **P5** | `ssh test106 'hostname; nproc; free; systemctl is-active nginx dshs-relay; ss; ls -d /opt/dsh-relay /opt/dshs-cluster'` | 106 有 nginx —— **但由宝塔托管**(master = `/www/server/nginx/sbin/nginx`,属 `bt.service`)⇒ `systemctl is-active nginx` = `inactive` **是正常态、不是故障**(收口时复核同值);443 已被占 ⇒ **S8 走"复用 443"分支,零新增公网口**;`/opt/dsh-relay` **不存在** ⇒ 首铺 | ✅ |
| **P6** | `node -v && ls lib/net/relay/` | `v22.22.2` + `lib/net/relay/*.js` 已 build(未触发补 build) | ✅ |
| **P7** | `ssh bt-server 'ss -lntp\|wc -l; nft list ruleset\|wc -l; curl …3080; curl …20080/status'` | 基线 **`79` / `72` / `200` / `capacity{max:225,used:2}`** —— 与单内期望**完全一致** | ✅ |
| **P8** | `node scripts/overlay-probe.cjs` | 12 行 + 退出码 `1`(有红项)。⚠️ **【留档缺口】S0 时刻的原始 FAIL 清单未单独留存**(首次运行已发生在 S 段推进中)⇒ 见 §8.8-3 | ⚠️ |
### 8.2 五项的实测值(怎么测 / 样本量 / 值 / 等级 / 写回位置)
| 项 | 怎么测(复现入口) | 样本量 | 值 / 等级 | 写回 |
|---|---|---|---|---|
| `HOLE_PUNCH_RATE_LOCAL` | `overlay-holepunch.cjs --stun`(首选"47 双 UDP 观察器"路径**失败**,按 S4 降级走公网 STUN) | **n = 3 对** | **2/2 可打洞**;映射 `125.83.247.110:33742`(两次 STUN 一致 ⇒ **cone 型**)/云机 `:21200`;⛔ 表内已写"云节点占 2/3,**不代表家宽场景**" + **方向性限制**(本机→云机被安全组拦,真打洞须成功后回摆)。等级 = **实测(分层)** | 参数表 §3.2 |
| `PER_PLAYER_BW_LOCAL` | `overlay-wan.cjs --players`(7 档扫描) | 10 / 50 玩家 × 5/20/50/200 msg/s | **9.8 KB/s(10 玩家)|3.9 KB/s(50 玩家)**;聚合天花板 **200–350 KB/s**;崩坏点:50 玩家 @50 msg/s(丢包 91%)、10 玩家 @200 msg/s(丢包 85.6%)。等级 = **实测**(边界写明"传输层上限,游戏需求仍是估值") | 参数表 §3.3 |
| `JITTER_LINK_MEASURED` | `overlay-jitter.cjs --icmp … --count 300 --interval 0.2` + `--tcp …` | ICMP 各 300 包 × 双向 + TCP 30 次握手 | **`p95(|ΔRTT|)` = 3 ms ⇒ 达标**(对 `JITTER_LIMIT_MS = 20`,且 20 是业界估值口径、已标明)。**三方对比**:ICMP 148.29 / TCP 153 / **relay 344–376** ⇒ relay 是真 RTT 的 **2.3×**(故 `RELAY_RTT_W106` 已加口径备注:**心跳往返,含应用层+验签,≠ 网络 RTT**)。等级 = 实测 | 参数表 §3.5 |
| `WAN_STEADY_THROUGHPUT` | `overlay-wan.cjs --serve/--download/--upload`(**尊重反压**:`write()` 返 false 必等 `drain`) | 每向 5 样本取中位数 | **下行 352 KB/s(106→47)|上行 12213 KB/s(47→106)**;口径三要素齐全。**决定性校验**:relay `/status` 计数 `in=1905328505B` 与 106 侧 `rchar=1905380725` 吻合 ⇒ 12 MB/s 确实跨了 WAN;106 公网出带宽封顶 ≈2.8 Mbps 正是下行 352 的成因。`WAN_UP_BOUND_KBPS` **192 → 作废**(下界偏低 64×) | 参数表 §3.5 |
| `MEM_PER_HOST_MB` | `relay-mem-calibrate.mjs`(本机独立 relay 子进程 + 同进程内 N 个 `RelayClient`;RSS 取 7 次采样中位数) | 6 点(N=2/10/25/50/100/150) | **0.06 MB/台**(斜率 46.7 KB/台,**R² = 0.9424**)⇒ `2 → 实测`;原"2 MB/台"**高估 36×**(把 per-stream 256 KB 当成了 per-host)。**首跑 R²=0.487 已作废**(单次采样噪声),修正过程记在 8.5。等级 = 实测 | 参数表 §5.1 |
### 8.3 参数表 diff 摘要(旧值 → 新值)
| 键 | 旧 | 新 |
|---|---|---|
| `HOLE_PUNCH_RATE_LOCAL` | 待测 | 分层实测(2/2,n=3 对,云节点 2/3) |
| `PER_PLAYER_BW_LOCAL` | 待测 | 9.8 / 3.9 KB/s(10/50 玩家) |
| `JITTER_LINK_MEASURED` | 待测 | `p95(|ΔRTT|)` = 3 ms(达标) |
| `WAN_STEADY_THROUGHPUT` | 待测 | 352(106→47)/ 12213(47→106)KB/s |
| `WAN_UP_BOUND_KBPS` | 192 | **作废**(下界偏低 64×) |
| `MEM_PER_HOST_MB` | 推导 2 | **实测 0.06** |
| `C_MEM` / `C_RELAY` | 501 | **16700** |
| `RELAY_MAX_HOSTS` | **225** | **7515** |
| §5.2 校验③ 结论 | `450 > 225` ⇒ 容量上必须 ≥2 台 | **结论变更**:容量上单台即够;2 台依据改为"公网节点升格"+"跨机真容灾" |
| §5.4 带宽判定 | 旧 | 用实测重判"带宽不进 `min`",登记实例面单次 46.3 MB ≈ **135 s 时延上界** |
| §7 待测项计数 | 4 + 1 待校准 | **0 + 0**(回头条件标记"已执行") |
| §8 权限影响 | ④ 单条 | ④ 改**实施态**(新增监听口 0 / 新增凭据 0 / 106 入站仍为 0,**结论维持**)+ 新增 ⑧(临时 UDP 观察口,已关闭)⑨(106 的 443 `location /dshs-relay`)—— 两条**结论均维持** |
| §9 已知边界 | 4 行 | **8 行**(新增:无失败切流 / per-stream 内存未测 / 106 无 bootstrap / 宝塔管 vhost) |
| §10 指纹 | `f3e68012698abb352549e2560746d992` | **`db1317c2f7aaef7b47785c1f4fc9de03`** |
### 8.4 E1–E12 逐条
| # | 现场证据 | 判定 |
|---|---|---|
| **E1** | `grep -c 待测` = 8,**逐条核对全部落在**:§7 标题/计数口径说明/图例行 `\| **待测** \|` —— **数据单元格 0 个**(4 → 0);`MEM_PER_HOST_MB` 等级 `推导 → 实测` | ✅ |
| **E2** | 五个新值全部标 `实测`/`实测(分层)`,每行带一条可跑命令(见 8.2 第 2 列) | ✅ |
| **E3** | 新指纹 `db1317c2…`、旧指纹 `f3e68012…` 均已记(本节 8.3 + 8.9) | ✅ |
| **E4** | 5 样本中位数 + 口径三要素(谁到谁/是否经 relay/稳态段长度)+ relay 侧与 106 侧字节数交叉校验 | ✅ |
| **E5** | ICMP 300 包 × 双向(≥200)+ `p95(\|ΔRTT\|)` + **ICMP/TCP/relay 三方对比表**(并在表内标明 20 ms 是估值口径) | ✅ |
| **E6** | 逐对结果 + **分层标注** + n=3 对 + 表内明写"不代表家宽场景" + 方向性限制 | ✅ |
| **E7** | 六点 `N = 2/10/25/50/100/150` + 斜率 46.7 KB/台 + `R²=0.9424`(非线性/跳点不适用;首跑 R²=0.487 已作废并记因) | ✅ |
| **E8** | 回头条件**已触发**:§5.2 三条校验**全部重跑**(① 防自锁 `7515 > 4×4` ✅ ② 见参数表 ③ `450 ≤ 7515` 结论变更)+ **两台** `capacity.conf` 重下发(47/106 `"max":7515`)+ **`OBS-02` 复验 PASS**(`max=7515 used=2 free=7513`) | ✅ |
| **E9** | 两台 relay 各自 `/status` 可见在册会话(47:`manager`/`w-106`;106:`max=7515` 就绪,443 入口 **WS 101**)。**前半绿**:杀掉 106 后 w-dev 断连并 **1.2 s 内**自动重连回同一台。**后半红**:**不会切到另一台** —— ⇒ 见 §8.8-1 | ⚠️ **半绿** |
| **E10** | 收口后 `ss -lntp \| wc -l` = **79**(= S0 基线)、`nft` = **72**(= 基线);106 = 14 个监听(基线 14 + relay 的 `127.0.0.1:20080`);参数表 §8 已逐条回填(新增 ⑧⑨) | ✅ |
| **E11** | `npm test`(Node 22)= **138 tests / 137 pass / 0 fail / 1 skipped**;双实例面 `本机:20000=401`/`w-106=401`(∈ `PROBE_CODE_SET`);门户 = **200** | ✅ |
| **E12** | 锁 `--release-exec` 已释放;下一棒已登记 automation **并已陈述句告知**;工作区日志已写 | ✅ |
### 8.5 🔴 S7 回头条件(触发与否 + 重算过程 + 重下发 + `OBS-02` 复验)
1. **触发**:`MEM_PER_HOST_MB` 2 → 0.06、`WAN_STEADY_THROUGHPUT` 待测 → 352/12213 ⇒ 命中"实测值替换后必须重算容量"。
2. **重算**:`C_MEM = floor(1002 / 0.06) = 16700` → `C_RELAY = min(16700, 65536) = 16700` → `RELAY_MAX_HOSTS = floor(16700 × 0.45) = 7515`。三条校验重跑,**结论③ 变更**(单台容量即足够)。
3. **重下发(关键坑位)**:主单元 ExecStart **已有显式 `--max-hosts 0`**,**CLI 优先于 `Environment=`** ⇒ 只设 env 会被**静默忽略** ⇒ 两台一律用 **drop-in 重写 ExecStart**:`47-capacity.conf` / (106 同款) → `daemon-reload` → `restart dshs-relay`。
4. **复验**:47 `/status` `"max":7515,"used":2,"free":7513`;106 `/status` `"max":7515`;**`OBS-02` PASS**。
5. **校准方法自纠**:首跑 `R² = 0.4871` 不合格 ⇒ 定性为**单次 RSS 采样噪声** ⇒ 每点改 **7 次采样取中位数** 并补第 6 点(N=150)⇒ `R² = 0.9424`。**首跑结论已作废、未写进参数表**。
### 8.6 第二中继(106)落地 + 切流验证 + 权限影响评估更新版
| 项 | 结果 |
|---|---|
| 落地 | `/opt/dsh-relay/`(build 后 scp)+ 新单元 `dshs-relay`(**只绑 `127.0.0.1:20080`**)+ 节点密钥 `ops/w-106`;**首铺,无旧件** ⇒ §7"记录无旧件"已满足 |
| 443 暴露 | 走 §4.2-2 自决:**复用既有 443**(`location /dshs-relay` 挂在 `include …/extension/106.54.21.172/*.conf` 里 ⇒ 落在既有 server 块**内部**)。实测 **WS 升级握手 = 101**、`/nope` = **404**。**零新增公网口**(安全组未动) |
| seeds 广播 | 47 的 `…-443fb.conf` 把 `https://106.54.21.172/dshs-relay` 追加到 `relays[]` **末位**(不动首位主入口) |
| 切流验证 | **前半绿 / 后半红** —— 详见 §8.8-1(**单外发现,只报告不动手**) |
| 权限影响(§8④ 更新版) | **新增监听口 = 0**(复用 443)|**新增凭据 = 0**(节点密钥落在既有 `/etc/dshs/relay-keys.json`,按 `<net>/<hostId>` 逻辑名索引)|**106 入站 = 0**(worker 永远只拨出)⇒ **R5 结论维持**:暴露面未扩大 |
| 收口清理(S9-3) | 两个临时节点密钥 **已吊销**(`ops/w-dev`、`ops/w-106p`;两台 keys 表 5 → **3 条**,回到基线 `manager`/`w-106`/`w-47`,先备份后原子写、`loadKeysFile` 自校验);探针目录 `/opt/seq6-probe`(47/106)、`/tmp/seq6-*`、106 的 `node-w-106p.*` **均已删**;复核 `pgrep` 零命中 |
### 8.7 不退化(S0 对照 / 双实例面 / 门户 / `npm test`)
| 项 | S0 基线 | 收口 | 判定 |
|---|---|---|---|
| 47 `ss -lntp \| wc -l` | 79 | **79** | ✅ 完全一致 |
| 47 `nft list ruleset \| wc -l` | 72 | **72** | ✅ |
| 门户 | 200 | **200** | ✅ |
| 双实例面 | 200/401 | `本机:20000=401`、`w-106:41775=401` | ✅ ∈ 码集 |
| `npm test`(Node 22) | — | 138 / 137 pass / 0 fail / 1 skip | ✅ |
| `overlay-probe` | 12 行(见 8.1-P8) | **12/12 PASS**(`OBS-02` `OBS-08` `OBS-11` 三红**全部转绿**) | ✅ |
| 47 relay 会话 | `manager` / `w-106` | 同(重启后自动重连,`identityOk=2`) | ✅ |
> **三红转绿的根因(诚实记录)**:`OBS-11`/`OBS-08` 的红**不是泄漏**,是 S1/S8 期间**临时探针会话在 relay 内存里留下的 2 条离线端点**(relay 每个 endpoint 会占 1 个本地监听 ⇒ 79 → 81)。S9 清理 + relay 重启后端点表回到 2 条、监听口回到 79。`OBS-02` 的红是**探针解析坑**(见 8.8-2),修的是**参数表书写**,不是改脚本。
### 8.8 未过项 / 遗留
1. 🔴 **E9 后半(无"失败切流")—— 本单唯一未过项,根因已定位**:客户端把 relay url **在首次解析后钉死** —— `main.js --client` 无重解析;worker 走 `DSHS_RENDEZVOUS_URL` 同样不吃引导链;只有 Manager 的**拨号通道**有周期重解析(`web/server.ts#refreshOverlay`),而它的换址条件是"**目录里的地址变了**",与"当前 relay 挂了"**无关**。⇒ **已做到哪一步**:第二中继本身可用(101 + 容量就绪 + seeds 已广播)、断连自动重连成立(1.2 s)。**什么条件一出现必须回头解决**:要做**多中继负载分担**或**真容灾切换**时,必须先补"连接失败后重解析 + 排除已失败 relay"这段**新功能**。已登记参数表 §9 第 5 行。
2. ⚠️ **`OBS-02` 假红的解析坑(已修,须防复发)**:探针 `KEY_RE` 取参数表**行内整格**并 `cleanValue`(只剥 `*` / 反引号)⇒ 值格里写 `**7515**(原 225)` 会被当成 `7515(原 225)` ⇒ `NaN` ⇒ 假红。**已把夹注挪出值格**,并在参数表该行写明"值格必须是纯数字"。**回头条件**:以后任何键改值,⛔ 别往值格塞夹注。
3. ⚠️ **【留档缺口】P8 的 S0 原始 FAIL 清单未单独留存**(详见 8.1-P8)。已做到哪一步:收口状态 12/12 PASS 有据可查。**回头条件**:下一棒若仍以探针作对照,**开跑即先存一份原始输出**(`> /tmp/xxx.txt`)。
4. ⚠️ **per-stream 内存开销仍未测**(`MEM_PER_HOST_MB` = 0.06 只是空闲会话斜率):已登记参数表 §9 第 6 行。**回头条件**:`--max-hosts` 若重新收紧,必须先有本数。
5. ⚠️ **106 的 `127.0.0.1:40179` 在收口时已不在监听**(P5 快照里有):非本单所留(本单在 106 只碰 `19777/19778/20080`)⇒ 如实登记,未追查,**亦未顺手修**(R7)。
6. ⚠️ **106 的 `systemctl is-active nginx` = `inactive`**:由宝塔(`bt.service`)托管 nginx,**不是退化**;但**任何"用 systemctl 判 106 nginx 死活"的脚本都会误判** ⇒ 记入运维注意。
### 8.9 指纹(本单收口后的可复现核对口径)
- **参数表**(§10 不计入):
`cd "E:/ProgramData/AI技能/aliyun-dsh-server" && sed '/^## §10 指纹/,$d' 参数表_覆盖网络_20260917.md | md5sum`
⇒ **`db1317c2f7aaef7b47785c1f4fc9de03`**(S0 = `f3e68012698abb352549e2560746d992`)
- **本交接单**(**§8 及其后不计入** —— 本值就在 §8 内,含进去即刻失效):
`cd "E:/ProgramData/AI技能/aliyun-dsh-server" && sed '/^## §8 执行回报(执行棒/,$d' 交接单_最小形态真机批次_20260917.md | md5sum`
⇒ **`1edde731eba5034c5f6f3a43864e5a8e`**
- ⚠️ §10 原口径(整文件不计 §10)**无法内嵌数值**(自指),故本单改用上面这条**前缀口径**。
附 A · 规划棒已核实的事实(执行棒不必重复探索)
- 🔴
src/全仓零 UDP / NAT 穿透代码 ——grep -iE "dgram|createSocket|stun|punch|udp" src/的命中全部是signature/native/alternative之类假阳性;src/net/relay/directory.ts:404-421只有 CGNAT 地址判定函数(100.64.0.0/10)。⇒ 本单不测"系统打洞成功率"(§4.1-1)。 - 参数表 §7 计数已核:
待测4 个(HOLE_PUNCH_RATE_LOCAL/PER_PLAYER_BW_LOCAL/WAN_STEADY_THROUGHPUT/JITTER_LINK_MEASURED)+ 待校准推导 1 个(MEM_PER_HOST_MB)。 - 参数表 §5.2 校验③ 已算出:
FLEET_RELAY_DEMAND = 450 > RELAY_MAX_HOSTS = 225⇒ ≥2 台中继;225 × 2 = 450⇒ 恰好达标、零余量(§4.1-4 要求把这个事实写进表)。 --max-hosts的落点陷阱:主单元 ExecStart 已有显式--max-hosts 0,CLI 优先于Environment=⇒ 只设 env 会被静默忽略,必须 drop-in 重写 ExecStart(S7-5 已写死)。- 现役只有 1 台中继(47);
relay只绑127.0.0.1:20080,经 nginx 443 暴露(origin + CF 双路 101)。 relay的rttMs是心跳往返(server.ts:810注释写明三个作用)⇒ 不一定等于网络 RTT(S3(b) 要求做三方校验)。JITTER_LIMIT_MS = 20 ms是估值口径(业界),不是实测 ⇒ 与实测对比时必须标明这一点。PER_PLAYER_BW_*三行全部是估值(0.5 / 2–5 / 10–20 KB/s,来源为同一份调研文档)⇒ 本单测的是传输层上限,不是游戏协议需求(S5 已写死边界)。
附 B · 硬约束复述(防走偏)
- 提问判据:技术实现(怎么造载荷 / 怎么绑 443 / 脚本怎么写 / 样本怎么分布)一律自决;只有 §4.3 一项属真取舍,且执行棒不必等(按倾向 A 开工)。
- 只做被明确要求的事:执行中发现的其他缺陷(如既有 502 / 引导链问题)先报告,不顺手改。
- 成本纪律:批量活先写脚本再让脚本跑,⛔ 不把"大范围取证"派给无人值守会话。
- 红线:R5(权限只准收窄;扩大必须出评估)|R7(不做未授权批量写入;本机是生产的前身)|R11(任一维度净变差即停)。
- 收口:锁必须释放;下一棒必须登记并用陈述句告知;日志必须写。
§10 指纹
- 本节口径(推荐核对用,可复现):整个 §10 不计入 ⇒
cd "E:/ProgramData/AI技能/aliyun-dsh-server" && sed '/^## §10 指纹/,$d' 交接单_最小形态真机批次_20260917.md | md5sum⇒ 见 §8.9 回填。 - 全文件 md5:请现取(⛔ 本行故意不内嵌数值 —— 包含本节自身,写进去即刻失效)。
§11 补记(2026-09-17 11:1x,在指纹口径之外)
- ✅ §4.3 的唯一待拍板项已闭环:用户原话「1 本机内存大 可以模拟多台」⇒ 选 D · 本机模拟多台,放弃候选 B(自备设备)与 C(新开云主机)。⭐ 本条覆盖 §4.3 里"才上抛 / 倾向 A+B / 执行棒按 A 开工"的表述。
- 本机实测:总内存 47.6 GB / 空闲 27.4 GB / 32 核;relay 单实例 ≈ 48 MB ⇒ 可模拟数十台。⚠️ 局限:多实例共用同一出口 IP ⇒ 对"切流逻辑"够用,对"家宽 / 运营商 NAT 差异"无增量(§4.3 里"云主机给不出分层样本"的判断同样适用于本机多实例)。
- 落到本单:"第 4/5 台"改由本机多实例承担(不再等新真机);§5 样本口径里"若第 4/5 台到位 ⇒ 10 对"按此理解执行。
- 🔒 本补记位于 §10 之后 ⇒ §8.9 回填的前缀指纹与 §10 口径均不变。