Files
dsh_ai1net_server/交付物/用户组网群聊可行性评估-20260925.md
T
admin c1b5e4d966 chore(工作区): 全量入库 + 补齐 .gitignore(以工作区为准)
- 变更规模:新增 514 / 修改 62 / 重命名 155 / 删除 4(归档重组与文档轮次)
- .gitignore 修:`归档/**/db-cwd归一-备份-*/` —— 原规则写绝对层级(归档/db-cwd归一-…),
  目录搬进 归档/配置与备份/ 后**静默失效**,43 MB 的 DB 备份又变成未跟踪
- .gitignore 补:嵌套 git 内部数据(归档/内嵌git-20261008/、归档/skills-git-旧线-20261007/dotgit-原样移出/)
- .gitignore 补:运行态与部署副本(.workbuddy/collab/、.workbuddy/tools/、.workbuddy/.load-pending、.workbuddy/tmp-*)
- .gitignore 补:备份件(*.bak-*)
- 未跟踪文件从 2190 降到 890(其余为 归档/ 归档件与 .workbuddy/memory/ 知识文件,按口径入库)
2026-10-10 23:13:22 +08:00

116 lines
8.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 交付物 · 用户组网群聊可行性评估(环形网络 · 2026-09-25)
> **工作区**:`E:/ProgramData/AIProject/aliyun-dsh-server`|**基线**:`D:/github/dsh_shenxian`
> **问**:能否让用户组成**环形网络**做群组聊天、只有数据保存到服务器?群聊的技术特性(对话顺序、历史记录等)是否满足?
> **方法**:① 只读取证现有覆盖网络能力 ② 联网核对业界群聊技术特性 ③ 逐条对照
---
## §0 判定(先说结论)
1. **「环形拓扑」不推荐** —— 三个硬伤(延迟随人数线性增长、单点断环、两两打洞成本 `O(N²)`),业界群聊无此形态。
2. ✅ **但「用户之间点对点组网」本身可行,且底座已经在平台里** —— 打洞、分网隔离、直连优先回落、组密钥**都已落地**(`src/net/relay/direct/` + `content/`)。
3. 🔴 **顺序与历史这两条,恰恰要求服务器必须是权威** —— 所以正确形态不是"P2P 取代服务器",而是
**服务器定序落库(权威)+ 用户间直连(加速投递)**。你说的"只有数据保存到服务器"与这个形态**完全一致**。
4. 🔴 **但有一个前置硬阻塞**:**云安全组未放行 ⇒ 真机打洞成功率实测 = 0** ⇒ 现在做 P2P 投递会**全部回落中继,等于没做**。
## §1 先分清两个概念(你的设想里混在一起)
| 说法 | 含义 | 判定 |
|---|---|---|
| **环形拓扑**(A→B→C→…→A) | 严格指消息沿环逐跳转发 | ❌ 不推荐(§2) |
| **点对点组网**(成员之间直连) | 彼此能直连,拓扑不限 | ✅ 可行,底座已在(§3) |
⇒ 我按"你要的是**用户之间能直连**"来评估;若你确实要严格的环,§2 给出为什么不建议。
## §2 为什么不推荐"环"
| 硬伤 | 具体 |
|---|---|
| **延迟随人数线性涨** | N 人环上,一条消息到最远的人要 **N/2 跳**;10 人尚可,100 人就是 50 跳的排队 |
| **单点断环** | 环上任意一人掉线/断网 ⇒ 环**断成两半**(要补救就得双向环 + 重路由 ⇒ 又回到全互联) |
| **两两打洞成本 `O(N²)`** | 每人要与其他所有人建直连 ⇒ 打洞次数按人数平方增长。🔴 这正是你们已记录过的 `O(N²)` 扇出问题 |
| **没有定序者** | 环只是拓扑,**不提供顺序**(见 §4-①) |
> 业界群聊的拓扑只有两种:**星型(服务器/中继为中心)**、**小群全互联(≤ 几十人)**。没有用"环"的。
## §3 已具备的能力(好消息 · 实测口径)
| 能力 | 落点 | 状态 |
|---|---|---|
| **打洞 / 直连** | `src/net/relay/direct/{candidate,punch}.ts` | ✅ 已落地(离线 NAT 模拟器 `24/24` 成功,p50 建连 155 ms) |
| **直连优先、回落中继** | `content/runtime.ts` 的 `PEER_CHANNEL_ORDER = ['direct','wss']` | ✅ 双通道已落地生产 |
| **块 id 复算闸门** | 取回的块必须复算 id 才对 | ✅ 防"假绿" |
| **分网隔离(用户可组独立网)** | `dialers: Map<networkId, Set<hostId>>`,**没列到的一个都拨不动(默认拒绝)** | ✅ **零代码**:新增一个 `networkId` 即可(`ops` / `u:<userId>` / 自命名) |
| **一键加入 + 分组准入** | `overlay-node-join.cjs` / `overlay-node-admit.cjs` | ✅ 已落地 |
| **组密钥加密** | 按 `(network, group)` 的 AES-256-GCM,组内密文一致 | ✅ 已落地 |
| **中继带宽节省(实测)** | `1 − 组数/节点数` ⇒ 4 人组 **75%**、16 人组 **93.75%** | ✅ 内容面实测 |
| 🔴 **云安全组放行 UDP** | 47 / 106 入站 `21100/21115` 仅放对端 `/32` | ❌ **未实施(唯一在册红)** ⇒ 真机打洞 **发 21 收 0** |
⚠️ **一处关键事实**:上述 P2P 能力目前**只用在「内容面」**(插件的静态内容分发,按块)。
**「消息面」零 P2P** —— `src/im/hub.ts` 的扇出只有"本进程逐连接"与"交给外部连接层"两条路。
⇒ 要在群聊里用 P2P,需要**把消息面接上这套直连**(新增接线,不是零成本)。
## §4 群聊技术特性逐条对照(联网核对 · 5 个来源一致)
### ① 对话顺序 —— 🔴 这条决定了"服务器必须权威"
业界一致结论(OpenIM / WhatsApp 设计 / 系统设计手册,五篇同口径):
> **会话内必须强顺序一致**(同一会话所有参与者看到的顺序严格一致);
> 做法 = **服务器在落库那一刻分配单调递增 `seq`**,客户端**按 seq 渲染、⛔ 不按本地到达时间**。
> **全局顺序不需要**(没人在乎两个不同群的先后)。
> 纯客户端时间戳**做不到**——时钟漂移 + 同毫秒并发无定序。
🔴 **推论**:`seq` 必须由**落库那一步**产生 ⇒ **定序点只能在服务器**。这否掉了"纯 P2P 群聊"。
✅ **你们已经这么做了**:`messages.seq` = DB 分配的房内单调整数(权威序),
`lamport` 只在**同一 seq 冲突**里做 tie-break、⛔ 不参与最终排序 —— 与业界口径**逐字一致**。
### ② 历史记录 —— 你说的"数据存服务器"正好满足
- 历史 = 按会话分区、按 `seq` 聚簇的**追加式日志**(append-mostly)。
- 离线补拉 = 客户端带上自己的游标,取"seq 大于游标"的那一段。
- ✅ 现有:`resume`(游标补拉是断线重连的**唯一**补齐路径)。
- 🔴 **如果消息只走 P2P 而不落库 ⇒ 历史直接缺失** ⇒ **每条消息都必须落库**,P2P 只做加速。
### ③~⑨ 其余特性对照表
| # | 特性 | 环网能否满足 | 现有平台 | 说明 |
|---|---|---|---|---|
| ③ | **多设备同步** | ❌ | ⚠️ 部分 | 每设备需各自游标(`last_read_seq` / `last_delivered_seq`);现只有单游标补拉 |
| ④ | **成员变更边界** | ⚠️ 要重构环 | ✅ 成员表 | 新成员能否看历史、踢出后是否即时停收 —— 现有成员表可判 |
| ⑤ | **在线态 presence** | ❌ 环上要全互联 | ✅ | 已有心跳 + 1 s 批合并 + 收敛(防惊群) |
| ⑥ | **群 E2EE** | ⚠️ 密钥分发复杂 | ✅ 方向已定 | 🔴 必须用**群共享密钥**(Megolm 化),⛔ 不能对 N 人各加密一次(`O(N²)`)—— 已在册 |
| ⑦ | **扇出成本** | ❌ `O(N²)` 连接 | ✅ | 中继 = 一次发布;P2P 反而放大连接数 |
| ⑧ | **单点故障** | ❌ 断环 | ✅ | 两台中继冗余 + 抖动切流(实测切换 18.8–20.9 s) |
| ⑨ | **CAP 取舍** | — | ✅ 已选 | 业界做法 = **最终一致 + 会话内强序**;现有实现同向 |
## §5 若要做,正确形态(架构选型 · 我已定,可推翻)
**不做环。采用「服务器定序 + 直连加速」**:
```
发消息 → 服务器落库(分配 seq = 权威序)──► 历史 / 补拉 / 多设备 都以此为源
└─► 同时经覆盖网络直连推给在线的其他成员(能直连就直连,不能就回落中继)
```
- 复用**已有**的 `direct/`(打洞)+ `content/runtime.ts` 的**双通道模式**(直连优先、回落 wss)+ **块 id 复算闸门**那套防假绿思路。
- 新增工作量:**消息面接直连**(现在只有内容面接了)+ 直连失败回落 + 防重放/去重。
- 收益(按已有实测外推):中继带宽节省 `1 − 组数/节点数`;**消息面需另测**(⛔ 不是照搬内容面读数)。
### 🔴 顺序约束(必须先做的前置)
**云安全组不放行 ⇒ 真机打洞成功率 = 0** ⇒ 先做消息面 P2P 会**全部回落中继 = 白做**。
⇒ 顺序是:**① 开云安全组 → ② 复测真机打洞率 → ③ 再决定要不要做消息面直连**。
⚠️ 云安全组只能**你在控制台操作**(我无云凭据,不得索取)。
## §6 一个更容易被忽略的前提
即便 P2P 建成,**"用户之间"要先在同一个覆盖网络里** —— 现有分网隔离是**默认拒绝**:
不在白名单的节点一个都拨不动。所以"让用户组成网络"这件事本身 = **先给这些用户建一个网 + 逐个准入**(P1 已能一键加入)。
## §7 边界
⛔ 本件**零代码改动**、未部署、未 commit/push、未动 47/106、未改任何别的工作区。
⛔ **未登记下一棒**(路线选择 + 云安全组操作均需你定)。