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

简介: 云原生项目实践DevOps(GitOps)+K8S+BPF+SRE,从0到1使用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


VSCode REST Client 插件


如果你是用 VSCode 作为 IDE,这个插件不错:


微信图片_20220611152444.png


游客登录业务



业务分析


从0到1使用Golang开发生产级麻将游戏服务器—第3篇


业务 E-R 图

微信图片_20220611152503.png


API:查询游客登录是否启用



REST Client 测试 API


Request


POST http://192.168.31.125:12307/v1/user/login/query HTTP/1.1
content-type: application/json
{
    "channelId": "konglai",
    "appId": "konglai"
}


Response


HTTP/1.1 200 OK
Access-Control-Allow-Headers: Origin, Content-Type, Authorization
Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS
Access-Control-Allow-Origin: *
Content-Type: application/json; charset=utf-8
Date: Sun, 07 Feb 2021 15:00:16 GMT
Content-Length: 24
Connection: close
{
  "code": 0,
  "guest": true
}


微信图片_20220611152521.png


业务逻辑分析


  1. 比较简单,就是根据服务器 configs/config.toml 文件的配置,进行验证:


...
[login]
guest = true
lists = ["test", "konglai"]
...


API:游客登录



REST Client 测试 API


Request


POST http://127.0.0.1:12307/v1/user/login/guest HTTP/1.1
content-type: application/json
{
    "channelId": "konglai",
    "appId": "konglai",
    "imei": "c0a4ce912c48a3d0b17b59e6b97f1dca"
}


Response


{
  "code": 0,
  "name": "G1",
  "uid": 1,
  "headUrl": "http://wx.qlogo.cn/mmopen/s962LEwpLxhQSOnarDnceXjSxVGaibMRsvRM4EIWic0U6fQdkpqz4Vr8XS8D81QKfyYuwjwm2M2ibsFY8mia8ic51ww/0",
  "fangka": 10,
  "sex": 1,
  "ip": "192.168.31.125",
  "port": 33251,
  "playerIp": "192.168.31.125",
  "config": {
    "version": "1.9.3",
    "android": "https://fir.im/tand",
    "ios": "https://fir.im/tios",
    "heartbeat": 30,
    "forceUpdate": true,
    "title": "血战到底",
    "desc": "纯正四川玩法,快捷便利的掌上血战,轻松组局,随时随地尽情游戏",
    "daili1": "kefuweixin01",
    "daili2": "kefuweixin01",
    "kefu1": "kefuweixin01",
    "appId": "xxx",
    "appKey": "xxx"
  },
  "messages": ["系统消息:健康游戏,禁止赌博", "欢迎进入游戏"],
  "clubList": [],
  "debug": 0
}


业务逻辑分析


DB Model

db/model/struct.go


type User struct {
  Id              int64
  Algo            string `xorm:"not null VARCHAR(16) default"`
  Hash            string `xorm:"not null VARCHAR(64) default"`
  Salt            string `xorm:"not null VARCHAR(64) default"`
  Role            int    `xorm:"not null TINYINT(3) default 1"`
  Status          int    `xorm:"not null TINYINT(3) default 1"`
  IsOnline        int    `xorm:"not null TINYINT(1) default 1"`
  LastLoginAt     int64  `xorm:"not null index BIGINT(11) default"`
  PrivKey         string `xorm:"not null VARCHAR(512) default"`
  PubKey          string `xorm:"not null VARCHAR(128) default"`
  Coin            int64  `xorm:"not null BIGINT(20) default 0"`
  RegisterAt      int64  `xorm:"not null index BIGINT(20) default 0"`
  FirstRechargeAt int64  `xorm:"not null index BIGINT(20) default 0"`
  Debug           int    `xorm:"not null index TINYINT(1) default 0"`
}


用户表 描述
Id 自增ID
Algo 加密算法
Hash 加密hash
Salt 加密撒盐
Role 账号类型(RoleTypeAdmin=1 管理员账号,RoleTypeThird=2 三方平台账号)
Status 账号状态(StatusNormal=1 正常,StatusDeleted=2 删除,StatusFreezed=3 冻结,StatusBound=4 绑定)
IsOnline 是否在线(UserOffline=1 离线,UserOnline=2 在线)
LastLoginAt 最后登录时间
PrivKey 账号证书私钥
PubKey 账号证书公钥
Coin 房卡数量
RegisterAt 注册时间
FirstRechargeAt 首充时间
Debug 用户信息调试

type Register struct {
  Id           int64
  Uid          int64  `xorm:"not null index BIGINT(20) default"`
  Remote       string `xorm:"not null VARCHAR(40) default"`
  Ip           string `xorm:"not null VARCHAR(40) default"`
  Imei         string `xorm:"not null VARCHAR(128) default"`
  Os           string `xorm:"not null VARCHAR(20) default"`
  Model        string `xorm:"not null VARCHAR(20) default"`
  AppId        string `xorm:"not null index VARCHAR(32) default"`
  ChannelId    string `xorm:"not null index VARCHAR(32) default"`
  RegisterAt   int64  `xorm:"not null index BIGINT(11) default"`
  RegisterType int    `xorm:"not null index TINYINT(8) default"`
}


用户注册记录表 描述
Id 自增ID
Uid 用户ID
Remote 外网IP
Ip 内网IP
Model 硬件型号
Imei 设备的imei号
Os os版本号
AppId 应用id
ChannelId 渠道id
RegisterAt 注册时间
RegisterType 注册类型(RegTypeThird=5 三方平台添加账号)

type Login struct {
  Id        int64
  Uid       int64  `xorm:"not null index BIGINT(20) default"`
  Remote    string `xorm:"not null VARCHAR(40) default"`
  Ip        string `xorm:"not null VARCHAR(40) default"`
  Model     string `xorm:"not null VARCHAR(64) default"`
  Imei      string `xorm:"not null VARCHAR(32) default"`
  Os        string `xorm:"not null VARCHAR(64) default"`
  AppId     string `xorm:"not null VARCHAR(64) default"`
  ChannelId string `xorm:"not null VARCHAR(32) default"`
  LoginAt   int64  `xorm:"not null BIGINT(11) default"`
  LogoutAt  int64  `xorm:"not null BIGINT(11) default"`
}


用户登录记录表 描述
Id 自增ID
Uid 用户ID
Remote 外网IP
Ip 内网IP
Model 硬件型号
Imei 设备的imei号
Os os版本号
AppId 应用id
ChannelId 渠道id
LoginAt 登录时间
LogoutAt 注销时间

  1. 根据 AppID(用户来自于哪一个应用) 与 Device.IMEI(设备的imei号),确定当前游客是否已经注册


user, err := db.QueryGuestUser(data.AppID, data.Device.IMEI)


db.QueryGuestUser,会从 registeruser 表中去查找用户是否存在。

相关 protocol 的定义:

protocol/login.go


type LoginRequest struct {
  AppID     string `json:"appId"`     //用户来自于哪一个应用
  ChannelID string `json:"channelId"` //用户来自于哪一个渠道
  IMEI      string `json:"imei"`
  Device    Device `json:"device"`
}


protocol/common.go


type Device struct {
  IMEI   string `json:"imei"`   //设备的imei号
  OS     string `json:"os"`     //os版本号
  Model  string `json:"model"`  //硬件型号
  IP     string `json:"ip"`     //内网IP
  Remote string `json:"remote"` //外网IP
}


  1. 如果没有注册,则生成一个新用户,并且注册一条用户记录

涉及到的相关 db 常量的定义:

db/const.go


const (
  StatusNormal  = 1 //正常
  StatusDeleted = 2 //删除
  StatusFreezed = 3 //冻结
  StatusBound   = 4 //绑定
)
const (
  UserOffline = 1 //离线
  UserOnline  = 2 //在线
)
// Users表中role字段的取值
const (
  RoleTypeAdmin = 1 //管理员账号
  RoleTypeThird = 2 //三方平台账号
)


生成一个新用户:


const defaultCoin = 10 // 默认给的房卡数量是 10
user = &model.User{
  Status:   db.StatusNormal,
  IsOnline: db.UserOffline,
  Role:     db.RoleTypeThird,
  Coin:     defaultCoin,
}
db.InsertUser(user)


注册一条用户记录


db.RegisterUserLog(user, data.Device, data.AppID, data.ChannelID, protocol.RegTypeThird) //注册记录


  1. 构造 login 响应数据

相关 protocol 的定义:

protocol/login.go


type LoginResponse struct {
  Code     int          `json:"code"`
  Name     string       `json:"name"`
  Uid      int64        `json:"uid"`
  HeadUrl  string       `json:"headUrl"`
  FangKa   int64        `json:"fangka"`
  Sex      int          `json:"sex"` //[0]未知 [1]男 [2]女
  IP       string       `json:"ip"`
  Port     int          `json:"port"`
  PlayerIP string       `json:"playerIp"`
  Config   ClientConfig `json:"config"`
  Messages []string     `json:"messages"`
  ClubList []ClubItem   `json:"clubList"`
  Debug    int          `json:"debug"`
}
type ClientConfig struct {
  Version     string `json:"version"`
  Android     string `json:"android"`
  IOS         string `json:"ios"`
  Heartbeat   int    `json:"heartbeat"`
  ForceUpdate bool   `json:"forceUpdate"`
  Title string `json:"title"` // 分享标题
  Desc  string `json:"desc"`  // 分享描述
  Daili1 string `json:"daili1"`
  Daili2 string `json:"daili2"`
  Kefu1  string `json:"kefu1"`
  AppId  string `json:"appId"`
  AppKey string `json:"appKey"`
}


protocol/club.go


type (
  ClubItem struct {
    Id        int64  `json:"id"`
    Name      string `json:"name"`
    Desc      string `json:"desc"`
    Member    int    `json:"member"`
    MaxMember int    `json:"maxMember"`
  }
  // ....
)


  1. 插入登录记录,返回客户端所需数据


device := protocol.Device{
    IP:     ip(r.RemoteAddr),
    Remote: r.RemoteAddr,
  }
db.InsertLoginLog(user.Id, device, data.AppID, data.ChannelID)
return resp, nil


  1. 一图胜千言,秒懂


微信图片_20220611152619.jpg



关于游戏服务器登录与 Nano 游戏服务器通信相关代码实战,我们下篇再详细讨论。

相关实践学习
容器服务Serverless版ACK Serverless 快速入门:在线魔方应用部署和监控
通过本实验,您将了解到容器服务Serverless版ACK Serverless 的基本产品能力,即可以实现快速部署一个在线魔方应用,并借助阿里云容器服务成熟的产品生态,实现在线应用的企业级监控,提升应用稳定性。
云原生实践公开课
课程大纲 开篇:如何学习并实践云原生技术 基础篇: 5 步上手 Kubernetes 进阶篇:生产环境下的 K8s 实践 相关的阿里云产品:容器服务 ACK 容器服务 Kubernetes 版(简称 ACK)提供高性能可伸缩的容器应用管理能力,支持企业级容器化应用的全生命周期管理。整合阿里云虚拟化、存储、网络和安全能力,打造云端最佳容器化应用运行环境。 了解产品详情: https://www.aliyun.com/product/kubernetes
相关文章
|
JavaScript 算法 前端开发
探索使用 Golang 和 Webassembly 构建一个多人游戏服务器
探索使用 Golang 和 Webassembly 构建一个多人游戏服务器
342 0
探索使用 Golang 和 Webassembly 构建一个多人游戏服务器
|
Kubernetes Cloud Native Devops
云原生项目实践DevOps(GitOps)+K8S+BPF+SRE,从0到1使用Golang开发生产级麻将游戏服务器—第7篇
云原生项目实践DevOps(GitOps)+K8S+BPF+SRE,从0到1使用Golang开发生产级麻将游戏服务器—第7篇
221 0
云原生项目实践DevOps(GitOps)+K8S+BPF+SRE,从0到1使用Golang开发生产级麻将游戏服务器—第7篇
|
Cloud Native Devops Go
云原生项目实践DevOps(GitOps)+K8S+BPF+SRE,从0到1使用Golang开发生产级麻将游戏服务器—第1篇(二)
云原生项目实践DevOps(GitOps)+K8S+BPF+SRE,从0到1使用Golang开发生产级麻将游戏服务器—第1篇(二)
175 0
云原生项目实践DevOps(GitOps)+K8S+BPF+SRE,从0到1使用Golang开发生产级麻将游戏服务器—第1篇(二)
|
Cloud Native Devops 关系型数据库
云原生项目实践DevOps(GitOps)+K8S+BPF+SRE,从0到1使用Golang开发生产级麻将游戏服务器—第1篇(一)
云原生项目实践DevOps(GitOps)+K8S+BPF+SRE,从0到1使用Golang开发生产级麻将游戏服务器—第1篇(一)
201 0
云原生项目实践DevOps(GitOps)+K8S+BPF+SRE,从0到1使用Golang开发生产级麻将游戏服务器—第1篇(一)
|
Kubernetes Devops 关系型数据库
生产级Golang麻将游戏服务器 NanoServer 二次开发,利用Drone CI/CD打通DevOps上 K8S 迭代流程
生产级Golang麻将游戏服务器 NanoServer 二次开发,利用Drone CI/CD打通DevOps上 K8S 迭代流程
606 0
生产级Golang麻将游戏服务器 NanoServer 二次开发,利用Drone CI/CD打通DevOps上 K8S 迭代流程
|
存储 Cloud Native 应用服务中间件
Golang 云原生分布式社交游戏服务器,Nakama Server 简介
Golang 云原生分布式社交游戏服务器,Nakama Server 简介
1180 0
|
JSON Cloud Native 网络协议
探索 Golang 云原生游戏服务器开发,3 分钟快速理解 Nano 通信协议(内含记忆脑图)
探索 Golang 云原生游戏服务器开发,3 分钟快速理解 Nano 通信协议(内含记忆脑图)
480 0
|
Cloud Native 前端开发 Go
探索 Golang 云原生游戏服务器开发,根据官方示例实战 Gorilla WebSocket 的用法
探索 Golang 云原生游戏服务器开发,根据官方示例实战 Gorilla WebSocket 的用法
629 0
|
1月前
|
SQL 前端开发 Go
编程笔记 GOLANG基础 001 为什么要学习Go语言
编程笔记 GOLANG基础 001 为什么要学习Go语言
|
3月前
|
物联网 Go 网络性能优化
使用Go语言(Golang)可以实现MQTT协议的点对点(P2P)消息发送。MQTT协议本身支持多种消息收发模式
使用Go语言(Golang)可以实现MQTT协议的点对点(P2P)消息发送。MQTT协议本身支持多种消息收发模式【1月更文挑战第21天】【1月更文挑战第104篇】
100 1