介绍
这将是一个完整的,完全践行 DevOps/GitOps
与 Kubernetes
上云流程的 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,握手阶段至此成功结束。
接下来我们看到如下日志:
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"` }
一图胜千言