Golang 游戏开发框架选型

9 天前
 ugpu

各位佬新春好. 因为业务选型 之前一直使用 cpp/lua/c# 目前打算使用 golang 作为新产品的游戏服务器开发语言. 对 golang 的选型不知道如何抉择.开发业务为游戏服务器.

如果是游戏服务器开发老鸟可能知道 c#有 et lua 南方地区偏向 skynet (小厂居多.方便快捷 cpp 的框架一看一大把,多半自己撸一套.

那么有用过 golang 框架的吗?希望这个框架一直在维护.最好类似 skynet 这种.主要是一直在维护,且有多个公司上线部署验证过的.最好还是有社区

3551 次点击
所在节点    Go 编程语言
47 条回复
lesismal
8 天前
@guanzhangzhang 游戏客户端跟 golang 没太大直接关系了, 一些开源项目客户端和服务端都提供了, 但都是游戏功能本身比较多, 游戏业务逻辑本身并不算是通用的框架部分. 游戏客户端引擎是框架的主要部分, 其他的加上网络和热更之类的就算足够做普通项目的脚手架了能对标 golang 这些忽悠人的脚手架项目, 复杂游戏的也是业务类的特殊需求也是不同类型不一样, 大型的多人海量元素同屏动作类之类的优化也是一大坨, 特效需求多的各种技术美术的需求 shader 优化.
不管是客户端还是服务端, 除了通用的简单脚手架的部分, 没有统一的框架, 各种类型游戏的特殊需求都加上的话就显得太重了不适合中小游戏, 比如 bigworld 去做卡牌和简单 arpg 完全是杀鸡用牛刀, 客户端服务端用这种大框架都很浪费反而开发效率和维护成本低. 如果特殊的不加上吧, 那些业务需求复杂的功能又都得自己造轮子, 简单脚手架提供的这点功能微不足道用处不大
lesismal
8 天前
> 还是 sqlx ,看到 gen 类型的框架我就头痛

@securityCoding sqlx 也是挺麻烦的, 一些没必要的搞得太多, 懒得浪费时间学它, 所以我自己搞了个. 性能最好的肯定是生成代码的这类了, 但用起来也是真不爽
asuraa
8 天前
@lesismal 受教了,我去研究下
asuraa
8 天前
@lesismal

1.2048 这个值是有考究的,很多框架都会有这种定值设计,这个定值在是在低峰和高峰中取的折中值,各项数据经验下取的比较合理的值,避免掉很多人手动设置不合理。当然,抽成配置也是可以,但是异步模块不是和框架强绑定的,是当成组件设计的。

2.worker 分配不均匀问题,玩家 id 是自增的,通过玩家 id 取模也是常用的一种负载策略,这也是多年经验下来用的比较简单的一种策略,这里当然可以加很多种策略模式去获取 worker ,但是得保证同一个玩家获取同一个 worker 才行。

3.代码估计没看仔细,是只有获取没创建的才需要初始化加锁的,后续获取是不需要的。这是典型的懒加载,需要用到才创建资源。如果在启动阶段全部初始化才是性能和资源的浪费,有的人都不需要用的,你给人初始化了。而且也会违背作为框架组件的设定,让框架变得臃肿。

这模块我们自己项目也在用,我们一百多万 dau ,跑的好好的。不过代码是比较简单,大家相互讨论印证也没问题。挺好的技术交流
lolizeppelin
8 天前
@securityCoding
go 你不 gen 咋搞,谁喜欢 gen 啊,还不是因为没办法,golang 这语言就这样,json 搞得都那样何况 orm
用 gorm 还不如用 squirrel 这种拼语句的
lesismal
8 天前
@asuraa #24

> 1.2048 这个值是有考究的

硬件配置差别很大, 1c1g/16c64g/36c/512g, 对于这点协程数量的压力完全不同, 所以不论什么吋间规格都用 2048 写死了, 真没什么解释的必要.

> 2.worker 分配不均匀问题

可以参考下我的 nbio 里的实现:
https://github.com/lesismal/nbio/blob/master/conn.go#L185
原理是, 每个 conn 上一个执行队列, 每个 job 入队列并判断是不是队首, 如果是队首, 就用通用的协程池去异步循环处理挨个取出直到没有任务; 如果不是队首, 说明已有协程在循环执行, 则入队后直接返回并等待被执行即可. 这种实现下, 协程池的部分是不受限制的, 如果对协程池 size 没有限制就直接 go func(), 如果对协程池 size 有限制, 则协程池实现的部分限制即可.
游戏服务, 通常对最大在线量本身就有限制, 在 Acceptor 或者 login 的部分就可以做限制, 而且通常单节点在线量不会超大, 例如单节点 5k-5w 在线量, 通常都是 1w 以内, 而这种在线量在 8c16g 的机器上即使对应的每个连接一个异步任务的协程也压力不大, 而且 go func()执行完当前任务队列没任务后即可退出所以不需要持续占用协程, 再考虑实际并行和下游基础设施连接池之类的, 临时异步任务并发度对应的协程数量也多数小于实际连接数.
协程池的实现大概几种, 早期 ants 那种自己用 cond_t+队列或者 chan 配合 idle time idle size 的常驻协程方案并没有性能优势而且常驻协程的释放不及时, 而且 cond_t+毒烈或者 chan 的性能都有些损失, 除了可以限制协程数量, 绝大多数场景不如 go func().
字节家的 workerpool 是 size 限制 + list + go func() , 新任务判断当前协程池 size 并发度, 如果没达到限制就直接 go func()去循环执行 list 队列里的 job, 如果达到 size 限制里就入 list 队列等待被执行, 每个 go func()循环执行直到 list 队列里没有 job 里就退出. 这种方案是通用协程池, size 设置为 1 则也可以直接作为我上面说的 nbio 里每个 conn 的执行队列的方案, nbio 每个 conn 的执行队列没有用 list 而是用 slice, 是因为考虑单个 conn 任务数量不会太大所以 slice len 也不会太大, 所以用 slice 做队列复用并且省去了 pool get/put 之类的额外逻辑, 相比于 list 可能略有优势.
较高版本的 go, runtime 协程复用很好了, 如果不考虑 size 限制, 其他协程池实现基本都不如直接 go func()性能好, 字节家的这种 go func()好处是只比 go func()多了一点点逻辑但非常接近 go func()了, 但也有个缺点, 就是对 list 队列 size 没有限制, 如果下游阻塞并且上游不断有新的 job 进来, 理论上是存在无限增长的风险的.
lxzan https://github.com/lxzan/concurrency 与字节的类似, 用于队列的数据结构略有不同.
nbio 的协程池为了更均衡, 达到 size 限制前直接 go func(), 达到 size 了, 用 chan 做队列, chan 的好处是满了就阻塞了可以让上下游自动均衡, 规避了字节家 workerpool 的缺陷:
https://github.com/lesismal/nbio/blob/master/taskpool/taskpool.go
golang runtime, 例如 8c16g 这种配置, 通常的场景下, 10w 个协程数量都算还好, 压力不大. 而且 nbio 的协程池 size 默认是按核心数初始化, 多数也是跑不满 size 的. 而且用户也可以自己随便配置用什么协程池, 因为不管用什么协程池, nbio 的每个 conn 已经保证了自己的有序所以不需要协程池去保证顺序

> 3.代码估计没看仔细,是只有获取没创建的才需要初始化加锁的,后续获取是不需要的

这个确实没仔细看, 只是简单扫了几眼, 你说的是对的

> 这模块我们自己项目也在用,我们一百多万 dau ,跑的好好的。不过代码是比较简单,大家相互讨论印证也没问题。挺好的技术交流

因为 golang 本身性能还不错, 即使框架代码一般, 对于绝大多数的非性能和开销敏感的业务而言也是过剩的.
但这并不意味着这种框架本身优秀.
而且如 OP 在#4 和我在#11 说的, 简单游戏业务, 游戏脚手架就那几个简单的组建, 网络库的部分算是大头的了, 其他的太多现成的甚至标准库直接就够用了, 真的挺简单的 😂😂😂
lesismal
8 天前
@asuraa BTW, nbio 每个 conn 上一个执行队列配合通用协程池的方案, 对于其他语言也是适用的. 传统 c++框架, 为了保证时序, 多数是逻辑单线程, 对于有状态和多连接交互类并且 cpu 高消耗的业务就未必能充分发挥 cpu 了, 都可以考虑我这种方式优化.
nginx 这种为了提升 cpu 利用率搞 fork 多进程, 因为业务是 web 类, 在 nginx 这一层面连接之间没什么交互, 多进程的方式又可以避免多线程的锁竞争, 是非常合理的. 但游戏类业务多数涉及交互广播之类的, 就不太适合 fork 多进程的方式了
lesismal
7 天前
@asuraa 再补充一下, nginx 的多进程也有不均衡的问题, 比如很多连接均匀连到不同的 worker 进程上, 但是很多断开了, 剩下的大量连接分布在少量 worker 进程上, 仍然是不均衡的.
guanzhangzhang
7 天前
@lesismal #21 抛开 GUI ,如果做一个游戏 bot 呢,游戏客户端好多都是发送一个,接收单独的,接收报文消息解析都是 msgHandler ,发送我要查看某人资料,实际是有接收到资料的 handler 里处理,这样就异步了,写 bot 的话如何成同步
lesismal
7 天前
@guanzhangzhang #29

客户端的交互, 绝大多数项目, 本质上都是异步的.
因为客户端发消息基本上是在 eventloop 主线程触发的, 如果不是异步那就要卡渲染了, 客户端引擎的 eventloop 线程里的操作不应该有阻塞. 除非有特定需求是不需要主线程逻辑触发, 才可以考虑异步线程去同步 IO 的模式, 不影响 eventloop 主线程就行.

如果想"同步"代码, 要依赖不同的游戏引擎, 或者说, 主要是依赖你编写这部分逻辑使用的语言以及这个语言对"同步"支持的程度.
这里说的"同步"不是指同步 IO, 而是指类似协程这些让代码顺序看上去同步的语言特性. 比如 lua 可以用协程, js 可以用 Promise 甚至 async await? 一些引擎自带的语言并非标准语言而是变体, 那么则要看这些具体语言的特性了.

传统的游戏客户端服务端网络协议主要是命令号, 命令号对应 handler, 但每个 msg 和可能收到的响应不方便一一对应, 所以即使是协程/Promise 之类的看上去"同步"代码, 传统游戏协议也不方便把发送的 msg 和响应对应起来做这个同步.

arpc 的 js client 是默认支持了 Promise, 并且本身是通过 rpc 的方式实现的协议, 每个 call 都与响应一一对应, 所以可以这样用, 这样看上去就比较方便了:
https://github.com/lesismal/arpc/blob/master/examples/webchat/chat.html#L81
lesismal
7 天前
@guanzhangzhang #29

如果你们客户端使用的引擎语言支持标准 js, 可以试试用 arpc, 处理你说的"同步"这种问题, 比 msgHandler 简单方便多了.
shellus
7 天前
Golang 的 GC 机制注定了它不适合大型游戏,内存管理简直是灾难。你们是不是被 Golang 的“简洁”给骗了?用 C++或者 Rust 不好吗?性能和控制力都甩 Golang 几条街。还有那些推荐 Nakama 的卖云服务的套路看不出来吗?真要用 Golang ,Pitaya 还凑合
guanzhangzhang
7 天前
@lesismal #30 不是 lua 和 js ,就是 go ,没 GUI 的客户端 bot
lesismal
7 天前
@guanzhangzhang #33

Call 是请求需要对方响应, Notify 是只发消息不需要对方响应, Call 是同步的, CallAsync 是异步的.
client/server 两端都可以主动发起 Call/Notify.
arpc 是涵盖了传统游戏服务器网络库和 rpc 模式的, 所以支持的业务场景更广更好用.

要不你先看下 arpc 的例子吧, 有提问这功夫, 花几分钟看下 Readme 早就懂了都可以开始写逻辑了:
https://github.com/lesismal/arpc?tab=readme-ov-file#quick-start
lesismal
7 天前
@shellus #32
能对框架选型发问的用户, 绝大部分人不是用来做大型游戏, 这类需求的主要部分其实就是个网络库. 性能需求不大, golang 足够, 而且相比于 c++或者 rust, 开发效率爽死

真要是做大型游戏, 基本都自家团队内解决选型和研发问题了, 自家负责技术的人如果连框架架构的能力都没有的话还做毛的大型游戏, 如果大项目还要外面咨询选型的说明这项目从立项就凉了 80%了, 除非中间把技术的人换掉
librasolo
7 天前
liuhan907
6 天前
你换 Go 的目的是什么要想好。用目前的 LTS 的 dotnet 8 开发,无论是开发速度还是运行性能基本都比 Go 强。如果是因为公司要求或者人员技能构成的方面倒还无可厚非,如果单纯是觉得 Go 能带来比 C#更好的开发或者运行性能收益那我觉得你会亏爆。
picone
6 天前
@lesismal #12
附议。我也去看了下 async worker 这块,其实本质是想做限制 Goroutine 数量的 func 执行,那用 Go 的思路就有更简单的办法,毕竟对 Go 来说创建 Goroutine 本身成本并不大,并且有优先同核的亲和性,目前这种很可能会被调度到不同的核心去执行 cache 亲和性并不好。
ugpu
6 天前
@liuhan907 盈利性公司, 很多人都会 Go.所以用 Go. 前后端统一 C# 也是我想做的 有点防御性技术栈的味道了
ugpu
6 天前
@liuhan907 江湖不是技术来技术去 是打打杀杀 人情世故 天变了

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://tanronggui.xyz/t/1108840

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX