如何使用 Go 语言写游戏服务器?

之前先后用Erlang,nodejs做过tcp,http的游戏服务器。接触了golang一两个月(纯新手),想在最近的tcp网游项目中使用,但又担心以下问题: # 如何高性能的搭建tcp底层,并且能负载到同时在线N多人 # 如何架构整个服务器端(包括网络层,缓存层,持久化层,日志层,逻辑分发处理层,通信协议层,以及如何有效部署) # goroutine间如何高效通信 # 担心go1.5版本及以后的gc问题 # 如何调试程序和快速定位线上问题 # 压力测试负载能力 希望用过golang的前辈给出一些建议~~

作者:达达 链接:http://www.zhihu.com/question/35385236/answer/62654548 来源:知乎 著作权归作者所有,转载请联系作者获得授权。

谢邀,昨天就有看到这问题,因为题太大不知道从何答起,所以就没答,今天易伟前辈邀请,不答不行了。 真有趣团队是从Go 1.0开始使用Go开发游戏服务端的,所以小经验有点,但是我们还处在不断学习摸索的阶段,所以太高深的学问不多,下面我就按题主的问题顺序尝试一个个的回答吧: # 如何高性能的搭建tcp底层,并且能负载到同时在线N多人 Go自身在特定平台会使用对应平台的io重用方案,比如epoll,kqueue等,所以底层部分效率已经不错了,比起自己用C/C++去封装底层或调用libevent之类的库,优势是Go将事件机制封装成了CSP模式,编程变得方便了,但是需要付出goroutine调度的开销,在游戏项目上实践的经验是调度开销可接受,无需做额外工作来优化。如果自己要在Go里面调用epoll重新封装一个网络层,这样做所能提升的效率和付出的代价对比起来,性价比太低了,不值得这么做。 所以用Go搭建TCP底层是很省事的,主要关注几个点: 1. 尽量减少系统的IO调用次数,比如使用bufio这个包来减少实际IO次数 2. 尽量减少不必要的数据拷贝,比如消息的封包解包过程,细心点设计是可以做到极少的数据拷贝的 3. IO阻塞时的边界情况处理,比如一个请求处理过程中,如果消息回发导致处理过程阻塞,是否会影响到其他后续请求,又或者广播过程中消息发送阻塞,是否应该把阻塞的连接关闭等 我这有个简单的库可以提供参考:funny/link · GitHub # 如何架构整个服务器端(包括网络层,缓存层,持久化层,日志层,逻辑分发处理层,通信协议层,以及如何有效部署) 这个议题挺大的,但是题主已经明确罗列出了这些项目层级和模块划分,说明是已经有经验的了。Go语言跟其他语言一样分层分模块,没太大特别之处。 Go在组织游戏项目的时候有一点需要提前预防,就是业务模块间的递归引用。Go从语法上是禁止包递归引用的。但是游戏的业务模块很多,交叉是很平常的事情,所以需要提前设计一个项目结构来防止业务上的交叉碰到Go的语法限制。 具体的代码我就不写在这边了,思路就是通过公共接口的注册来解耦包的引用关系,我这边有个演示项目可以参考一下,并没有什么高深的设计在里面,就是一个脑筋急转弯而已。funny/go-project-demo · GitHub 缓存层、持久层的实现方式不同团队差异巨大,这边我能分享的经验只有一点,就是尽量不要人工去维护缓存和持久化之间的关系,尽量做成自动的,这样才不会人工引发BUG导致数据损坏。 如果要说具体说法,我们目前是MySQL做持久存储,这样做数据分析和备份什么的都比较方便也比较可靠。缓存则是根据MySQL的结构自动生成代码映射到Go里边的。 Go做大数据量的缓存的时候需要小心GC的负载,如果你的缓存设计是内存吃得多但是对象很少,就不用担心这一点。如果是像我一样一条MySQL数据对应一个Go对象到内存里的,就要小心处理,要嘛做成按需加载的,减少对象数量。要嘛就是干脆用堆外内存来存储缓存数据,这样GC不会有负担。堆外内存的一些技巧我之前网上也有分享过了,原理比较简单,就是用cgo机制让C来分配内存。 通讯层也是各个项目差异很大的部分,我们团队是自己实现一套二进制协议格式,也有团队是用protobuf,也有用json,各式各样都有。这个按个人喜好和传统来做就可以了,差异不会差到哪里去的。 如果做自定义格式的协议,我这有个二进制操作的库可以用用:funny/binary · GitHub 部署方面其实跟语言无关,单进程的结构都很好运维和部署,多进程都会麻烦一些,所有语言都一样的,这方面我没有太值得分享的经验。 # goroutine间如何高效通信 goroutine就是靠chan通讯了,没什么好办法。如果关心goroutine通讯的各种开销,最好是按自己的应用场景测试看看。 有些场景下chan通讯是不划算的,比如一个简单的map数据获取,可能用锁就可以了。有些场景用chan是必须的,比如做个多人互动功能。 还有就是带buffer的chan和不带buffer的chan的差异,最好通过试验来让自己有个直观认识,除了异步和同步的差异,还会有边界情况的处理差异,比如带buffer的chan阻塞了,在功能设计上需要考虑,否则可能引发严重问题,这个上面其实也讲了。 # 担心go1.5版本及以后的gc问题 如果有这个担心,就最好从项目初期就提前预防,比如从设计上就避免产生大量对象,或者就是前面说的堆外内存分配,或者是通过多进程结构来分散负担。 还有就是提前做好测试,对量级有个心理预期。 游戏已经比实时交易系统好很多了,正常的用Go是不用担心GC延迟导致服务质量不符合需求的,游戏会产生大量对象的地方就是缓存了,这个地方小心设计基本上就没什么问题了。 1.5版本的GC我还没测试过,因为用了堆外内存,现在不怎么关心这个了。。。 # 如何调试程序和快速定位线上问题 调试Go确实有点麻烦,如果要用GDB调试Go,你最好关掉Go的编译优化,否则可能出现调试不了的情况。另外就是靠打印了,所以我们项目里面有这样一个模块:funny/debug · GitHub 线上问题定位要靠提前留好定位措施来实现,最常用的就是排查死锁和排查内存泄漏,可以参考一下这个模块:funny/pprof · GitHub 死锁的时候通过lookup goroutine来获取所有goroutine的堆栈跟踪信息,然后排查死锁的原因。 内存泄漏或者效率问题通过cpuprof和memprof来定位问题:Go语言程序的状态监控 保存cpuprof和memprof的工具函数在 funny/pprof 包里也有。 # 压力测试负载能力 游戏的完整压力测试我没做过,感觉没法做,游戏操作逻辑太复杂了。所以我的测试方式是对逐个可能成为瓶颈的点做benchmark或对算法做benchmark,来估计一个整体的效果。 另外就是开发期间持续监控所有请求的响应时间,我们团队的要求是在小于1毫秒,实际线上平均是30多微秒(不包含IO过程),有这样的响应速度,应该不用担心负载问题,如果有负载问题,会在请求执行时间上暴露出来。 用来监控请求执行时间的模块也在这个包里:funny/pprof · GitHub 能力范围内只能回答这些了,我最近在研究怎么进一步提高开发和运维的整体效率,所以感觉自己还很多东西不懂,懂的只是一些皮毛的东西,当抛砖引玉了。

原文发布于微信公众号 - Golang语言社区(Golangweb)

原文发表时间:2016-12-12

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏魏艾斯博客www.vpsss.net

wodpress 博客禁止对某篇文章发表评论的方法

1943
来自专栏互扯程序

到底什么是分布式系统,该如何学习

现在是资源共享的时代,同样也是知识分享的时代,如果你觉得本文能学到知识,请把知识与别人分享。

1465
来自专栏编程微刊

推荐一款稳定快速免费的前端开源项目 CDN 加速服务

前面学习到什么是CDN,全称是Content Delivery Network,即内容分发网络。CDN的通俗理解就是网站加速,CPU均衡负载。

6731
来自专栏性能与架构

Hotjar在架构演进中总结的8条经验

Hotjar 提供了帮网站主了解用户行为的服务,网站接上此服务后,可以生成用户的点击热区,录制用户的行为,查看各个页面的跳出路径以及停留时间等,根据这些统计数据...

3966
来自专栏企鹅号快讯

Smartnet 网络运维

「举一反三」 「继开源工具分享之后,本章系列文章将带来团队初尝自研的一些故事和技术分享、几个python模块、几个自动化空白工作领域等....」 1、作者介绍 ...

2659
来自专栏数据和云

【从根源出发,化风险为可控】应用到数据库的连接数管控

作者介绍 ? 巩飞(Morinson) 云和恩墨技术专家 网名Morinson,现服务于云和恩墨西北区,有14年在IT公司的技术类工作经验,特别是在 Ora...

3175
来自专栏企鹅号快讯

Git 企业开发者教程

为什么要写这样一个面向企业开发者的Git教程?这个问题也困扰我自己很久。其实我使用git的时间也不短了,但是就和正在阅读本文的每一位一样,常用的基本就是那么几个...

30310
来自专栏IT技术精选文摘

京东网络接入体系解密之高性能四层网关DLVS

DLVS诞生之初 在SLB这块,京东用户接入系统提供四层负载均衡服务和应用层负载均衡服务,对于公网流量接入部分,和很多公司一样采用的是四层和应用层负载相结合的架...

4999
来自专栏蓝天

使用protocol buffers优缺点分析

很早就听说过PB(protocol buffers的简称),但从未实际地去了解,最近动手对它进行了初步了了解和使用,由于它长得和RPC/IDL相似,加上以前有...

1022
来自专栏13blog.site

Spring+SpringMVC+MyBatis+easyUI整合基础篇(九)版本控制

作者:13 GitHub:https://github.com/ZHENFENG13 版权声明:本文为原创文章,未经允许不得转载。 前言 还好在第一篇文章...

3268

扫码关注云+社区

领取腾讯云代金券