云原生项目实践DevOps(GitOps)+K8S+BPF+SRE,从0到1使用Golang开发生产级麻将游戏服务器—第5篇


游戏服务器(Nano)登录 & 游戏数据包通信实战

系列文章

  1. Golang开发生产级麻将游戏服务器—第1篇
  2. Golang开发生产级麻将游戏服务器—第2篇
  3. Golang开发生产级麻将游戏服务器—第3篇
  4. Golang开发生产级麻将游戏服务器—第4篇

介绍

这将是一个完整的,完全践行 DevOps/GitOpsKubernetes 上云流程的 Golang 游戏服务器开发的系列教程。

这个系列教程是对开源项目 Nanoserver 的完整拆解,旨在帮助大家快速上手 Golang(游戏)服务器后端开发。通过实践去理解 Golang 开发的精髓 —— Share memory by communication(通过通信共享内存)

同时这个项目可能还会涉及到 Linux 性能调优(BPF 相关的工具)和系统保障(SRE)的相关的工作。

Step-By-Step 开发 Mahjong Server

  • 单体架构理解 Mahjong Server 业务 -> Nano Distributed Game Server(分布式) + 微服务 改造。
  • Demo:go-mahjong-server

客户端连接游戏服务器

游戏服务器开启 Debug Mode

服务端加入 nano.WithDebugMode() 后,会有详细的日志输出。

// internal/game/game.go
nano.Listen(addr,
  nano.WithPipeline(pip),
  nano.WithHeartbeatInterval(time.Duration(heartbeat)*time.Second),
  nano.WithLogger(log.WithField("component", "nano")),
  nano.WithSerializer(json.NewSerializer()),
  nano.WithComponents(comps),
  nano.WithDebugMode(),
)

查看登录日志

...
...
time="2021-02-18T23:01:16+08:00" level=info msg="New session established: Remote=192.168.31.125:62569, LastTime=1613660476" component=nano
time="2021-02-18T23:01:16+08:00" level=info msg="Session handshake Id=2, Remote=192.168.31.125:62569" component=nano
time="2021-02-18T23:01:16+08:00" level=info msg="Receive handshake ACK Id=2, Remote=192.168.31.125:62569" component=nano
time="2021-02-18T23:01:16+08:00" level=info msg="UID=0, Message={Request Manager.Login (195bytes)}, Data=&{Name:G1 Uid:1 HeadUrl:http://wx.qlogo.cn/mmopen/s962LEwpLxhQSOnarDnceXjSxVGaibMRsvRM4EIWic0U6fQdkpqz4Vr8XS8D81QKfyYuwjwm2M2ibsFY8mia8ic51ww/0 Sex:1 FangKa:10 IP:192.168.31.125}" component=nano
time="2021-02-18T23:01:16+08:00" level=info msg="玩家: 1登录: &{Name:G1 Uid:1 HeadUrl:http://wx.qlogo.cn/mmopen/s962LEwpLxhQSOnarDnceXjSxVGaibMRsvRM4EIWic0U6fQdkpqz4Vr8XS8D81QKfyYuwjwm2M2ibsFY8mia8ic51ww/0 Sex:1 FangKa:10 IP:192.168.31.125}"
time="2021-02-18T23:01:16+08:00" level=info msg="玩家: 1不在线,创建新的玩家"
time="2021-02-18T23:01:16+08:00" level=info msg="Add session to group _SYSTEM_MESSAGE_BROADCAST, ID=2, UID=1" component=nano
time="2021-02-18T23:01:16+08:00" level=info msg="Type=Response, ID=2, UID=1, MID=1, Data=&{Uid:1 Nickname:G1 HeadUrl:http://wx.qlogo.cn/mmopen/s962LEwpLxhQSOnarDnceXjSxVGaibMRsvRM4EIWic0U6fQdkpqz4Vr8XS8D81QKfyYuwjwm2M2ibsFY8mia8ic51ww/0 Sex:1 FangKa:10}" component=nano
time="2021-02-18T23:01:16+08:00" level=info msg="[SQL] SELECT `id`, `algo`, `hash`, `salt`, `role`, `status`, `is_online`, `last_login_at`, `priv_key`, `pub_key`, `coin`, `register_at`, `first_recharge_at`, `debug` FROM `user` WHERE `id`=? LIMIT 1 []interface {}{1}" component=model orm=xorm
time="2021-02-18T23:01:16+08:00" level=info msg="Type=Push, ID=2, UID=1, Route=onCoinChange, Data=&{Coin:10}" component=nano
time="2021-02-18T23:01:16+08:00" level=info msg="Receive handshake ACK Id=2, Remote=192.168.31.125:62569" component=nano
time="2021-02-18T23:01:16+08:00" level=info msg="UID=1, Message={Request DeskManager.UnCompleteDesk (2bytes)}, Data=[91 93]" component=nano
time="2021-02-18T23:01:16+08:00" level=debug msg="DeskManager.UnCompleteDesk: 玩家不在房间内" player=1
time="2021-02-18T23:01:16+08:00" level=info msg="Type=Response, ID=2, UID=1, MID=2, Data=&{Exist:false TableInfo:{DeskNo: CreatedAt:0 Creator:0 Title: Desc: Status:创建 Round:0 Mode:0}}" component=nano
...
...

分析日志

很清晰的看到三条 info 级别的 log

  • New session established(有一个连接进来了,建立新的会话)
  • Session handshake(客户端向服务器发起握手请求)
  • Receive handshake ACK(握手成功,客户端向服务器发送一个握手 ACK

其实这个就是游戏客户端与游戏服务器(Nano框架)的握手??过程。

当底层连接建立后,客户端向服务器发起握手请求,并附带必要的数据。服务器检验握手数据后,返回握手响应。如果 握手成功,客户端向服务器发送一个握手ack,握手阶段至此成功结束。更多,请查看官方Nano 通讯协议

接下来我们看到如下日志:

UID=0, Message={Request Manager.Login (195bytes)}, Data=&{Name:G1 Uid:1 HeadUrl:http://wx.qlogo.cn/mmopen/s962LEwpLxhQSOnarDnceXjSxVGaibMRsvRM4EIWic0U6fQdkpqz4Vr8XS8D81QKfyYuwjwm2M2ibsFY8mia8ic51ww/0 Sex:1 FangKa:10 IP:192.168.31.125}

表示游戏客户端(Game Client)向游戏服务器(Game Server)的路由 Manager.Login 发送了一个消息类型为 Request 的数据包。

Manager.Login

关于 Nano 基础知识,这里不在赘述:
5 分钟上手 Nano 游戏服务器框架

业务代码逻辑分析

  • 为当前 Session 绑定玩家 uid
  • 查询玩家列表
  • 玩家不在线,创建新的玩家
    • 玩家的游戏数据初始化
    • 绑定 Session 到当前玩家
    • 异步从数据库同步房卡
    • 将玩家加入到玩家列表统一管理
  • 玩家在线
    • 重置之前的session
    • 绑定新session
  • 添加到广播频道
  • 响应结果

涉及到的通信协议:

protocol/login.go

type LoginToGameServerRequest struct {
	Name    string `json:"name"`
	Uid     int64  `json:"uid"`
	HeadUrl string `json:"headUrl"`
	Sex     int    `json:"sex"` //[0]未知 [1]男 [2]女
	FangKa  int    `json:"fangka"`
	IP      string `json:"ip"`
}

type LoginToGameServerResponse struct {
	Uid      int64  `json:"acId"`
	Nickname string `json:"nickname"`
	HeadUrl  string `json:"headURL"`
	Sex      int    `json:"sex"`
	FangKa   int    `json:"fangka"`
}

一图胜千言

我是为少
微信:uuhells123
公众号:黑客下午茶
加我微信(互相学习交流),关注公众号(获取更多学习资料~)

相关