核心服务角色
Category
server / services 子树。节点定位:服务角色划分单独讨论——每个角色真正解决什么、边界污染形态、边界稳比角色多更重要。
Definition
系统开始拆分以后,最常见的问题往往不是“服务不够多”,而是名词很多、职责很乱。大家都在说房间服、场景服、活动服、账号服,但一落到实现里,房间服顺手写资产,活动服顺手推进局内状态,网关顺手做半套业务编排,最后服务名字分开了,状态边界还是混着的。
所以这里要做的不是罗列常见名词,而是把这些角色背后的责任和状态归属讲清楚。
Problem
「服务不够多」不是问题,「名词很多、职责很乱」才是:房间服顺手写资产、活动服顺手推进局内状态、网关顺手做半套编排——名字分开了,状态边界还混着,等于没拆。
为什么角色划分必须单独讨论
上一节解决的是“为什么拆”和“边界先围绕什么画”;这一节解决的是“拆出来以后,哪些角色最容易稳定下来,哪些角色最容易做脏”。
在真实项目里,大多数系统最终都会围绕几类典型角色组织:
- 面向连接和会话的角色
- 面向运行时实例的角色
- 面向长期资产和外围能力的角色
- 面向调度和治理的角色
如果这些角色没有分清楚,系统通常会走向两个结果:
- 某个服务什么都接,最后变成新的大单体
- 名字虽然拆开了,但状态和责任仍然交叉缠绕
常见服务角色真正各自解决什么问题
1. 网关 / 接入服务
这类服务最核心的价值是维护连接和会话入口。它通常负责:
- 连接建立、断开、心跳、限流
- 鉴权后会话绑定
- 基础协议转换和入口路由
- 踢线、封禁、基础接入治理
它通常不应该负责:
- 重业务逻辑
- 高代价数据库事务
- 房间、场景或世界状态推进
网关之所以常被做坏,是因为它看起来“离玩家最近”,于是所有临时逻辑都往里塞。结果连接层和业务层互相污染,故障和性能问题都更难定位。
2. 账号 / 角色服务
这类服务更接近长期身份与角色基线治理。它通常负责:
- 账号信息与角色档案
- 登录入口和角色选择
- 基础角色状态读取
- 局外资产入口或资产主索引
它不适合承担高频局内状态,也不应该变成“什么都能顺手查一下”的万能入口。账号服务如果不断被塞入玩法运行时状态,后面很容易变成最难拆的核心耦合点。
3. 匹配 / 调度服务
匹配服务不只是把玩家凑成一桌。它往往承担:
- 匹配分桶与规则计算
- 队伍组织与开局前校验
- 房间创建请求
- 节点选择与实例分配
- 开局前的状态协调
从工程视角看,匹配层其实是很多会话型游戏进入调度体系的第一道入口。
4. 房间 / 副本 / 战斗实例服务
这一类服务的核心特征是实例态强、生命周期相对短、边界和一局或一个副本直接绑定。它们通常负责:
- 高频运行时状态推进
- 玩家输入处理与广播
- 局内权威状态维护
- 重连上下文和托管上下文
- 结算前的最终运行时结果
这类服务最怕被塞入长期资产写入、全局规则解释和外围查询逻辑,因为它们的目标应该是让局内主链路尽可能单纯、稳定、低延迟。
5. 场景 / 世界服务
房间服务强调短生命周期实例,场景或世界服务强调长期存在的共享空间。它们通常区别在于:
- 状态是否长期驻留
- 是否有多人共存和公共空间
- 是否存在区域切换和世界迁移
- 是否承担持续在线的世界规则维护
MMO、大地图、生存沙盒项目里,这类服务往往是系统主干。它们的难点不只是同步,还包括区域管理、热点治理和控制权迁移。
6. 活动 / 任务 / 商店等外围业务服务
这类服务更适合围绕长期规则、配置解释和进度治理组织,常见职责包括:
- 活动规则解释
- 任务或活动进度维护
- 奖励发放请求
- 商店状态、购买约束和兑换记录
它们最大的问题通常不是“功能多”,而是边界容易失控。一个活动服务如果同时承担配置、进度、发奖、报表和跨服编排,后面每次改动都会变成系统级连锁反应。
7. 排行 / 邮件 / 社交 / 聊天等公共能力服务
这些能力通常跨玩法、跨场景复用,因此边界是否清楚非常关键。
- 排行更像读取、聚合和展示能力,不应该反向侵入战斗主链路
- 邮件和补偿更像长期资产外围投递链路
- 聊天与社交有自己的连接、广播和关系模型,不适合和局内同步混在一起
它们之所以容易被低估,是因为平时不在主战斗循环里,但一旦边界不清楚,就会在全系统里产生隐性耦合。
8. 支付 / 订单 / 风控服务
这类服务通常不在玩法主循环上,但它们的边界往往比玩法服务还稳定。常见职责包括:
- 订单状态机
- 回调确认与发货触发
- 异常订单处理
- 审计、风控和追责链路
这类服务最怕“顺便做一下”。因为一旦资产和订单链路出错,修复成本远高于普通玩法问题。
9. 协调层 / 控制平面相关服务
很多项目在早期没有正式把它们算作服务角色,后期却一定会补上。它们通常负责:
- 节点注册、分组和健康治理
- 区服路由和实例分配
- 迁移、摘流和切流
- 扩缩容编排
- 全局配置下发和版本门禁
它们本身不推进业务状态,却决定整个系统是不是能被稳定调度和恢复。
这些角色最常见的边界污染
长期资产和实例态混在一起
比如房间服直接写长期资产,账号服直接推进副本状态,活动服直接持有局内高频状态。前期看起来省事,后期会同时拉高耦合、恢复成本和故障影响面。
规则解释和状态维护混在一起
尤其容易出现在活动、任务、商城这类系统里。配置解释、进度维护、奖励发放、统计报表如果长在同一个服务里,每次小改动都可能影响整条链路。
把网关做成业务总线
网关一旦开始承担过多业务判断,它就不再只是接入层,而会变成最难拆、最难测、最难排障的隐性总线。
角色不是越多越好,而是边界越稳越好
列出这么多角色,不意味着每个项目一开始就该全部拆出来。真正应该问的是:
- 这些职责是否已经足够稳定
- 拆出来是否能减少跨服务抖动
- 是否值得独立扩缩容和治理
- 是否有独立故障域和独立运维价值
早期一个服务兼任多个角色并不罕见,问题不在于是否合并,而在于逻辑边界有没有先被说清楚。边界清楚,后续才能平滑外提;边界不清楚,拆不拆都乱。
一个更实用的观察框架
建立直觉时,可以先把这些角色看成四层:
- 连接与会话层:网关、接入、会话绑定
- 运行时状态层:匹配、房间、副本、场景、世界
- 长期资产与公共能力层:账号、活动、支付、邮件、排行、社交、风控
- 系统治理层:协调层、控制平面、路由、编排、监控
这不是固定部署图,但它能帮助团队避免“所有东西都叫业务服”的混乱。
常见误区
最常见的错误,是把服务名字当成边界,以为起了“房间服”“活动服”“账号服”这些名字,职责就自动清楚了。第二类错误,是让同一个服务同时承担连接、实例态和长期资产三种职责。第三类错误,是忽视协调层和控制平面的独立角色,结果业务逻辑能跑,系统却无法稳定调度、迁移和恢复。
实战案例:一次「网关越做越胖,一次改动全网抖」的业务总线化排查
案例为教学示例(非摘自真实项目),数字经过取整简化。它把本页「1. 网关 / 接入服务」「把网关做成业务总线」「角色不是越多越好,而是边界越稳越好」用到深水区:改的是限流,抖的是全服——这是服务名字分开了、状态边界还混着的排查,也是「怎样让边界稳而不是角色多」的完整展开。
背景与现象:某 MMO 的网关服务,从最初的连接接入层一路长到 8 万行代码、内嵌 30 多个业务逻辑点。一次只改限流策略的小发布,引发全服登录异常 20 分钟——影响面从单模块扩到了所有接入玩家。
第一步:现象与口径——先看故障影响面有多大
影响面口径:网关每次发布影响全部接入玩家,单次故障影响面按全服算;同期业务服务发布只影响对应玩法。
改动口径:30 多个业务逻辑点(订单校验、活动状态查询、部分结算触发)都长在网关里,任何一处改动都要重新回归整条接入链路。
定位口径:网关内连接层与业务层互相污染,同类故障平均定位耗时 4 小时——排障时说不清是接入问题还是业务问题。
第二步:分层归因——按本页边界污染清单排查
故障影响面扩散归因 占比
网关承担重业务逻辑与半套业务编排 57%
房间服顺手写长期资产 25%
活动服持有局内高频状态 18%逐项比对:网关里塞了业务编排(本页说网关「离玩家最近,于是所有临时逻辑都往里塞」);房间服顺手写长期资产;活动服持有局内高频状态。三类污染都在,网关那条影响面最大。
第三步:根因——把服务名字当成了边界
起了「网关」这个名字,团队以为职责自动清楚了,实际上连接、实例态和业务逻辑在同一进程里混着。网关一旦承担过多业务判断,就不再只是接入层,而是最难拆、最难测、最难排障的隐性总线。
第四步:处置与回填——按角色重新划边界
- 网关瘦身:只保留连接建立断开、心跳、限流、鉴权后会话绑定、基础协议转换和入口路由,业务判断全部外提到业务服务。
- 长期资产与实例态分离:房间服不再写长期资产,资产变更统一走账号/资产服务;活动服不再持有局内高频状态。
- 边界写进角色定义评审:每个服务明确「负责什么、不负责什么」两栏,评审时逐条对照。
复核账:网关代码 8 万行 → 2.1 万行;故障影响面从全服收敛到单模块;同类故障平均定位耗时 4 小时 → 40 分钟;限流策略改动的发布回归范围缩小到网关自身。
回填清单:
| 回填项 | 动作 | 回填处 |
|---|---|---|
| 网关职责边界 | 网关只留连接、会话、鉴权、路由,业务判断外提到业务服务 | 1. 网关 / 接入服务 |
| 长期资产与实例态分离 | 房间服不写长期资产,账号服不推进副本状态,资产变更走资产服务 | 长期资产和实例态混在一起 |
| 规则解释与状态维护分离 | 活动服的配置解释、进度维护、发奖、报表拆开,不挤在一个服务里 | 规则解释和状态维护混在一起 |
| 角色边界评审 | 每个服务写「负责/不负责」两栏,边界稳优先于角色多 | 角色不是越多越好,而是边界越稳越好 |
案例的三个教训
- 服务名字不等于边界。起了「网关」不等于职责清楚,连接、实例态、业务逻辑混在一个进程里,故障影响面就按最大者算。
- 离玩家最近的服务最容易长成隐性总线。网关一旦开始承担过多业务判断,就变成最难拆、最难测、最难排障的那一环。
- 边界稳比角色多重要。拆不拆是后话,先把每个服务的「负责/不负责」说清楚,才谈得上平滑外提。
Used By
服务角色定义评审;边界污染排查。
- BigWorld(commit
088d3b84,路径相对programming/bigworld/):base/cell 双形态角色分工——玩家代理class Proxy : public Base挂 baseapp(server/baseapp/proxy.hpp:74),真身class Cell挂 cellapp(server/cellapp/cell.hpp:45),八类服务进程各有独立 main(server/baseapp/main.cpp、server/cellapp/main.cpp等) - KBEngine(commit
0bc93d5):角色划分是显式枚举——enum COMPONENT_TYPE把十余类组件类型与 ID 一并定死(kbe/src/lib/common/common.h:73-91),实体跨进程调用封装为EntityCall(kbe/src/lib/entitydef/entity_call.h:15),baseapp→cellapp 建实体流走createCellEntity(kbe/src/server/baseapp/entity.cpp:27-28)
Related
关系链:角色独立讨论 → 各角色解什么 → 边界污染 → 边界稳 > 角色多 → 观察框架。上游:services/01(四问定边界)。下游:server/sync(对局角色的展开)、glossary/07(控制平面)。
Reference
本页为工程经验归纳;页内实战案例为教学示例(数字取整简化)。