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

游客登录鉴权之业务代码实战
系列文章
介绍
这将是一个完整的,完全践行 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
 
VSCode REST Client 插件
如果你是用 VSCode 作为 IDE,这个插件不错:

游客登录业务
业务分析
业务 E-R 图

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
}

业务逻辑分析
- 比较简单,就是根据服务器 
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 | 注销时间 | 
- 根据 
AppID(用户来自于哪一个应用) 与Device.IMEI(设备的imei号),确定当前游客是否已经注册 
user, err := db.QueryGuestUser(data.AppID, data.Device.IMEI)
db.QueryGuestUser,会从 register 或 user 表中去查找用户是否存在。
相关 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
}
- 如果没有注册,则生成一个新用户,并且注册一条用户记录
 
涉及到的相关 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) //注册记录
- 构造 
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"`
	}
  // ....
)
- 插入登录记录,返回客户端所需数据
 
device := protocol.Device{
		IP:     ip(r.RemoteAddr),
		Remote: r.RemoteAddr,
	}
db.InsertLoginLog(user.Id, device, data.AppID, data.ChannelID)
return resp, nil
- 一图胜千言,秒懂
 

关于游戏服务器登录与 Nano 游戏服务器通信相关代码实战,我们下篇再详细讨论。
我是为少
微信:uuhells123
公众号:黑客下午茶
加我微信(互相学习交流),关注公众号(获取更多学习资料~)

                
            
        
浙公网安备 33010602011771号