4、服务拆分实战(上):按业务领域拆分
好,咱们进入实战环节了。前面几章讲了那么多理论,什么DDD、什么限界上下文,现在终于要动手了。
这一章,我带你走一遍最经典的游戏服务器拆分路径——按业务领域拆分。说白了,就是把一个巨大的单体服务器,切成几个独立的小服务。
我个人习惯,先画一张业务大图,再下刀。你想想看,一个典型的游戏服务器,核心业务无非就是:登录、匹配、战斗、社交、商城。这五个领域,几乎覆盖了90%以上的游戏类型。
4.1 为什么是这五个领域?
我在项目中遇到过不少团队,一上来就拆成十几个微服务,结果运维成本直接爆炸。其实,拆分的粒度不是越细越好。
这五个领域,每个都有自己独立的生命周期和资源需求:
- 登录:高并发、短连接,对安全要求极高
- 匹配:实时性要求高,需要维护大量玩家状态
- 战斗:计算密集,对延迟极度敏感
- 社交:读写比例悬殊,好友关系复杂
- 商城:事务性强,数据一致性要求高
你看,每个领域的“脾气”都不一样。硬塞在一起,只会互相拖累。
核心原则:每个微服务应该有自己的数据库,而不是共享一个库。这是微服务架构的底线。
4.2 登录服务:第一道门
登录服务,说白了就是玩家的“门卫”。它负责验证身份、发放令牌、管理会话。
嗯,这里要注意:登录服务不要做任何游戏逻辑。我曾经见过一个项目,登录服务里还顺带查了玩家的VIP等级,结果登录高峰期直接把数据库打崩了。
登录服务的边界定义:
- 职责:账号注册、密码验证、Token签发与刷新、第三方登录(微信、Google等)
- 不负责:玩家数据加载、好友列表、邮件通知
- 数据存储:账号表、设备信息表、黑名单表
- 对外接口:
Login(account, password) → Token、VerifyToken(token) → playerId
个人经验:登录服务最好独立部署,并且做限流。我习惯用Redis做Token缓存,TTL设成30分钟。玩家活跃时自动续期,不活跃就过期释放。
4.3 匹配服务:撮合的艺术
匹配服务,是游戏里最“热闹”的地方。玩家排队、段位匹配、队伍平衡,全在这里完成。
为什么会这样?因为匹配算法通常需要维护一个“等待池”,里面全是正在排队的玩家。这个池子,必须放在内存里,不能放数据库。
匹配服务的边界定义:
- 职责:玩家入队、出队、段位匹配、队伍平衡、超时处理
- 不负责:战斗房间创建、玩家属性修改
- 数据存储:Redis(等待队列、匹配分数)、MySQL(匹配记录、历史数据)
- 对外接口:
JoinQueue(playerId, mode, rank) → queueId、LeaveQueue(queueId) → bool
避坑指南:我曾经踩过一个坑——匹配服务直接调用了战斗服务的接口来创建房间。结果匹配高峰期,战斗服务被打挂了,所有匹配成功的玩家都进不了房间。正确的做法是:匹配服务只负责“撮合”,撮合成功后通过消息队列通知战斗服务。
4.4 战斗服务:核心中的核心
战斗服务,是游戏服务器里最“重”的服务。它负责处理每一帧的逻辑、技能释放、伤害计算、胜负判定。
我个人习惯,战斗服务一定要做成无状态的。什么意思?就是战斗房间里的所有状态,都只存在内存里,不落盘。战斗结束后,把结果写入数据库即可。
战斗服务的边界定义:
- 职责:房间创建、帧同步/状态同步、技能逻辑、AI行为、胜负判定
- 不负责:玩家数据持久化、匹配逻辑、聊天消息
- 数据存储:Redis(房间信息缓存)、MySQL(战斗回放、结算数据)
- 对外接口:
CreateRoom(players, mode) → roomId、SubmitAction(roomId, playerId, action) → bool
关键点:战斗服务是唯一一个可以“丢数据”的服务。因为战斗过程中的中间状态,丢了也就丢了,只要最终结算结果正确就行。所以,战斗服务可以用纯内存方案,追求极致性能。
4.5 社交服务:关系网的中心
社交服务,负责处理玩家之间的互动。好友、公会、聊天、邮件,全归它管。
你想想看,社交服务的特点是:读多写少,关系复杂。一个玩家可能有几百个好友,每个好友又有自己的状态。如果用关系型数据库硬查,性能会非常难看。
社交服务的边界定义:
- 职责:好友添加/删除、公会管理、聊天频道、邮件系统、排行榜
- 不负责:玩家战斗数据、商城购买
- 数据存储:图数据库(好友关系)、Redis(在线状态、聊天缓存)、MySQL(邮件、公会信息)
- 对外接口:
AddFriend(playerId, friendId) → bool、SendMessage(from, to, content) → msgId
个人经验:社交服务最容易出现“扇出”问题。比如一个公会会长发了一条全服公告,结果要推送给几千个玩家。我习惯用消息队列做异步推送,避免同步阻塞。
4.6 商城服务:最后的金库
商城服务,直接和钱打交道。商品购买、订单生成、支付回调、发货确认,每一步都不能出错。
嗯,这里要特别强调:商城服务必须保证数据一致性。不能出现“玩家付了钱,但没收到道具”的情况。
商城服务的边界定义:
- 职责:商品列表、下单、支付对接、发货、退款、限购逻辑
- 不负责:玩家背包管理、战斗道具使用
- 数据存储:MySQL(订单表、商品表、支付记录)、Redis(限购计数器、热销商品缓存)
- 对外接口:
CreateOrder(playerId, goodsId, count) → orderId、PayCallback(orderId, result) → bool
避坑指南:我曾经遇到过一个经典问题——玩家下单后,支付回调还没到,但玩家已经关掉了游戏。结果订单状态卡在“待支付”,玩家再登录时无法重新购买。解决方案是:引入订单超时机制,30分钟未支付自动取消。
4.7 服务边界总结
好了,五个服务的边界都定义完了。我整理了一张表,方便你对照:
| 服务名称 | 核心职责 | 数据存储 | 关键特征 |
|---|---|---|---|
| 登录服务 | 身份认证、Token管理 | MySQL + Redis | 高并发、安全敏感 |
| 匹配服务 | 玩家撮合、段位匹配 | Redis为主 | 实时性、内存操作 |
| 战斗服务 | 房间管理、逻辑计算 | 内存为主 | 计算密集、无状态 |
| 社交服务 | 好友、公会、聊天 | 图数据库 + Redis | 读多写少、关系复杂 |
| 商城服务 | 订单、支付、发货 | MySQL + Redis | 事务性强、一致性要求高 |
记住一句话:边界清晰,服务才能独立演进。每个服务只关心自己的那一亩三分地,不要越界。下一章,我会带你看看服务拆分后的通信问题——服务之间怎么说话,才能又快又稳。