Skip to content

路线 B|C++ Linux 游戏服务端 ​

B5|Distributed Systems:RPC、消息与容错 ​

适用基础: B3、B4 目标: 在多服务环境中正确处理超时、重试、幂等、限流和最终一致性。


一、分布式错误是默认状态 ​

网络延迟、进程重启、重复消息、部分成功、时钟偏差和依赖过载都可能发生。设计目标不是“永不失败”,而是失败可观察、可恢复、不会扩大损失。

二、RPC 与消息队列 ​

同步 RPC 适合需要即时结果的短请求;消息队列适合解耦、削峰和异步事件。每条消息要有 ID、版本、来源、时间和幂等处理策略。

同步并不意味着可靠,异步也不意味着最终一定到达。RPC 调用方需要处理未知结果,消息生产者要处理发送成功但事务失败、事务成功但发送失败。Outbox 模式通过在同一数据库事务中保存业务变化和待发事件,再异步投递,减少双写不一致窗口。

三、Timeout、Retry、Idempotency 的组合语义 ​

超时不是一个整数,而是连接、排队、读写和业务总预算的分解。重试会放大依赖压力,必须配合指数退避、抖动、次数上限和熔断。幂等键让重复请求只产生一次业务效果,但还要保存请求结果或可查询状态,因为超时并不能证明服务端没有成功。没有幂等保证就不要自动重试写请求。

四、Circuit Breaker 与 Rate Limit ​

依赖连续失败时熔断,避免雪崩;恢复探测必须有限。限流按用户、IP、服务和资源区分,拒绝也要有错误码和监控。

熔断器通常经历 Closed、Open、Half-Open:正常放行,失败达到阈值后快速失败,冷却后只允许少量探测。限流常用固定窗口、滑动窗口、漏桶或令牌桶;令牌桶允许有限突发,漏桶更强调稳定输出。选择算法要匹配业务峰值,而不是只看平均 QPS。

五、服务发现与健康检查 ​

实例注册、租约、健康状态和下线要有时间边界。客户端不能无限缓存地址;发布和扩容期间要处理连接迁移。

负载均衡可以按轮询、最少连接、一致性哈希或权重选择实例。无状态请求容易迁移,有状态房间需要稳定路由或显式迁移。服务发现告诉你“实例在哪里”,不能证明该实例对当前玩家状态负责。

六、订单事件链的正确性 ​

Gateway→Game→Reward 的链路中,requestId 是业务相关性标识,幂等键是防止重复副作用的状态约束,消息 ID 是传输层去重线索,三者不一定相同。Reward 超时后可能已经发奖,调用方只能通过查询或幂等重试确认最终状态。

七、CAP 与一致性 ​

CAP 不是简单的“三选一配置”。在网络分区时必须在可用和一致之间做业务取舍;游戏货币、排行榜、在线状态和聊天可以采用不同策略。

时间与顺序 ​

跨机器墙上时钟可能偏移,不能单独用时间戳证明全局先后。业务顺序常依赖单调版本、数据库序列、逻辑时钟或同一 owner 的串行日志。分布式 ID 需要考虑唯一性、趋势有序、时钟回拨、节点标识和可用性。

最终一致并不等于“以后会自动好” ​

必须定义收敛机制:重试、补偿、对账、版本比较或重新构建。若没有负责发现和修复差异的流程,“最终一致”只是没有截止日期的数据错误。

可观测性 ​

跨服务排查需要统一 trace/request ID、服务版本、重试次数、幂等命中、队列延迟和依赖状态。仅记录本服务耗时会把排队、网络和下游等待隐藏起来。

八、练习与答案 ​

1. 为什么 timeout 后不能直接判定操作失败? ​

请求可能已经在服务端成功,只是响应丢失。必须用 requestId 查询或依赖幂等重试确认最终状态。

2. 消息至少一次投递意味着什么? ​

消费者可能收到重复消息,处理必须幂等;“恰好一次”通常是端到端业务效果,而不是网络传输天然保证。

九、阶段输出 ​

提交一份 RPC/消息契约、超时预算、重试矩阵、幂等设计和故障注入报告。