Go语言(或 Golang)起源于 2007 年,并在 2009 年正式对外发布。Go 是非常年轻的一门语言,它的主要目标是“兼具 Python 等动态语言的开发速度和 C/C++ 等编译型语言的性能与安全性”。

网络游戏

要成功的运营一款网游,很大程度上依赖于玩家自发形成的社区。只有玩家自发形成一个稳定的生态系统,游戏才能持续下去,避免鬼城的出现。而这就需要多次大量导入用户,在同时在线用户量达到某个临界点的时候,才有可能完成。因此,多人同时在线十分有必要。

网络游戏程序特点

网游的常见玩法,除了排行榜这类统计和数据汇总的功能外,基本没有需要大量CPU时间的应用。以前的项目里,即时战斗产生的各种伤害计算对CPU的消耗也不大。玩家要完成一次操作,需要通过客户端-服务器端-客户端这样一个来回,为了获得高响应速度,满足玩家体验,服务器端的处理也不能占用太多时间。所以,每次请求对应的CPU占用是比较小的。

网络IO与磁盘IO

网络IO,这里主要分析游戏逻辑的IO。游戏逻辑的IO跟CPU占用的情况相似,每次请求的字节数很小,但由于多人同时在线,因此并发数相当高。另外,地图信息的广播也会带来比较频繁的网络通信。

磁盘IO方面,主要是游戏数据的保存。采用不同的数据库,会有比较大的区别。以前的项目里,就经历了从MySQL转向MongoDB这种内存数据库的过程,磁盘IO不再是瓶颈。总体来说,还是用内存做一级缓冲,避免大量小数据块读写的方案。

Golang服务器开发

针对网游的这些特点,golang的语言特性十分适合开发游戏服务器端,go语言提供goroutine机制作为原生的并发机制,每个goroutine所需的内存很少,实际应用中可以启动大量的goroutine对并发连接进行响应,遇到IO阻塞的时候,调度器就会自动切换到另一个goroutine执行,保证CPU不会因为IO而发生等待,就不需要利用多进程来榨取多核机器的性能了

每个玩家由自己的协程提供服务

// 网络接口
type IAgent interface {
   OnMessage
}

// 提供Message,序列化&反序列
type IMessage interface{
}

// 基于携程的Session封装
type Agent struct {
   recvChan chan IMessage
   sendChan chan IMessage
}

func (agent *Agent) recv() {
    // IO recv
    // recvChan<-IMessage
}
func (agent*Agent) send() {
   //msg := <- sendChan
   // IO send
}

func (agent *Agent) Send(msg IMessage) {
   // sendChan <- msg(
}

// 玩家Session网络
type Player struct {
   Agent
}
func (player* Player) OnMessage(){
   
}

通过上面的代码模型实现了一个单独玩家IO对象,这里也体现了一个chan的用法, go语言提供的这种协程间通信机制,十分优雅地揭示了协程通信的本质,避免了以往锁的显式使用带给程序员的心理负担,确是一大优势

Agent组件数据收发流程

GC问题

go语言提供的gc机制,以及对指针的保护式使用,可以大大减轻程序员的开发压力,提高开发效率,同时处理不好对象的管理将对影响程序的执行效率,GC的过程是很复杂的耗时的,网络游戏通信是非常频繁的而且基本都是小包传输,这个时候网络层就会出现不停New跟GC,可以通过golang的sync.Pool来管理对象

type Buffer struct {
    Data   []byte // buff
    bsize  int    // 大小
    refcnt int32  // 引用次数
}

type bufCacheInfo struct {
    maxbody int
    pool    *sync.Pool // 没错就是golang自带的Pool来管理对象
}

func newBuffer(sz int) *Buffer {
   m := &Buffer{}
   m.Data = make([]byte, sz)
   m.bsize = sz
  return m
}

// 放入不同大小的buff,64,128...
var bufCache = []bufCacheInfo{
{
    maxbody: 64,
    pool: &sync.Pool{
        New: func() interface{} {
           return newBuffer(64)
        },
      },
    }
}

// 使用
func NewBuffer(sz int) *Buffer {
    var buf *Buffer
    for i := range bufCache {
        if sz < bufCache[i].maxbody {
            buf = bufCache[i].pool.Get().(*Buffer)
            break
        }
    }
}

好的golang游戏服务器框架

本人使用golang实现的框架,该框架游戏以上线

推荐的开源的golang框架