用 Go 给休闲小游戏写服务端,我踩过的 7 个坑
我做休闲小游戏,服务端一直用 Go。写了几年,踩了不少坑。今天把这 7 个坑一次性倒出来,都是真金白银换来的。
坑一:map 并发读写,直接 panic
Go 的 map 不是并发安全的。多个 goroutine 同时读写同一个 map,会直接 panic,而且这个 panic 不是必现的,压测的时候才炸。
解法:加锁,或者用 sync.Map。玩家数据、房间数据这类高频读写的,一定用带锁的结构包一层。别心存侥幸,我线上炸过一次,凌晨两点爬起来修。
坑二:goroutine 泄漏,内存慢慢涨
每个玩家连接开一个 goroutine 处理,连接断了 goroutine 没退出,内存就一点点涨,跑个几天服务就 OOM。
解法:连接关闭时,用 context 或者 channel 通知对应的 goroutine 退出。写完连接管理,记得压测时盯着 goroutine 数量,别只盯内存。
坑三:心跳没做好,死连接占着坑
玩家手机切后台、断网,TCP 连接还在服务端挂着。不做心跳检测,死连接越积越多,白白占用资源。
解法:定时心跳,超时踢下线。踢之前记得把玩家数据落盘,别把进度搞丢。
坑四:数据只存内存,重启全丢
为了快,把玩家数据全放内存,重启进程就清空。玩家辛辛苦苦打的进度没了,投诉能淹了你。
解法:关键数据定时落盘,或者用 SQLite、Redis 这类持久化。休闲小游戏数据量不大,SQLite 完全够用。
坑五:消息没做长度限制
客户端发一个超大消息,服务端傻乎乎地读,内存直接被打爆。这是最简单的攻击手段,也是最多人忽略的。
解法:读消息前先校验长度,超了直接断开。这条真的重要,我见过有人的服务就是这么被打挂的。
坑六:没做优雅关闭
上线发新版本,直接 kill 进程,正在处理的请求全丢,玩家被踢。
解法:监听退出信号,先停止接新连接,等处理中的请求跑完,再退出。几行代码的事,体验完全不一样。
坑七:上线前没压测
开发环境跑得溜,一上线人就多了。并发一上来,各种隐藏问题全暴露。
解法:上线前用压测工具打一轮,重点关注 goroutine 数、内存、连接数这几个指标。压测一次,抵得上线上炸三次。
写在最后
Go 写游戏服务端,性能是真好,坑也是真多。这 7 个坑,每一个都是我在真实项目里踩过的,写出来就是想让后来人少交点学费。
关注公众号「西米分享」,我还在写 Go、Cocos 的实战系列,回复【服务端】领一份我整理的 Go 游戏服务端避坑清单。
—— 西米,一个 Java、Go 双修的老程序员
完整代码、踩坑清单与最新进展,都在公众号。
📱 关注公众号「西米分享」