交接单按归属约定(正文落文档库、工作区只放指针)归档至 归档/交接单-20260924-归档/,含逐件判定 README: - 16 件已被文档库正式版取代(T09–T21 + 覆盖网络-24/25/26) - 4 件主题已被覆盖网络线入口汇总 - 7 件历史接续包/规划件 CODEBUDDY §9:收口清本棒 tmp、tmp 保留期 7 天、禁「待清理」中间态、 工作区入库只放文档与文件、不保留脚本副本、>60 KB 单文件须逐个判。
435 lines
44 KiB
Markdown
435 lines
44 KiB
Markdown
# 交接单 · 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` 重算并重下发到两台中继。
|
||
|
||
**判定"做完了没有"**:
|
||
1. `参数表` 中 `HOLE_PUNCH_RATE_LOCAL` / `PER_PLAYER_BW_LOCAL` / `WAN_STEADY_THROUGHPUT` / `JITTER_LINK_MEASURED` / `MEM_PER_HOST_MB` **五行**的"等级"列都不再是 `待测`(改成 `实测`,或写成 `取不到 + 原因`——⛔ 但**不许编数**);
|
||
2. 两台中继机各自的 `/status` 都能看到在册会话,且**杀掉任一台后客户端能自动切到另一台**;
|
||
3. §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 并说明理由)
|
||
|
||
1. 🔴 **本单只做「把估值换成实测」,不做打洞实现** —— 依据:① `清单 §五 第 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 映射/过滤行为)」,⛔ 不是"我系统打洞成功率"**(后者需要先有实现)。**测到的数仍然可用**:它决定"打洞实现值不值得做"(若可打洞比例低 ⇒ 这项能力可以直接不做)。
|
||
2. **`--max-hosts` 的分母 = 单台中继**(参数表 §5.3 已写死);`FLEET_RELAY_DEMAND`(450)**只作校验上界**。
|
||
3. **L3 第二中继机 = 106 升格**(依据:`清单 §3 关键决定`已定"**有公网 IP 的节点自动升格为中继候选**"+参数表 §5.2 校验③"需 ≥2 台")。⛔ 不新建机器、不启用"另开一台云主机"这条路(花钱属边界外,且 §4.3 列着)。
|
||
4. **2 台就落地 2 台**:`225 × 2 = 450 = FLEET_RELAY_DEMAND` ⇒ **恰好达标、零余量**。⚠️ **必须把"零余量"这个事实写进参数表**,并登记第三台的**触发条件**(方案里不得出现"余量充足"这类不实表述)。
|
||
5. **打洞探测的观察面必须是我们自己的机器**(首选:47 上一次性 UDP 观察器,测完即停),⛔ 不优先依赖公网 STUN(降级手段,且须在 §8 标注"经第三方")。
|
||
6. **五项的判定口径一律"实测优先、取不到如实记"**:⛔ 任何一项**都不许用估值顶替**;取不到就写"取不到 + 卡在哪 + 什么条件一出现必须回头"。
|
||
|
||
### 4.2 交给执行棒自决(⛔ **不上升为提问**)
|
||
|
||
1. `WAN_STEADY_THROUGHPUT` **怎么造可控载荷**(本单给判定顺序:① 复用既有端点声明机制新增一个临时端口 → ② 复用已声明但空闲的端口 → ③ 降级为"经 relay 的实例面链路吞吐"并如实标注口径);
|
||
2. 106 上 relay 的**绑定方式**(P5 探明后按序:**有 nginx ⇒ 复用 443,零新增口** → 无 nginx ⇒ 装 nginx 复用 443(标准组件、不新增监听)→ 都不可行 ⇒ 停下报告,⛔ 不许开新口);
|
||
3. 合成节点的**工程实现**(脚本落 `scripts/`,用 Node 22 内置能力,不引依赖);
|
||
4. 样本点的具体分布(⚠️ 本单给的是**下限**,允许加密);
|
||
5. 所有输出文件落在 `_中间产物_待清理/` 下的临时目录,收口时**只保留正式产物**(任务收尾纪律)。
|
||
|
||
### 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):
|
||
1. **首选**:在 106 上把一次性 HTTP 大响应服务绑在 `127.0.0.1:<PORT>`,**经既有端点声明机制**(relay `PORT_ADD`)把它纳入 relay 映射;在 47 上经 relay 拉取。
|
||
2. **次选**:复用已声明但空闲的端口(P4 的 `/status.endpoints[]` 里挑一个不在用的)。
|
||
3. **兜底**:直接测"经 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**:测的是「**该网络能不能打洞**」,⛔ 不是"本系统打洞成功率"。
|
||
|
||
**首选路径(不依赖第三方)**:
|
||
1. 47 上起**一次性 UDP 观察器**(脚本,绑定一个高位口,**仅测期监听、测完立即停**):收到包即回显 `{from: <对端源 ip:port>, mapping: <看到的源地址>}`。
|
||
2. 各节点向它发 5 包 ⇒ 拿到**本节点的 UDP 公网映射**。
|
||
3. 节点两两互发:双方各自向对方映射每 200 ms 发 1 包、共 10 包,同时收包。
|
||
4. **判定**:任一方收到对方 ≥ 1 包 ⇒ 该对"**可打洞**"。
|
||
5. 逐对记录,并**标注两侧网络类型**(家宽 / 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/台 是推导值,不是实测**。
|
||
|
||
**怎么测(干净方案,⛔ 不污染生产)**:
|
||
1. **本机起一个独立 relay 实例**(高位回环口,**独立于生产的 20080**);
|
||
2. 起 **N 个合成 client**(一次性脚本)连它,每个声明 1–2 个端口;
|
||
3. 量 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):
|
||
1. 重算 `C_MEM = floor(MEM_BUDGET_MB / MEM_PER_HOST_MB_new)`;
|
||
2. 重算 `C_RELAY = min(C_MEM, C_FD)`;**并重新判断带宽是否仍不参与 `min`**(若新实测吞吐使 225 台的控制面+实例面流量逼近实测吞吐 ⇒ **带宽进 `min`**,这是 §5.4 预留的口子);
|
||
3. 重算 `RELAY_MAX_HOSTS = floor(C_RELAY × DESIGN_MARGIN)`;
|
||
4. **重跑 §5.2 三条校验**(防自锁 `> used×4` / 余量自洽 / 千台需求 `≤ 225 × 2 = 450`);
|
||
5. **重下发**:47 上**重写 drop-in `capacity.conf`**(⚠️ **必须重写 ExecStart 而不是只加 `Environment=`** —— 主单元已有显式 `--max-hosts 0`,**CLI 优先于 env**,只设 env 会被静默忽略)→ `daemon-reload` → `restart dshs-relay`;**106 上的第二中继同值同步**;
|
||
6. **复验**:`overlay-probe.cjs` 的 `OBS-02`(`max = RELAY_MAX_HOSTS` 且 `free = max - used`)必须 PASS;
|
||
7. **回写**:`参数表 §5.2`(含**变更前后对照**:旧值 → 新值 → 为什么变)+ `§10 指纹`+工作区日志记一笔"哪个键从什么换成什么"。
|
||
|
||
**若未触发**(某项确实没换成实测)⇒ 在 §8 **明写"未触发,因为 X 没换成实测,卡点是 Y,回头条件是 Z"** —— ⛔ 不许默认跳过。
|
||
|
||
---
|
||
|
||
### S8 · L3 第二中继机落地(106 升格,**与上面合批**)
|
||
|
||
**依据**:`清单 §3 关键决定`(有公网 IP 的节点自动升格中继候选)+ 参数表 **§5.2 校验③**(需 ≥2 台)。
|
||
|
||
**顺序**:
|
||
1. **权限影响评估更新(先做,R5)**:把 `参数表 §8` 第 ④ 行从"只评估不实施"改为**实施态**,逐项写明:**新增监听口(几个、哪个、公网还是回环)** / **新增凭据(节点密钥签发)** / **是否改变 106 "入站 = 0" 这条已收窄成果**。⛔ 只能用「收窄 / 维持」二选一作结论;**确有扩大 ⇒ 在 §8 逐条列出**(陈述式,不是征询)。
|
||
2. **铺 relay 到 106**:本机 `npm run build`(Node 22)→ scp 到 106 的 `/opt/dsh-relay/`(⚠️ 与本线既定纪律一致:**本机 build 后 scp**;relay 代码真身在 `/opt/dsh-relay/lib/`,⛔ 不是 `/opt/dshs/lib/`)。
|
||
3. **签发 106 的 relay 身份**:用既有密钥仪式 CLI `scripts/overlay-keyring.cjs`(序③ 产物);relay 侧 keys 表用**逻辑名 `<net>/<hostId>`** 索引。
|
||
4. **绑定方式**:按 §4.2-2 判定顺序(**首选复用既有 443**,零新增口)。
|
||
5. **加入 seeds 广播**:把第二中继项写进 `DSHS_OVERLAY_BOOTSTRAP_SEEDS` **或** 签名目录 `relays[]`(⚠️「改一次 seeds 全网刷新」是既定机制;注意 `relays[]` **顺序语义 = 主入口首位**,⛔ 第二中继**不得**插到首位)。
|
||
6. **两台各自设 `--max-hosts`**(值 = S7 重算结果;若 S7 未触发则沿用 225)。
|
||
7. **端到端验收(见 §6 E9)**。
|
||
|
||
**硬约束**:⛔ 不新开非 443 公网口;⛔ 不改安全组;⛔ 不动 47 的角色。
|
||
|
||
---
|
||
|
||
### S9 · 回填 + 不退化 + 收口
|
||
|
||
1. **参数表回填**:§3.2 / §3.3 / §3.5 / §5.1 / §5.2 / §7(`待测` 行清空或写明取不到)/ §8 / §10 指纹。
|
||
2. **不退化检查**(逐条对照 S0 的 P7/P8):`ss -lntp | wc -l`、`nft list ruleset | wc -l`、门户 `200`、双实例面、`npm test`(**Node 22**)。
|
||
3. **临时产物清理**:探测/压测脚本与中间输出按 §4.2-5 处理,**只保留正式产物**。
|
||
4. **收口三件(⛔ 缺一即算本条未完成)**:
|
||
① `--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(|ΔRTT|)` + **ICMP / TCP / relay 三方对比表** |
|
||
| **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 · 规划棒已核实的事实(执行棒**不必重复探索**)
|
||
|
||
1. 🔴 **`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)。
|
||
2. **参数表 §7 计数已核**:`待测` **4 个**(`HOLE_PUNCH_RATE_LOCAL` / `PER_PLAYER_BW_LOCAL` / `WAN_STEADY_THROUGHPUT` / `JITTER_LINK_MEASURED`)+ **待校准推导 1 个**(`MEM_PER_HOST_MB`)。
|
||
3. **参数表 §5.2 校验③ 已算出**:`FLEET_RELAY_DEMAND = 450 > RELAY_MAX_HOSTS = 225` ⇒ **≥2 台中继**;`225 × 2 = 450` ⇒ **恰好达标、零余量**(§4.1-4 要求把这个事实写进表)。
|
||
4. **`--max-hosts` 的落点陷阱**:主单元 ExecStart **已有显式 `--max-hosts 0`**,**CLI 优先于 `Environment=`** ⇒ 只设 env 会被**静默忽略**,**必须 drop-in 重写 ExecStart**(S7-5 已写死)。
|
||
5. **现役只有 1 台中继**(47);`relay` 只绑 `127.0.0.1:20080`,经 nginx 443 暴露(origin + CF 双路 101)。
|
||
6. **`relay` 的 `rttMs` 是心跳往返**(`server.ts:810` 注释写明三个作用)⇒ **不一定等于网络 RTT**(S3(b) 要求做三方校验)。
|
||
7. **`JITTER_LIMIT_MS = 20 ms` 是估值口径**(业界),不是实测 ⇒ 与实测对比时**必须标明这一点**。
|
||
8. **`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 口径均不变**。
|