12. v2.0.0-alpha3 幽灵多端与频道消息升级说明¶
本文说明升级到 v2.0.0-alpha3 时,从旧的幽灵“单端、单频道”模型迁移到
Session、多收单发和频道消息历史模型的部署、验证与回滚步骤。升级不放宽标准
UDP 实体设备的单端约束;旧幽灵协议不再兼容。
1. 行为变化¶
| 范围 | 升级后行为 |
|---|---|
| 标准 UDP 实体设备 | 继续以 owner_id + ssid 唯一在线;同一实体第二端仍被拒绝 |
| 旧 UDP/WS 幽灵设备 | raw-JWT、无实例 ID 或缺少必需能力时拒绝认证 |
| UDP/WS 幽灵设备 | 以稳定 client_instance_id 和临时 session_id 隔离,同账号可多端在线 |
| 幽灵路由 | 每个 Session 一个 tx_group_id,rx_group_ids 上限由 GhostSessions.MaxSubscriptionsPerSession 配置;发送频道自动包含在接收集合中 |
| 实时消息 | 支持来源频道元数据;下行 Type 4/5 的 Reserved 为大端 source_group_id |
| 历史消息 | /api/groups/:id/messages 按频道 ACL 查询完整历史;/api/comm-records 继续用于本人发信记录和审计 |
默认上限为每账号 8 个在线幽灵 Session、每 Session 16 个接收频道,可通过 GhostSessions 配置调整。客户端必须同时声明 multi_receive_v1 与 source_group_v1,边缘节点必须同时协商多 Session 功能位 8 和恢复票据功能位 9。
幽灵多端和多收能力始终启用,不提供账号、平台白名单或全局关闭开关。部署只需要在中心和边缘配置一致的 Session 数与订阅数上限。
频道消息列表默认每页 50 条、最多 100 条;账号每分钟 120 次、IP 每分钟 300 次,最多同时处理 32 个查询。以上值通过 MessageAPI 配置调整。账号/IP 超限返回 429 和 Retry-After,并发槽满返回 503 message_api_busy。
2. 升级前准备¶
- 备份 MySQL、对象存储和
config.yaml,并记录当前中心、边缘和客户端版本。 - 在副本数据库执行迁移和消息查询验收,确认
comm_records的数据规模、字符集,以及投递快照表、索引和补录时间。 - 安排写入维护窗口。
comm_records回填会分 1,000 行批次执行,但新增列和复合索引仍可能在大表上持锁。 - 确认所有中心和边缘节点时钟同步,节点凭据有效,控制链当前健康。
- 确认所有受支持客户端已经具备实例 ID、Session tag、多收和来源频道能力;服务端切换后旧幽灵客户端将无法连接。
- 如果 UDP 幽灵客户端跨不可信网络接入,先部署 WireGuard/IPsec 等加密隧道;
session_tag只做运行时绑定,不提供加密、消息认证或媒体重放防护,详见协议文档中的 UDP 幽灵 Session 威胁模型。
3. 数据库迁移¶
停止旧服务写入后,在新版本二进制上显式执行一次迁移:
./draarl -c ./config.yaml -auto-migrate
Compose 部署可在维护窗口将 .env 中 DRAARL_AUTO_MIGRATE 临时设为 true,启动新镜像并确认迁移成功;后续可恢复为 false。迁移会:
- 为
comm_records增加message_type、text_content和发送者快照字段。 - 创建
comm_record_delivery_groups投递快照表,以及普通消息和类型消息各自的频道时间游标索引。 - 为旧通信记录按仍存在的来源组补录一条投递快照,并补齐快照时间和消息类型,不猜测历史虚拟互联范围;来源组已删除的孤儿记录会保留、记录跳过数量且不创建无效快照。
- 为管理员全局发信记录和按发送用户筛选增加状态时间游标索引。
- 把旧
audio_path='text:...'记录转换为显式文本消息,并按 1,000 行批次回填可恢复的发送者快照。 - 创建
ghost_client_preferences与ghost_client_subscriptions,保存每个安装实例的发送/接收偏好;这些表不代表在线状态。 - 补充节点和设备会话投影所需字段。
迁移完成后至少检查:
SHOW COLUMNS FROM comm_records;
SHOW CREATE TABLE comm_record_delivery_groups;
SHOW INDEX FROM comm_record_delivery_groups WHERE Key_name = 'idx_delivery_group_record';
SHOW INDEX FROM comm_record_delivery_groups WHERE Key_name IN ('idx_delivery_group_cursor', 'idx_delivery_group_type_cursor');
SHOW INDEX FROM comm_records WHERE Key_name IN ('idx_comm_records_status_start_id', 'idx_comm_records_user_status_start_id');
SELECT COUNT(*) FROM comm_records WHERE group_id IS NOT NULL;
SELECT COUNT(*) FROM comm_record_delivery_groups;
SHOW CREATE TABLE ghost_client_preferences;
SHOW CREATE TABLE ghost_client_subscriptions;
SELECT message_type, COUNT(*) FROM comm_records GROUP BY message_type;
不要手工把偏好表中的记录标记为在线。暂时保留 user_device_preferences,新实例首次创建偏好时仍可用其中的平台最后频道作为初始化值。
4. 滚动升级顺序¶
- 数据库兼容迁移:完成备份、停写和
-auto-migrate,确认回填及索引成功。 - 中心服务端:先升级中心。新中心仍接受旧节点承载实体设备,但拒绝旧幽灵认证。
- 边缘节点:逐个升级并确认控制链重新建立。新中心/边缘应同时协商
ghost_multi_session(节点功能位 8)和ghost_recovery_ticket(节点功能位 9)。 - Web 客户端:发布带稳定实例 ID、Session routing、来源频道和多频道混音的 Web 包。
- 原生客户端:逐个平台发布版本化 UDP 认证和来源频道支持;未升级客户端不再允许接入。
- 观察:确认会话上限拒绝、无效 session tag、路由变更、fan-out 去重和消息查询耗时没有异常,再扩大客户端覆盖率。
旧边缘不支持功能位 8 或 9 时仍可服务标准实体设备,但不能承载任何幽灵 Session。不得通过关闭能力校验让 Session 静默降级,也不保留无票据的旧幽灵重确认路径,否则同账号会话可能互相覆盖或被伪造恢复。
5. 发布后验证¶
5.1 服务端和数据库¶
- 执行完整 Go 测试、竞态检测和
go vet。 - 使用真实 MySQL 执行通信记录权限、幽灵偏好、路由原子性、频道消息 HTTP E2E 和百万消息查询计划测试。
- 构建 Web,并确认
/radio不出现旧的同账号单端阻断。 - 对无类型过滤和带
message_type过滤的频道消息查询分别执行EXPLAIN ANALYZE,确认从idx_delivery_group_record进入投递快照,并记录关联、排序和过滤成本。
5.2 单机功能¶
- 同账号打开两个 Web 实例,确认
/api/radio/sessions返回两个不同session_id。 - 为实例 A 配置
tx=群组1, rx=[群组1,群组2],实例 B 配置独立路由;任一端断开不得影响另一端。 - 从群组 1、2 分别发送文本和语音,确认消息按
source_group_id进入正确频道,Web 可独立开关和调整频道音量。 - 请求
/api/groups/:id/messages,确认返回频道内其他发送者的已完成消息;请求无权限私有群组应返回403。 - 使用同一标准 UDP 实体身份从第二地址上线,确认新端被拒绝且旧端保持在线。
5.3 中心与边缘¶
- 在两个边缘分别接入同账号、同平台的幽灵 Session,确认中心同时保留两条活动路由。
- 订阅多个频道和互联域,确认一次消息对每个目标 Session 只投递一次,并保留真实来源频道。
- 更新和恢复接收订阅,确认边缘缓存立即失效并按新路由投递。
- 把其中一个安装实例迁移到第三个边缘,确认只迁移该 Session。
- 中断并恢复控制链,确认 Session 重新确认后仍属于原实例,另一个 Session 不被撤销。
- 依次重启中心和两个边缘,确认中心重启保留原 Session/tag,边缘重启使旧 tag 失效且只要求目标实例重新认证,在线数按两条边缘幽灵 Session 统计。
- 连续多次中断同一边缘控制链,再让网络分区超过边缘 grace;确认短抖动重确认后 Session 数不增长,长分区清理本地 Session,恢复控制链后必须重新认证且在线数回到真实 Session 数。
6. 回滚¶
6.1 应用和节点回滚¶
- 本版本没有幽灵多端/多收关闭开关;需要紧急回滚时,先停止新的幽灵接入并让现有 Session 断开或自然下线。
- 先把边缘回滚到目标版本,确认实体设备接入正常,再回滚中心。
- 保留新表、新列和索引;旧 GORM 模型通常会忽略额外结构,删除结构会使再次升级和已保存路由偏好不可恢复。
- 只有明确重新启用旧幽灵协议时才能回滚 Web;否则旧 Web 客户端将无法连接。
仅回滚二进制不能完整恢复旧文本数据语义:迁移已把旧 audio_path='text:...' 移入 text_content,而升级后新写入的文本也不会生成旧前缀。需要让旧版本继续展示全部文本时,必须恢复升级前数据库备份或使用理解新字段的兼容回滚版本。恢复整库备份会丢失备份时间点之后的数据,应先导出新增记录并制定合并方案。
6.2 不应执行的回滚操作¶
- 不要直接删除
ghost_client_preferences或ghost_client_subscriptions。 - 不要删除
comm_records新字段或复合索引后继续运行新服务。 - 不要把超长
text_content无条件写回varchar(255) audio_path,这会截断消息。 - 不要在旧中心仍运行时强制新边缘接受多端 Session。
7. 已移除入口¶
服务端不再提供 /api/radio/conflict、旧 /api/radio/group 或 PUT /api/radio/ssid 切组语义。所有切组与多收更新统一使用 /api/radio/sessions/:session_id/routing。/api/comm-records?group_id=... 只筛选本人发信记录,频道历史只使用 /api/groups/:id/messages。