Files
bl/docs/pvp-login-rpc-match-design-2026-04-09.md
xinian d2cd601802
All checks were successful
ci/woodpecker/push/my-first-workflow Pipeline was successful
更新说明
2026-04-09 13:11:59 +08:00

6.6 KiB
Raw Blame History

PVP Match Via RPC, Battle Via Redis

目标

本次调整先不解决 login 更新期间的排队保活和补偿问题,只收敛到一个更简单、可控的方案:

  • 匹配请求走 logic -> login 的同步 RPC
  • 对战过程仍走 logic 本地战斗 + Redis 转发战斗指令
  • login 不可用时,logic 直接返回“匹配服务不可用”
  • 前端通过轮询重新发起 / 更新匹配请求,不在后端保留离线补偿队列

这个方案的核心是:先把“能否立即判断匹配服务可用”做好,不继续依赖 Redis PubSub 做匹配入口。

当前现状

现有匹配入口

现有跨服协调

  • logic 侧订阅 PVP Redis topic 的入口在 common/rpc/func.go
  • PVP 匹配状态当前存在 logic/service/fight/pvp/service.go 的 manager 内存里:
    • queues
    • lastSeen
    • localQueues
    • sessions
    • userSession

现有 RPC 能力

当前问题

Redis PubSub 适合“广播消息”,不适合“同步判断服务是否可用”。

如果继续让匹配入口走 PubSub

  • logic 无法在请求当下知道 login 是否真能处理
  • login 更新、重启、未订阅时,匹配请求可能直接丢失
  • 前端即使轮询,也只是重复投递,不能精确表达“当前匹配服务可用/不可用”

收敛后的职责划分

login

login 只负责匹配控制面:

  • 接收 logic 发来的同步匹配 RPC
  • 判断当前匹配服务是否可用
  • 维护匹配队列
  • 找到对手后,记录 match 结果
  • 再通过 Redis 或其他异步方式通知对应 logic 开始 Ban/Pick / Battle

logic

logic 只负责:

  • 接收前端匹配请求
  • 同步 RPC 到 login
  • RPC 失败时立即返回“匹配服务不可用”
  • RPC 成功时返回“排队中”
  • 收到 match 结果后负责真正 fight.NewFight(...)
  • 对战期间继续使用现有 Redis topic 转发战斗指令

Redis

Redis 只保留在“对战消息面”:

  • match_found
  • ban_pick_submit
  • battle_command
  • packet_relay
  • session_close

也就是说:

  • 匹配入口走 RPC
  • 对战过程走 Redis

推荐目标链路

1. 前端加入/更新匹配

前端定期轮询 logic 的加入/更新接口。

logic 处理流程:

  1. 校验玩家当前战斗状态
  2. 同步调用 login 的匹配 RPC
  3. 如果 RPC 成功:返回排队中
  4. 如果 RPC 失败:清理本地匹配状态,返回匹配服务不可用

2. login 完成匹配

login 维护排队队列和匹配结果,匹配成功后:

  1. 确定 host / guest 所在 logic
  2. 通过 Redis 通知两个 logic
  3. host logic 开战
  4. guest logic 设置远端代理并进入 Ban/Pick 或战斗态

3. 对战期间

继续复用当前 logic/service/fight/pvp/service.go 内的 Redis 指令转发模式:

  • 战斗操作通过 Redis topic 转发
  • host logic 维持真实战斗对象
  • guest logic 维持 remote proxy

失败语义

本阶段不做补偿,不做离线保队列。

login 不在线

如果 logic -> login RPC 调用失败:

  • 本次匹配直接失败
  • logic 清理本地匹配状态
  • 返回前端“匹配服务不可用”

前端轮询停止

如果前端不再轮询:

  • 视为用户不再持续请求匹配
  • logic 不负责继续保活
  • 是否从 login 队列移除,由 login 的超时策略决定

login 更新中

如果 login 正在更新:

  • logic 的同步 RPC 会失败
  • 前端当前轮询会收到“匹配服务不可用”
  • login 恢复后,前端下一轮再发起匹配

这是本阶段明确接受的行为,不在后端做补偿。

最小实现建议

一、先增加 RPC 健康/匹配接口

common/rpc/rpc.go 增加面向 logic -> login 的 RPC 方法。

建议最小接口:

  • MatchJoinOrUpdate(PVPMatchJoinPayload) error
  • MatchCancel(userID) error

如果需要单独健康检查,也可以加:

  • MatchPing() error

但在最小方案里,MatchJoinOrUpdate 自身就可以承担健康检查职责。

二、logic 的匹配入口改为同步 RPC

改造 logic/controller/fight_巅峰.gologic/service/fight/pvp/service.go

  • 入口不再直接发布 queue_join
  • 先发 RPC 到 login
  • 成功才更新本地匹配状态
  • 失败直接返回错误
  • 取消匹配时通过 MatchCancel 做 best-effort 清理

三、保留 Redis 对战链路

logic/service/fight/pvp/service.go 之后的 Redis 消费、match result 处理、Ban/Pick、战斗 relay 不需要一次性重写,可以继续保留。

调整重点是:

  • 不再让匹配入口依赖 PubSub
  • 让对战过程继续走 Redis

对前端的要求

前端不要无脑重复“新 join”而是按“轮询更新匹配状态”处理。

建议行为:

  1. 首次点击匹配时发一次加入
  2. 匹配中每隔 3~5s 轮询一次更新
  3. 如果返回“匹配服务不可用”,前端退出匹配态并提示
  4. 如果返回“已匹配/进入 Ban/Pick”前端切换到对应界面

本阶段不做的事

以下内容明确不在这次最小改造内:

  • login 更新期间的排队保活
  • 持久化消息补偿
  • login 重启后的队列恢复
  • Redis Stream 化
  • login 实例协调
  • 匹配服务自动拉起目标 logic

后续可选增强

如果后面要继续提高可用性,可以再逐步演进为:

  1. 匹配入口仍走 RPC
  2. login 内部把队列落 Redis
  3. 加入 ticket 和续租机制
  4. login 更新时支持恢复匹配状态

但这不是当前阶段的目标。

最终收敛结论

当前阶段建议明确成一句话:

匹配走 RPC对战走 Redis。

对应业务语义:

  • 需要立即判断服务可用性的时候,用 RPC
  • 需要跨服转发战斗消息的时候,用 Redis