对象是一套已支撑百万 DAU 多年的开房间 PVP 引擎(FSEngine)。 本文记录的不是"从零搭建",而是在 AI 时代对一套已验证架构做现代化迭代的完整推演过程—— AI 提供技术广度和数据支撑,人提供约束、判断和经验。 你会看到每个架构决策背后的思考链路、依据、和被修正的假设, 以及为什么在 AI 时代,人依然是代码的把控者和兜底者。
技术栈
| 客户端 | Unity · Android / iOS · 状态同步 + 帧同步双版本 |
|---|---|
| Lobby | FastAPI (Python) · JWT RS256 · 按业务域拆 6 个独立服务 |
| Room | FastAPI · Redis 集群 · 房间调度与生命周期 |
| Battle | C++ 内核 + C# 业务层(PhysX 物理)· 运行时 Mono → CoreCLR (.NET) |
| 数据 | OceanBase 分布式数据库 · Redis 集群(分区分服模式) |
| 底层 | Boost ASIO · 自研 fse_rpc (Protobuf) · cluster_rpc · Mono → CoreCLR 运行时升级 |
技术亮点
-
动态负载均衡——
ServerMgr多策略选路(轮询 / 随机 / 区域偏好)+Cell0Balancer按 Battle 服务负载动态分配房间,Gateway 层同样按负载分发会话。 -
高并发分布式 RPC—— Boost ASIO
io_service多 worker 线程 + 自研fse_rpc(Protobuf 序列化 + 方法分发)+cluster_rpc节点互联,服务发现走 Redis。 -
物理 · 状态同步 · 帧同步—— PhysX SDK 嵌入服务端,
Roomtick 固定 0.1s 步长确定性物理;SyncProperty字段级 dirty-bit +SyncableTransform携 AOI 视野剔除;同时支持状态同步与帧同步两类客户端。 -
双端共用 C# 逻辑—— Mono JIT 嵌入 C++ 进程(
mono_vm), 服务器与 Unity 共享同一份战斗代码;本次迭代只把嵌入的 Mono 运行时升级为 CoreCLR (.NET), C++ 内核与 C# 业务层结构保持不变,双端共用特性原样保留。 - 业务域拆分 + JWT RS256 —— 大规模分布式的核心支撑: Lobby 拆 Auth / Player / Inventory / Shop / Matchmaker / Chat 6 个 FastAPI 服务, 各自独立扩缩、独立故障域;Auth 持私钥签发,其他服务公钥本地验签, 零跨服务鉴权调用——所有业务服务天然无状态,可任意水平扩容 / 灰度 / 滚动发布。
-
组件化多进程架构——
AppComponent+RoomComponent两层组件系统(DEFINE_APPCOMPONENT_TYPE宏自动分配类型 ID); Gateway / Cell0 / Battle / Logic / Center / IDIP 多进程按职责拆分。 -
分布式数据层—— 原架构就是 纯 Redis 集群
(
slothash.h即 CRC16 slot 协议的客户端实现),分区分服原生支持,至今跑得很好; 本次迭代仅在需要强一致的业务域(账号 / 物品 / 商城)上新增 OceanBase 分布式关系库作为持久化底座, Redis 集群保持不变,依然不引入数仓 / Kafka。
起点:一个决策为什么值得认真讨论
01项目背景
讨论对象是一套几年前的 C++ + Mono 游戏引擎(FSEngine): C++ 负责底层网络、物理和 RPC,Mono 把 C# 运行时嵌入 C++ 进程, 让业务逻辑在服务器和 Unity 客户端双端共用。 引擎本身是典型的开房间式架构——RoomMgr 管理房间生命周期、 Worker 线程跑战斗、Session 在房间间流转——这类架构天然适合 MOBA、卡牌、休闲对战、吃鸡等"创建房间 / 匹配 / 结算"形态的游戏。
该引擎上线几年以来,已经稳定支撑百万 DAU 级别的开房间 PVP 游戏—— Redis 集群、分区分服、开房间架构都已在生产环境被验证过,并非本次要重新设计的部分。
本次要做的不是"从零设计一个百万 DAU 系统",而是在 AI 时代对这套已验证架构做一次现代化迭代。 核心改动只有两点:
- Lobby: Mono C# → Python (FastAPI)——业务逻辑层脱离 C++/Mono 嵌入宿主, 拆成 6 个独立 FastAPI 服务,Python 生态更契合 AI 协同开发与快速迭代。
- Battle:嵌入的运行时 Mono → CoreCLR (.NET)—— C++ 内核、C# 业务层、PhysX 物理全部保持不变,只替换嵌入的 C# 运行时本身。
数据层保留原有的 Redis 集群,仅为需要强一致的业务域新增 OceanBase 分布式关系库; 其余基础设施(cluster_rpc / 开房间模型 / 分区分服)延续原架构。
02决策的赌注
一次架构选型错了,后面要花 3 年填坑。 AI 写代码再快,也填不回架构债。 架构决策是所有决策里 AI 含量最低、人含量最高的那一类—— 而这正是本文关心的核心问题。
三个转折:决策是怎么被人"拧弯"的
讨论过程中有三次关键的方向修正。每一次都来自人的反驳或洞察, 而 AI 在每一次修正后提供了支撑数据、案例、完整方案。 下面我把三个转折拆开,展示各自的思考链路和依据。
03转折一:从"技术最优"到"团队最优"
① Conway's Law——系统最终会长成团队的样子;
② MTTR 分布——故障平均恢复时间 90% 依赖人,AI 不值班;
③ Instagram 先例——20 亿 MAU 的 Python 单体,证明"团队 + 工具链熟练"比"语言性能最优"更决定长期成败。
04转折二:从"性能焦虑"到"规模稀释"
① FPS 必须把 100 人塞进同一个 tick 循环,对单机性能是硬要求;
② 开房间 PVP 永远是"N 个小房间并行"——每局 5–30 分钟、独立状态、独立 tick;
③ 行业验证:Clash Royale、炉石传说、皇室战争、王者荣耀都是这个架构。
05转折三:从"性能瓶颈"到"无状态治理"
关键工具:JWT + RS256 非对称加密。Auth 独占私钥签发 token, 其他服务只有公钥本地验证——零跨服务鉴权调用,零 DB 查询,完全无状态。
② 性能数学:6 业务 × 10 pod × 2 地区 = 120 个业务 pod, 每 pod 只扛 3–5k QPS,FastAPI 闭着眼能做到;
③ 失败模式:某个业务挂了不影响其他业务, 而不是传统单体"一炸全炸"。
最终架构:每一层都有它的理由
06系统全景
关键决策和背后理由
| 决策 | 理由 | 放弃的候选 |
|---|---|---|
| Lobby 用 FastAPI | 团队熟悉 + 业务拆分后性能足够 | ASP.NET Core / Go |
| 每个业务域独立服务 | 故障隔离 + AI 管理小服务更高效 | FastAPI 单体 |
| JWT RS256 非对称 | 零跨服务鉴权调用 + 密钥泄露影响最小 | HS256 / Session |
| Room Allocator 独立层 | 房间调度是开房间游戏的核心,必须解耦 | Lobby 自己分配 |
| Battle 保持 C++ + C#(Mono → CoreCLR) | 原 C++ 内核已验证多年,只升级嵌入的 C# 运行时本身;避免无端重写高性能战斗层 | Unity Dedicated Server · 推倒重写纯 .NET |
| 共享 C# 业务层原样保留 | 武器/技能/物理数学在 Unity 和 Server 完全一致,双端共用特性不动 | gRPC / Protobuf 只共享 DTO |
| 百万 DAU 阶段暂不分区 | 房间隔离已够用;分区留着等 DAU 翻 10 倍 | 多 region 直接上 |
| 保留原 Redis 集群 + 新增 OceanBase | 原架构已是纯 Redis 集群跑分区分服,验证过;仅为强一致业务域新增 OceanBase 分布式关系库;百万 DAU 阶段仍不需数仓 | 引入单机 MySQL · ClickHouse / Kafka / PySpark |
人机协同:谁擅长什么
07角色分工
把整个讨论过程拆开看,人和 AI 承担了完全不同的职责—— 不是 AI 做一半 + 人做一半,而是两者在不同维度上互补。
- 广度扫描:列出所有候选方案
- 数据支撑:生成 benchmark 对比矩阵
- 案例调取:引用 Instagram / Netflix / Discord 等先例
- 方案展开:把人的一句洞察延伸成完整架构
- 代码样板:JWT / FastAPI middleware / Room Allocator 实现骨架
- 参数核对:QPS / 延迟 / 内存 / 成本的数字
- CRUD 生成:业务逻辑、测试用例、文档
- 设定约束:团队熟 Python,不能上 Rust
- 提出洞察:开房间天然隔离、业务可拆分
- 拒绝过度方案:百万 DAU 不用先做 10 region 分区
- 做不可量化的权衡:性能 vs 招聘 vs 迭代
- 审查 AI 代码:捕捉 AI "看起来对但不合适"的产出
- 把控边界:Shared 库不能 import UnityEngine
- 兜底线上故障:AI 不值班,人值班
08协同的节奏
整个循环里,决策权永远在人手里,AI 是被驱动的那一方。 AI 提供了巨大的"搜索带宽"(扫遍方案、比对数据、生成代码), 但每一次方向的选择和确认都是人做的。 这不是 AI 的局限,而是 AI 应有的位置—— 责任和判断本来就不能被外包。
人的六种不可替代价值
回头梳理,人在这次决策中贡献的价值不是"写了多少代码",而是六种 AI 无法提供的东西:
一、判断团队的真实能力
AI 不知道你团队的水平、偏好、历史。它会推荐"最优"的栈, 但"最优"的栈如果团队调不动,就是最差的栈。 只有人知道"我们团队能 hold 住什么"。
二、理解业务的本质
"开房间式游戏天然是 N 个并行小单元"这个判断, 需要懂游戏形态(MOBA / 卡牌 / 吃鸡 / 休闲对战的共性)、 产品决策(战局是否跨房间、匹配是否跨分区)、 运维经验(哪些引擎因为没做好 Room 生命周期而炸过)。 AI 难以在第一轮就想到这种跨领域直觉。
三、做不可量化的权衡
FastAPI 比 ASP.NET Core 慢 10 倍,但团队熟悉 Python——这两者怎么比? 需要人在"理论性能"、"招聘成本"、"迭代速度"、"故障恢复"这些无法通约的维度之间做判断。 AI 能列出所有维度,但不能替你做决定。
四、承担责任
线上 3 点钟炸了,AI 不会醒。架构失败了,AI 不会被问责。 责任让人的判断带有 AI 没有的"重量"—— 这种重量让人更审慎、更保守、更懂得哪些"聪明"不该尝试。
五、选择正确的抽象层次
"业务隔离 + 房间隔离 + 无状态"这个组合,AI 能解释其中每一个; 但把它们组合起来形成"三维扩展模型"这个心智框架, 需要一个懂系统、懂业务、懂团队的人在混乱中捏出结构。 AI 给的是零件,人拼的是系统。
六、把长期经验压缩到短期决策里
"不要早期拆微服务"、"Room Allocator 要和 Battle 解耦"、 "跨服务事务是陷阱"、"百万 DAU 别急着分区"—— 这些都是用过去踩的坑换来的决策。 AI 的训练数据里有这些经验,但只有真正被烫过的人, 才会在第一时刻把它们拉出来。
人把控代码的三道关
架构定下之后,代码层面也不是"AI 写完就行"。 AI 能写出看起来正确的代码,但"看起来正确"离"在千万级系统里安全运行"还隔着三道关卡。 这三道关必须由人守住——因为出了事 AI 不兜底,是人兜。
例:AI 为
Shared.GameLogic 库自动 using UnityEngine;,
因为它看到其他代码这么用。但这个库的边界规则是绝对不能依赖 Unity,
否则服务器就无法引用。这种"合理但违规"的代码,只有人能看出来。
审查的本质是对齐"隐性约定"——这些约定往往没写在代码里,写在人的脑子里。
这些问题不在单个文件里,在文件之间的契约里。 人的角色是守住契约:Protobuf schema、JWT claim、Redis key、数据库迁移—— AI 可以辅助检查,但最终一致性由人保证。
kubectl rollout undo。
线上事故的处置权必须在人手里: 因为决策有时效(1 分钟 vs 30 分钟)、有经济后果(每分钟损失几万)、 甚至有法律后果(合规数据泄露)。 AI 可以辅助分析日志、起草修复 PR,但"按下回滚按钮"这件事,必须是人。
兜底的本质是"责任归属"。 代码可以 AI 写、流程可以 AI 优化, 但当事情出错,有人必须站出来说"我负责"—— 这个位置 AI 永远无法顶替。
"AI 可以加速,但不能免责。" 每一行 merge 进主干的 AI 代码,都必须有一个人名字在 PR 审核者位置。 这不是形式主义,而是让责任链条不被 AI 切断—— 出了事找得到人、复盘得到经验、经验沉淀进下一次决策。
结语:AI 是杠杆,人是支点
"AI 时代还需要架构师吗?" 这个问题其实搞反了。 正确的问法是:"AI 时代是不是更需要架构师了?"
因为 AI 能把糟糕的架构也写得飞快:100 个微服务、5 种数据库、 10 层嵌套的分布式事务——AI 能秒出代码,但人要付出 3 年的运维成本。 AI 降低了写代码的门槛,抬高了做决策的门槛。
最好的人机协作形态,是让两者各司其职:
- 扫描所有方案
- 生成所有样板
- 核对所有数据
- 写所有样板测试
- 24 小时不累
- 设定约束
- 做最终决策
- 审查每一行代码
- 守住系统边界
- 线上事故兜底
AI 是杠杆,人是支点。 杠杆再长,没有支点,什么都撬不动。 百万 DAU 的系统不是被代码量撑起来的, 是被一个又一个"人的判断"撑起来的。
这大概就是 AI 编程时代最反直觉的真相: 代码越来越容易写,架构师越来越值钱。
复盘:三项实锤能力的证据链
本文的决策过程里,真正留下清晰、可验证痕迹的是三项能力: 判断力、洞察力、标准感。 下面逐一展开它们的证据链。
09判断力:在无法通约的维度间落子
判断力的定义不是"选了什么",而是—— 在所有维度单位都不一样、无法直接相加时,仍然能落子。 AI 可以列出所有维度和权重,但不能替你做决定, 因为决定的根据不在数据里,而在对团队、业务、组织、时间的综合感受里。
面对 AI 推荐的 Rust + Go,人拍板"团队熟 Python 不变"。 这是在MTTR 风险 × 招聘成本 × 迭代速度 × 理论性能 这些单位不同的维度之间权衡。
② 百万 DAU 暂不分区
延后 vs 提前,是在"当前架构复杂度"和"未来扩展成本"之间押宝。 赌的是"DAU 翻 10 倍之前,架构债还得起"这个经验判断。
③ 拒绝引入数仓 / Kafka / ClickHouse
在"长期扩展性"和"当前团队运维能力"之间取舍—— 知道什么时候不该加工具。
这三次选择都没有理论最优解。如果有,AI 早给了。 AI 给的是候选清单;从清单里挑哪一个、在哪一维上妥协—— 这个动作本身就是判断力。它之所以是实锤, 是因为 AI 的输出恰好无法完成这一步,反衬出来的痕迹格外清楚。
10洞察力:在 AI 的穷举之外看见隐藏结构
洞察力的定义不是"想到了什么",而是—— 看到了 AI 的穷举里没有列出的那一条。 AI 的初始输出通常是完备的、合理的、有数据支撑的, 但它漏掉了一个维度。人把这个维度加回来,整个方案的形态就变了。
AI 初始输出停留在"FastAPI 扛得住但成本贵 3–5 倍"—— 这是在单机性能维度上思考。 人说"开房间 PVP 本来就是 N 个独立房间"—— 把问题从"物理问题"拽到"部署问题"。 这个重定义不在 AI 的任何一项输出里。
② 转折三 · HTTP + JWT 无状态化
AI 讨论的是"怎么让 Python 跑得更快"。 人说"HTTP 可按业务拆分 + JWT 无状态"—— 把"性能瓶颈"直接降维成"治理问题"。 这是对问题性质的重新识别,不是方案的优化。
文中写得很直白:转折一的"依据补全"里, "AI 承认这个反驳击中了盲区"—— AI 自己承认的"盲区",就是洞察力最干净的证据。 不是人想得比 AI 更全,而是人看到了 AI 看不到的那一面。 更关键的是:洞察一旦说出来,AI 就能顺着展开出完整的三维模型; 但 AI 自己不会主动走出这一步。
11标准感:对不能让步的事情划出红线
标准感不是"选了什么",也不是"看见了什么",而是—— 能清楚说出"绝对不能 X"、"必须 Y" 这种斩钉截铁的规则。 它表现为一组非协商的边界,把可以权衡的事情和不可以权衡的事情清楚地隔开。 AI 会顺着现有代码风格推进,不会凭空生出红线——红线必须由人设定。
"绝对"这个词本身就是标准的信号——不是权衡,是红线。 就算 AI 顺着其他代码的风格加了
using UnityEngine;,
也必须推翻。这种边界规则只在人的脑子里,不在代码里。② JWT RS256 非对称,放弃 HS256 / Session
决定的根据不是性能或代价,而是一条标准: "密钥泄露的影响必须可控"。非对称 + 零跨服务鉴权 是从安全标准倒推出来的架构,不是从性能倒推。
③ Battle 保持 C++ + C#(只升级 Mono → CoreCLR)
原 C++ 内核已在生产跑出百万 DAU 战绩,重写的风险远大于收益; 真正要解决的是 Mono 运行时老旧——那就只换运行时, C++ 内核、C# 业务层、PhysX 物理都原样保留,这是对"不可推倒重来"的判定。
④ "AI 可以加速,但不能免责"
"每一行 merge 进主干的 AI 代码,都必须有人名在 PR 审核者位置"—— 这被明确命名为"把控代码的原则", 是一条过程标准:责任链条不被 AI 切断。
标准感的表现形态本来就是规范性—— 能清楚说出"绝对不能 X"、"必须 Y",并在 AI 想越界时把它拉回来, 这就是实锤。AI 做不到的那一部分是: AI 会顺着已有代码和上下文推进,不会主动生出红线。 什么不能让步、哪条线不能越—— 这种判定只能来自人对业务、对安全、对责任的长期积累。