代码改变世界

GoNet项目学习笔记(一)

2015-07-14 21:59  量子与唯心  阅读(578)  评论(0编辑  收藏  举报

  【题记】近一段时间一直想学几种不是那么主流的(C/C++/JAVA/.NET)语言,于是便把自己不熟悉的脚本语言拿来学习,从Perl到PHP、 Python,总感觉有种不是很亲切的感觉,直到遇见GO。遇见GO后立马喜欢上了它,简洁不简单,强大不臃肿,美妙不可言喻。

  看完GO的基本语法,便想找个开源项目边研究边巩固基础,于是在Git上找了找,很有幸看到GoNet项目,项目规模不大但功能却很完善,实现了一个基础的游戏服务器通信框架,是入门的不可多得之选。

一、项目介绍

地址:https://github.com/xtaci/gonet

介绍直接取用官网:

部署:

  • Game Server(GS):
    玩家直接连接GS, 处理玩家逻辑,并与 HUB/SS 通信,GS存在若干个。
  • Hub Server(HUB):
    若干个GS 连接到一个HUB, 只存在一个HUB,维护基础的全局信息,以及 GS<--->GS 的消息转发.
  • Stats Server(SS):
    统计服务器,根据玩家的行为,记录策划需要的数据,以便于后期统计。
    统计属于事后分析,数据量较大,性能需求不同, 故单独列为一个服务器。

通信原则:

  1. GS到HUB/SS的通信,都是Call同步调用,即GS必须等待ACK。
  2. HUB到GS的通信,只有forward数据包。
  3. 单播消息在玩家离线时会存入db, 登录后的启动过程 GS 直接读取db,并forward给玩家goroutine。(持久化)
  4. 多播消息会发送给所有的在线玩家(非持久化)
  5. 广播消息会发送给所有的在线玩家(非持久化)

服务器状态一致性

    1. GS节点可以单独重启
    2. HUB 重启后,GS必须全部重启
    3. SS 可随意重启,不影响业务

本文着重分析GS、HUB、Timer等模块的启动,以及典型的业务流程。

二、模块启动分析

一)HUB模块启动分析

     请结合代码阅读

  1、启动时,先初始化导入的包:

 1) Package cfg初始化。----加载所有配置项到map容器。

 2) Package core初始化

 a) 初始化导入的包misc/timer (定时器模块初始化)

     ----初始化定时器资源并启动定时器routine,守护定时器队列。

      b) 初始化db模块

       ----连接数据库服务器

 c) 初始化导入的包 hub/core有限状态机初始化 

                ----初始化玩家信息;启动状态机协程维护玩家状态

  2、启动Signal routine,处理SIGHUP信号,重新加载配置。

  3、启动Sys routine,主要用于定期强制内存回收

  4、启动TCP监听,用于接收客户端连接

  5、从数据库加载所有用户

    ----根据用户id构造玩家信息,记录到全局map(user.id, PlayerInfo)

  6、循环接收客户端(GameServer)连接,一个连接一个routine。

  启动流程时序图:

     

上图中

  1. Agent Routine启动时会为每个客户端(Game server)生成唯一HOST ID(图中标为Client ID)并创建转发队列,然后将HOST ID和转发队列加入到全局Map中。转发队列用来接收其它Game server发过来的消息,全局Map以便于索引到转发队列。那么问题来了,用户的User ID是如何和HOST ID发生关系的?例如在GS1上登陆的用户A向在GS5上登陆的用户B发消息,HUB是如何转发到GS5上的?我认为这一块相对较模糊,起码在流程上没有贯穿,系统中倒是有相关的实现:在用户登陆时HUB会记录该用户所在的HOST ID,当转发用户消息时,根据目标用的ID查找到HOST ID。
  2. 在接收到客户端的消息后,交给Proxy处理,最后到具体的协议处理函数中处理,完成后根据情况回响应消息。