棋牌AI事件流协议设计:幂等、断线与续传
棋牌AI接入后的线上故障,八成出在事件流协议而非模型:重复决策、事件乱序、断线后状态漂移。协议要回答四件事——事件带单调递增seq、决策带Idempotency-Key幂等、断线30秒内凭seq续传无感恢复、客户端按指数退避重连。REST与WebSocket各管一段,边界清楚才不打架。本文给出事件信封与重连参数。
棋牌AI接入后的线上故障,八成出在事件流协议而非模型:重复决策、事件乱序、断线后状态漂移。协议要回答四件事——事件带单调递增seq、决策带Idempotency-Key幂等、断线30秒内凭seq续传无感恢复、客户端按指数退避重连。REST与WebSocket各管一段,边界清楚才不打架。本文给出事件信封与重连参数。
接入棋牌AI之后的线上故障里,模型原因占比很低,多数是事件流协议没设计好:网络抖动导致的重复决策请求、断线重连后的状态漂移、事件乱序引发的非法动作。这些问题在单机演示里永远不会出现,一旦上了真实移动网络,每天都会来。
会话内每条事件带单调递增的seq,这是幂等、续传、乱序三个能力共同的地基:客户端凭seq丢弃迟到事件,服务端凭seq判断续传起点,审计凭seq重建完整对局。seq必须按会话隔离、从零开始、严格递增——任何“用时间戳当序号”的捷径,都会在时钟回拨那天连本带利地还债。
移动网络下“请求发出去了但响应丢了”是常态,客户端必然重发,没有幂等防护就会同一手牌处理两次。约定是请求方为每个决策生成唯一Idempotency-Key(会话ID加seq即够),服务端按键去重:重复请求返回首次结果而不是重新决策,AI的行为天然一致,审计也不会出现分叉。
// 决策请求/响应信封(示意)
{
"seq": 1042,
"session_id": "s_88213",
"type": "decision",
"idempotency_key": "s_88213:1042",
"payload": { "legal_actions": ["play", "pass"], "context_ref": "ctx_5521" },
"result": {
"action": "play",
"latency_ms": 42,
"think_ms": 2600,
"emotion": "focused",
"chat_suggest": null,
"audit_ref": "a_5521"
}
}断线续传的体验目标是玩家无感:30秒内的断线,重连后凭客户端持有的最后seq向服务端拉取增量事件,补齐后从断点继续,牌局状态与服务端保持一致;超过30秒转入快照重建路径,由服务端下发全量状态。30秒这个阈值来自真人行为——半分钟以内的网络抖动,真人是不会离桌的。
乱序处理规则要简单到可以穷举测试:seq小于本地已见最大值的事件直接丢弃,等于本地状态则幂等跳过,大于则缓存等待补洞。重连退避用指数加抖动(1秒起步、逐次翻倍、上限30秒),避免服务端抖动时重连风暴雪上加霜;退避超过断线阈值的客户端应直接走快照重建,不再尝试增量续传。
REST与WebSocket各管一段才不打架:REST管无状态动作——创建会话、拉取配置、上传结果、查询审计;WS管会话内的事件流——牌局事件、决策下发、心跳。同一个动作绝不允许两条通道都能触发,否则幂等键要跨协议维护,复杂度翻倍。我们的API同时提供REST与WS,边界即按此划分,每类操作只出现在一个通道。
30 天全功能免费试用,5 分钟接入首个AI牌局。开通试用请添加商务微信 kxlin0101 或发送邮件至 skyin.lewis@gmail.com。