求一款比较成熟的 golang 服务端热更方案

250 天前
 htxy1985
如题,主要用于在有状态的 golang 服务端进行在线修复 bug ,找了几个方案感觉都不是很靠谱。
AI 提到的 air 和 hotswap 简单看了一下,都不怎么维护了。
特此来请教大佬们,有没有已经填过坑的较为成熟的框架或方案呢。
感谢感谢/抱拳
4409 次点击
所在节点    程序员
47 条回复
maocat
250 天前
啊,楼主的根本问题不是排查 bug 吗,热更只是一个路子,日志埋点排查业务 bug ,程序启用 pprof 查看系统 profile 信息,这不才是正经路子吗
sagaxu
250 天前
hotswap 也就开发环境用用,保证不了原子性和正确性。A 任务处理到一半,然后开始热更新,系统应该怎么处理?

所以现在主流更新,都是先从网关把目标机器的流量切走再更新,再灰度把流量切过去。
ben1iu
250 天前
go 有状态的热更 没有很好的办法 只能架构层面把 网关拆出去 网关后面的服务 新连接走新逻辑 旧的等连接断开不再接受新请求
go plugin 方式的热更 需要从代码结构拆分 不推荐
zihuyishi
250 天前
根据网游的经验,就是先上更新的服务器,不给旧的导流量,然后在 deadline 时把旧的切掉。
实际操作就是 k8s 上新 pod ,然后下旧 pod 。
pluswu1986
250 天前
1. goplugin 逻辑和状态分离,2. 大状态能且成足够小的粒度,用 config_route_shard 思路迁移小粒度对象,3. 大状态无法切割用 replicate 主副代理切换 理论上 3 种都能做到无损热更, 如果逻辑和状态强耦合(游戏服务器一般都是这种) 神都救不了, 各种原地热更都是邪教
Kauruus
249 天前
这还是从架构角度去整吧。

有状态服务,无论如何都要处理断线重连、重启的状态迁移问题。

新启一个实例,状态迁移过去,例如发消息给客户端,让它们连到新的实例。
sunny352787
249 天前
同行告诉你,没有

我这边的做法是无状态服务器,数据都塞 redis 里,实时性比较高的拆出去,比如 PVP 单独开一服,打完就断掉连接

如果你是做 ARPG 或者大世界之类的一定要保持长连接,那就加一层网关,后面连接逻辑服可以接收信号将数据用某种方式(比如 redis 中转)迁移到新进程然后再杀掉自己,这种方法以前写 C++时候就这么干

你想完全保存内存状态的迁移本质上不现实,代码更新了内存布局肯定有变化,非脚本语言不可能完美迁移的,脚本语言也只能迁移一部分逻辑内存数据变了也是不行的
bruce0
249 天前
看很多人的回答, 应该是没做过游戏这类的服务器开发, 楼主说的热更方案我之前也查了很多, 纯 go(不用 lua 和 js 这些脚本语言辅助)写的 有状态服务, 目前应该没有好的热更方案, 看很多人说加一个中间层, 这个真没法加,因为游戏的 server 进程有太多的全局状态和数据, 没法做到无感分层(或者非常难) 还有 k8s 滚动更新,这个更不靠谱了, web 这种无状态的服务还可以

说一下我之前了解到的"热更"方案
1. 双进程.类似 Nginx 的 reload, 新开一个进程, 新玩家连接到新的进程,老进程只处理老玩家数据,等所有人都下线,自动退出. 这个方案不太可行, 像是游戏内的全局数据, 跨进程同步比较难搞, 全新设计的游戏可以尝试,老游戏没戏,约等于重写

2. 使用 go plugin, 这个方案对代码改动也不小, 而且 go 的 plugin 官方维护也不积极,还有很多不稳定的东西, 而且使用起来也不方便, 还不如 C/C++ 动态加载 .so 文件好用.

这两个是我了解到的方案, 都不太好, 再拓展一下, 其实游戏内的很多热更只是更新策划表, 一般是 json,xml 的文件, 这时候可以在游戏内做一个命令, 比如游戏主逻辑内监听一个 web 端口, 收到 web 的命令后重新加载一下所有的表格文件.如果是更新游戏内的逻辑,修复 bug, 没办法. 有一种是游戏类型决定的, 像是 吃鸡, moba 这类的房间型游戏, 其实不用考虑热更, 这局对战完成后, 新的游戏直接用新的二进制启动就行了.像是 mmo 这类的, 如果硬要热更, 只能上 lua,js 这些脚本语言来辅助了.
yc8332
249 天前
好像没办法吧。。。有状态的感觉是更不了或者说成本很高,而且要一开始就是设计好。。反正现在我们是直接重启,主要是 pvp 的对战或者组队服务
hackroad
249 天前
蓝绿、A/B
p1gd0g
249 天前
有状态是大坑啊,还是早点改吧
renshengluguo
249 天前
参照英雄联盟的更新方式如何
rrfeng
249 天前
vm 都可以在线迁移,go 肯定也可以,只要你肯花时间 🐶
rrfeng
249 天前
@renshengluguo 一局游戏就是最大的不可更新时间了,完全没必要热更。MMO 才需要
luaex
249 天前
我之前用过 go plugin + hook 的方案,plugin 主要是用来加载 so ,然后 hook 要修改的函数,跳转到新的 so 里面的函数去执行
hongjic93
249 天前
goplugin (同一个进程,动态链接库),sidecar (不同进程,ipc ),wasm (进程内部 sandbox ) 。 第一个坑很多,ipc 很麻烦,wasm 最理想,但不成熟( golang 已经支持 wasi 编译)
htxy1985
249 天前
感谢各位的指教
realpg
249 天前
@southwolf #13
滚动更新版本,如果不涉及 websocket 超长连接,或者纯 tcp 业务,设计好都不会打断请求,都是平滑的
如果设计的好,tcp 长连接都可以不打断
这边就有业务有完全不能缺请求的要求 golang 做的 目前没问题
southwolf
249 天前
@realpg 所以还是有前提的嘛, rolling update 可以满足 zero downtime, 也就是旧 pod 承接现有连接,新连接去到新 pod, 但并不能保证旧连接能无缝迁移到新版本, 就是楼主要的在线更新
另外有种玩法, 就是状态信息放在外部, 然后客户端跟服务端搞个双向心跳, 挂了再由另外的服务端重新连接, 但这么一搞工作量又更大了😂
realpg
249 天前
@southwolf #39
我们的 api server 同时处理长短连接
在逻辑层,api server 可以接收一个外部信号
这个外部信号接收到以后,就会在 slb 的 service check api 中返回自己已经故障了
但是正常提供服务
这样 SLB 就不会发新的请求过来,这样基本只要 30 秒,就不太会有新的短 http 请求到这个 backend 了

长连接是在逻辑层实现的,一旦这个服务端接收到了外部信号,服务端的长连接就会通知客户端我要死了 你现在跟一个新的后端建立连接,建立连接后,有交接逻辑,在某个信号以后,就全跟新连接通信

这样基本上只要这个 signal 过来 60 秒,就可以完全无感停服务了

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

https://www.v2ex.com/t/1101408

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

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

© 2021 V2EX