Skip to content

服务端语言与运行时 ​

Category ​

server / 横向节点 / 语言与选型。一句话:服务端语言选型不是语法偏好,而是「对局逻辑以什么运行时模型执行、热更边界切在哪层」的选型。

Definition ​

客户端语言地图(engines/02)的四问——人才、熟悉度、生态、历史——在服务端同样成立,但服务端多出两条自己的硬约束:实时性(tick 预算与 GC 停顿直接决定帧抖动)与权威性(权威逻辑要在运营期改,改的通道就是热更面)。所以成熟的服务端语言形态几乎都是双层:一门核心语言承载权威与并发,一层脚本承载高频变化的玩法逻辑。语言选择的实质,是给这两层各选一个运行时。

Problem ​

  • 按榜单选:性能榜单和语法偏好不回答「这个运行时的停顿形态能否塞进 tick 预算」。GC 停顿形态(并发标记 vs 全停顿)不同,同一段代码的帧表现可以差一个量级。
  • 单语言理想主义:客户端与服务端的要求本来就不同(多语言并存是常态,见 engines/02 的多语言边界)。服务端全用脚本,CPU 密集段顶不住;全用编译型,每次改活动逻辑都要走发版。
  • 热更与权威打架:服务端是权威,运营期又要频繁改数值与活动逻辑。没有脚本层的服务端用「重启发版」硬扛;有脚本层的服务端若不把热更边界画清楚,权威口径跟着漂移(热更体系见 system/scripting/04)。
  • 低估人才与生态的地心引力:并发友好的语言很多,能招到人、协议栈 / DB 驱动 / 可观测生态齐备的不多——engines/02 四问里生态一问在服务端最贵。

Algorithm ​

运行时四型 ​

运行时模型代表逻辑执行形态热更面典型代价
编译核心 + 嵌入脚本 VMBigWorld(C++ + Python)、KBEngine(C++ + Python)、skynet(C + Lua)权威与 IO 在编译核心,玩法逻辑在脚本脚本层热载两套语言心智,跨语言边界要管理
托管运行时Orleans(.NET)、Nakama(Go 核心 + Lua/TS/Go 扩展)GC 换生产力,Actor / 粒度并发运行时插件 / 灰度部署停顿形态要配容量模型
原生并发 GC 语言leaf、nano(Go)goroutine / channel 化的并发编译期,靠模块化 + 发版运营期改逻辑慢
事件循环脚本运行时Colyseus(Node.js)单线程事件循环,房间级隔离部署期换代码CPU 密集上限低,多核靠多进程

选型四问(engines/02 四问的服务端投影) ​

  1. tick 预算下,这个运行时的停顿形态可容忍吗?
  2. 它的并发模型与对局模型匹配吗(房间制并行 vs 大世界单世界串行)?
  3. 热更边界切在哪一层,谁有权改权威逻辑?
  4. 人才存量与生态(协议、DB 驱动、可观测性)接得住吗?

落地路径:先按运行时模型定性,再用四问收窄,最后对照 开源服务器地图 看有没有形态匹配的现成实现——能选形态就不要从语言开始造。

Used By ​

  • BigWorld(commit 088d3b84,路径相对 programming/bigworld/):C++ 服务端 + 嵌入式 Python(README.md:21)——引擎侧承载 AOI、同步与持久化,实体玩法方法全部写在 Python 脚本层。
  • KBEngine(commit 0bc93d5):同构的双层选择——「The engine is written in C++」(README.md:71),Python 脚本「supports hotfixing」(README.md:67),Python 3.12 嵌入运行时(README.md:132)。
  • skynet(commit 64391f7):「multi-user Lua framework supporting the actor model」(README.md:3)——C 只做内核与调度,业务逻辑全部是 Lua Actor。
  • Nakama(commit e920249):Go 编译核心 + 三选一运行时扩展(Lua / TypeScript / Go,README.md:27),go.mod:1 声明 Go 模块——平台核心编译、玩法扩展脚本化的混合形态。
  • leaf(commit af71eb0):「A pragmatic game server framework in Go」(README.md:3);nano(commit dbf22c7):Go 网络库(README.md:12)——原生并发语言形态的轻量端。
  • Colyseus(commit e620123):「Authoritative Multiplayer Framework for Node.js」(README.md:21)——事件循环形态,房间模拟 60fps 推进(packages/core/src/Room.ts:57-58)。
  • Orleans(commit b084e03):.NET 上的 Virtual Actor Model(README.md:10、:16)——托管运行时把分布式复杂性收进运行时,开发者只写 grain。

关系链:实时性与权威两条硬约束 → 双层结构 → 运行时四型 → 选型四问 → 落地地图。逐段回答「为什么需要下一个」:

  • 约束定了才有双层:实时性与权威性决定「核心承载权威、脚本承载变化」的分工(脚本层定位见 脚本层定位与语言选择)。
  • 双层定了才选运行时:两层的语言合起来才是完整的技术栈决定,单说「服务端用什么语言」说不清。
  • 运行时定了要过四问:人才、生态、热更、停顿把候选收窄到可执行的一两个。
  • 四问过了要落地:开源服务器地图 按形态给出可对照的现成实现,避免从零造。

上游:engines/02 语言地图(四问的客户端版)、engines/03 选型综合(自研/框架/现成方案的判断);下游:开源服务器地图(形态落地)、执行模型与运行时(并发模型的展开)。

Reference ​

  • 本页为工程经验归纳;各家引擎事实以文内 commit 锚定的源码引用为准
  • 术语对照:Runtime Model(运行时模型)、Embedded Scripting(嵌入式脚本)、Hotfix(热修复)、GC Pause(GC 停顿)
  • 本页为工程经验归纳;各家引擎事实以文内 commit 锚定的源码引用为准;页内实战案例为教学示例(数字取整简化)

实战案例:一次「榜单第一的语言,帧抖动超了一倍」的运行时选型排查 ​

案例为教学示例(非摘自真实项目),数字经过取整简化。它把本页「Problem」的按榜单选与「Algorithm」的选型四问用到深水区:语言选完了,帧抖动才来收账——这是停顿形态没进 tick 预算的排查,也是「怎样让两层的语言合起来才是完整技术栈决定」的完整展开。

背景与现象:某房间制竞技服务端按性能榜单选了全停顿 GC 的托管语言。上线后战斗帧 P99 从 16 毫秒涨到 34 毫秒,GC 停顿 80 毫秒每 2 秒一次;运营期改一次活动逻辑要走发版,两周一次。

第一步:现象与口径——先分清是哪一问没答 ​

停顿口径:全停顿 GC 一次 80 毫秒,直接超 tick 预算 16 毫秒的 5 倍——性能榜单和语法偏好不回答「这个运行时的停顿形态能否塞进 tick 预算」。

热更口径:没有脚本层的服务端用「重启发版」硬扛,运营期改活动逻辑两周一次;有脚本层又不把热更边界画清楚,权威口径跟着漂移。

并发口径:房间制并行与托管运行时的粒度不匹配,对局模型与并发模型对不上。

第二步:分层归因——按选型四问比对 ​

text
帧抖动归因  占比
  停顿形态没进 tick 预算评估     58%
  热更边界没画,权威口径漂移      24%
  人才与生态低估                  18%

逐项比对:GC 停顿 80 毫秒超 tick 预算 5 倍——命中第一问没答;改逻辑走发版——命中第三问没答;招不到人、协议栈与可观测生态不齐——命中第四问没答。四问只答了第二问。

第三步:根因——按榜单选,没按运行时模型选 ​

语言选择的实质是给两层各选一个运行时:一门核心语言承载权威与并发,一层脚本承载高频变化的玩法逻辑。按榜单选只看了单语言性能,没看停顿形态、并发匹配、热更边界、人才生态——两层的语言合起来才是完整的技术栈决定。

第四步:处置与回填——四问过一遍,双层落地 ​

  1. 选型四问过一遍:tick 预算下停顿形态可容忍吗、并发模型与对局模型匹配吗、热更边界切在哪层、人才生态接得住吗。
  2. 双层结构落地:编译核心承载权威与 IO,脚本层承载玩法逻辑,改完即生效不走发版。
  3. 热更边界画清:谁有权改权威逻辑写进设计,脚本层定位与语言选择对齐。
  4. 对照开源服务器地图:先按运行时模型定性,再对照形态匹配的现成实现,能选形态就不从语言开始造。

复核账:战斗帧 P99 34 毫秒 → 17 毫秒;GC 停顿 80 毫秒 → 并发标记 8 毫秒;活动逻辑改动脉 2 周 → 当天热载。

回填清单:

回填项动作回填处
停顿形态评估tick 预算下停顿形态可容忍吗,进选型第一问Problem
双层结构编译核心承载权威并发,脚本层承载玩法Algorithm
热更边界谁有权改权威逻辑写进设计Problem
形态落地对照开源服务器地图选形态,不从语言造Reference

案例的三个教训 ​

  1. 榜单不回答停顿形态。全停顿 GC 一次 80 毫秒,超 tick 预算 5 倍,帧抖动直接翻倍。
  2. 两层的语言合起来才是技术栈决定。单说「服务端用什么语言」说不清,核心与脚本要各选一个运行时。
  3. 热更边界要提前画。没有脚本层用重启发版硬扛,有脚本层不画边界,权威口径跟着漂移。

游戏知识体系