进入架构进阶部分。这一篇讲两件事它们分别回答「怎么更便宜、更快」和「怎么把资源边界管住」- **P2P 直连**让媒体绕过边缘节点从推流端直接到观众——理论上延迟最低、且不占边缘出口带宽的「最后一公里捷径」。- **节点池调度**在「角色 区域」之外再加一维资源隔离边界决定一个请求**能看见哪些节点**。---## 第一部分P2P 直连### 1. 预测与实测标签只是预筛选ICE 检查才决定能不能连上「Full cone / restricted / symmetric NAT」这套经典分类在现代 ICE 实践里被认为是**过度简化**。更准确的描述是分别观察- 地址/端口的 **mapping behavior**映射行为- 入站包的 **filtering behavior**过滤行为再结合是否支持 hairpinning、映射寿命、IPv6、UDP 是否可用、网络是否切换综合判断。CGNAT 只说明地址转换发生在运营商网络**不等于必然无法打洞**。但现实中的实现往往还在用「经典类型标签做硬性预筛选」这个简化版本比如只要任意一方报了 symmetric 或 CGNAT就直接拒绝连 ICE 都不尝试。这是不是一个错误**不是。** 对称型 NAT 和 CGNAT 组合的打洞成功率本来就低提前拒绝能省掉一次几乎注定失败的信令和 ICE 开销是合理的工程取舍。但不应该把它包装成「系统已经在用 mapping/filtering 行为做精细判断」——**准确的说法是理想的 ICE/NAT 工程实践基于行为观测分级处理而简单实现仍在用标签做硬性预筛选。**关键要分清两个概念预筛选通过值得尝试 ≠ 已经证明这两端能连上真正决定直连成不成的永远是客户端那一次 ICE candidate-pair 连通性检查预筛选只负责把明显没希望的组合提前挡在门外省开销**不负责预测「这一次具体会不会成功」**。### 2. 诚实的边界网络世界没有数学意义上的 100%必须说清楚**NAT 预筛选不可能在互联网环境下提供数学意义上的 100% 成功保证。** 即使通过了全部预筛选规则真正尝试连接时仍可能因为防火墙策略、网络切换、地址映射恰好过期而失败。所以「ICE 可能失败」必须配一条兜底路径。这里有一个值得强调的设计演进**从「并行竞速」改成「顺序回退」。**- 早期一些实现让直连和边缘**并行起跑**谁先出帧用谁用一个窗口限制等待时间。这会让首帧时间不可预测、建连逻辑复杂。- 更清晰的模型是**顺序回退**客户端先尝试直连失败、超时或名额已满就直接改用**随决策一起下发、早已拿在手里的边缘地址**。不是两条链路抢首帧是一条主路径 一个随时能用的备用地址。### 3. TURN 与 Edge这是产品取舍不是技术优劣TURN 是 ICE 的标准 relay中继来源它的价值是提高受限网络、防火墙或 UDP 不可用环境下的连接成功率代价是中继带宽、部署运维以及可能增加的路径时延。如果产品**已经有成熟的边缘媒体路径**完全可以选择「直连失败后回退边缘」而不部署 TURN。这是成本、协议一致性、覆盖率和运维复杂度之间的取舍。但要诚实这个选择的代价是**对称 NAT / CGNAT 用户完全没有「走中继也能直连」的退路只能走边缘**。如果将来要覆盖这批用户TURN 部署会是绕不开的一步。### 4. 名额分配为什么要用「原子租约」P2P 直连会占用推流端自己的上行带宽所以不能无限制地让所有观众都跟同一个推流端直连。一个常见做法是限制「每个推流端同一时间最多服务 N 路直连」第 N1 个观众必须走边缘。这个上限保护两件事推流端上行不会被「一拖多」拖垮以及已建立的几路直连不会因新连接加入而质量下降。但这里藏着一个并发系统里的经典问题如果多个观众几乎同时请求「判断能不能跟这个推流端连」光靠「查询当前有几路小于 N 就同意」会出现竞态——两个请求都查到「当前是 N−1 路」都判断「还有名额」结果同时获批实际建立了 N1 路。**解决办法是把「检查名额是否够 占用一个名额」合并成一个不可分割的原子操作atomic lease**请求进来时直接尝试原子性地扣减一个名额扣减成功才继续失败就直接判定不满足资格、返回边缘。这样不管多少请求同时涌进来最终拿到名额的最多只有 N 个不会超发。 这类「资源租约」的设计模式不是 P2P 专属。任何「有限资源、高并发申请」的场景都会遇到同样的问题用的是同一类解法。### 5. 信令走控制面媒体走直连直连建立前Offer、Answer、ICE candidate 这些信令消息都要经过控制面转发。但控制面在这里做的只是**转发和身份校验**不参与媒体数据本身呼应架构篇的「控制面不碰媒体」。校验的核心是确认信令双方的**身份和会话确实匹配**防止有人把信令注入到别的直播流或别的用户的会话里。信令通过之后媒体数据走推流端和播放端之间的直连通道完全不经过控制面。### 6. 诚实的现状一次真实的 P2P 直连跑通约 70ms是特定实验条件下的端到端观测不是理论值也不是所有网络的承诺。连接可靠性近期有改善但**「在测过的这些网络组合下能连上」不等于完成了覆盖各类 NAT 组合、CGNAT、多运营商、IPv4/IPv6、UDP 受限和网络切换的系统性验收**。设计完成、单次连通验证、大规模系统性验收是三个不同阶段。---## 第二部分节点池调度### 7. 问题为什么「角色 区域」两维不够用很多系统的节点筛选只有两个维度**角色**Origin 还是 Edge和**区域**region/city。同一角色的所有在线节点被当成一个大资源池不区分这批机器归属谁、给谁用。下面这些场景就不够用了- **大客户/高优先级应用需要独占一批节点**或同一租户的多个应用需要共享一批保留资源避免被其它租户挤占- **不同硬件规格/编码能力的节点要分开调度**比如高规格节点只服务特定套餐- **新版本或刚上线的节点需要先放进「灰度池」验证**不能立刻进入生产候选- **运维希望按团队或合同边界拆分节点的管理权限和可见范围**。共同点是需要在「角色 区域」之上再加一维**节点池**作为资源的隔离边界。这一维不是给人分三六九等的「标签」而是一条**「谁能被看见」的硬约束**。### 8. 机制池是第一层过滤池内只按空闲度择优一次调度请求的处理顺序调度请求role, appId│▼解析请求所属的池app 显式绑定优先否则按租户/用户默认池都没有 → 共享池│▼按池过滤只保留属于该池的可用节点不属于该池的节点从这里开始就不在候选里│▼池内排序剩余容量最大优先│▼返回最优节点主池无可用候选 → 进入回退判定关键点是**池过滤发生在最前面**。池内的排序再怎么变都改不了「别的池的节点压根不在候选里」这个事实——这就是隔离的落点。节点池的归属通常是**部署时静态确定**的属性和区域一样不是每次调度都变的动态数据。### 9. 池归属由谁决定从「节点自报」到「控制面权威分配」这里有一段值得讲的演进。早期设计是**节点自报**节点在部署配置里写一个池 ID注册时上报给控制面。但当系统允许自建节点用较低信任度的凭证注册之后这套做法必须收紧**如果允许节点自己上报「我属于哪个池」一个自建节点理论上可以谎称自己属于别人的池绕开隔离边界。** 所以池归属收回到控制面权威记录——节点创建时由控制面写入归属节点对此没有发言权也改不了。这是一个很好的安全设计例子**隔离承诺要成立就必须把「声明归属」的能力从不可信的一方手里拿走。**### 10. 回退语义fail-closed不静默突破隔离这是整套机制里最需要讲清楚的一条。假设某个应用绑定了池但池内**没有可用候选**怎么办注意「没有可用候选」包括无节点、节点离线或不健康、处于排空状态、没有节点满足准入条件等不能只用「节点数量为零」判断。两种做法语义完全不同- **静默回退到共享池**调度「永远不失败」但隔离承诺被悄悄破坏了——业务方以为自己在用独占资源实际已经跑到公共池上成本核算和隔离预期全部错位。- **显式配置 显式告警**回退是一个需要**主动打开**的开关并且**默认关闭**。推荐后者默认 fail-closed主池内没有可用候选│▼允许回退 ?┌────┴─────┐false true│ │▼ ▼调度失败 在共享池重新计算候选 池耗尽告警 ├─ 有候选回退成功 记录一条可区分的回退事件└─ 无候选调度失败设计哲学很简单**隔离是一条承诺静默回退就等于承诺失效。** 所以它必须由业务方显式打开而且无论哪种结果都要留下痕迹——要么失败被告警要么回退被记录绝不能「看起来一切正常」。### 11. 两个「看不见」的用法移出调度与灰度池池机制里还藏着两个很实用的边界用法靠的是同一个原理——**只要一个池没有任何应用解析到它池里的节点就不会被任何调度选中**- **unassigned移出调度**一个专属的哨兵池。任何应用都不会解析到它所以被置为该池的节点等于被干净地「拔出调度」但进程还活着、连接还在可以随时再搬回来。这就是后台「移除节点」按钮背后的动作。- **灰度池**把新节点注册到一个暂时没有任何应用绑定的池里它就自然地「隐身」在生产调度之外——不参与候选但心跳、容量上报、健康检查都照常。等验证通过再搬进真正的生产池不需要为灰度单独发明一套流程。### 12. 诚实的边界- **池约束的是「控制面把请求分给谁」**不是天然的物理隔离。共享池内部仍可能由多个应用共用节点网络和进程边界不会自动隔离。- **它通常只覆盖 Origin/Edge 两个角色。** 录像、跨区域桥接等角色如果走独立调度路径就不在这套隔离机制的管辖范围内。- **「设计能力」和「产品暴露的能力」是两回事。** 设计里可以支持任意命名池、池优先级、节点多对多等灵活形态但产品往往把常见诉求收敛成「一人一池 可选的 app 显式绑定」这样更简单、更保守的形态。---## 小结- **P2P 直连**标签预筛选只负责「值得尝试」ICE 检查才决定能不能连上不承诺数学意义的 100%用顺序回退而不是并行竞速有成熟边缘路径时可不部署 TURN但要接受 CGNAT 用户没有中继退路。- **名额分配**用原子租约防止并发超发。- **信令走控制面、媒体走直连**控制面只转发和校验身份。- **节点池**在角色/区域之上加一维隔离边界先按池过滤、池内按空闲度择优。- **归属权威化**池归属由控制面决定节点不能自报防止绕开隔离。- **回退 fail-closed**默认不回退显式打开且留下痕迹绝不静默突破隔离。下一篇我们把镜头拉远**横向扩容和低延迟到底是什么关系**以及当流量不是缓慢上涨、而是突然爆发时该怎么办。---**系列导航**- 上一篇[06 · 可观测性](06-可观测性-端到端延迟稳定性与画质.md)- 下一篇[08 · 横向扩容与突发高并发](08-横向扩容与突发高并发.md)- 返回[系列导览](README.md)