1. 程式人生 > >雲原生專案實踐DevOps(GitOps)+K8S+BPF+SRE,從0到1使用Golang開發生產級麻將遊戲伺服器—第4篇

雲原生專案實踐DevOps(GitOps)+K8S+BPF+SRE,從0到1使用Golang開發生產級麻將遊戲伺服器—第4篇

![](https://img2020.cnblogs.com/blog/436453/202102/436453-20210217194414160-1510051665.jpg) ## 遊客登入鑑權之業務程式碼實戰 ### 系列文章 1. [從0到1使用Golang開發生產級麻將遊戲伺服器—第1篇](https://mp.weixin.qq.com/s/Jyq_A1vehrnMwv6AdOtQ1w) 2. [從0到1使用Golang開發生產級麻將遊戲伺服器—第2篇](https://mp.weixin.qq.com/s/jnQaz08fAzQ3J7tZBdil4Q) 3. [從0到1使用Golang開發生產級麻將遊戲伺服器—第3篇](https://mp.weixin.qq.com/s/ktGxVD5VBoSzbBb-vwxxOg) ### 介紹 這將是一個完整的,完全踐行 `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](https://github.com/Hacker-Linner/go-mahjong-server) ## VSCode REST Client 外掛 如果你是用 VSCode 作為 IDE,這個外掛不錯: ![](https://img2020.cnblogs.com/blog/436453/202102/436453-20210217194440209-1964410437.png) ## 遊客登入業務 ### 業務分析 [從0到1使用Golang開發生產級麻將遊戲伺服器—第3篇](https://mp.weixin.qq.com/s/ktGxVD5VBoSzbBb-vwxxOg) ### 業務 E-R 圖 ![](https://img2020.cnblogs.com/blog/436453/202102/436453-20210217194455282-159528682.png) ## API:查詢遊客登入是否啟用 ### REST Client 測試 API `Request`: ```sh POST http://192.168.31.125:12307/v1/user/login/query HTTP/1.1 content-type: application/json { "channelId": "konglai", "appId": "konglai" } ``` `Response`: ```json 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 } ``` ![](https://img2020.cnblogs.com/blog/436453/202102/436453-20210217194534315-934337761.png) ### 業務邏輯分析 1. 比較簡單,就是根據伺服器 `configs/config.toml` 檔案的配置,進行驗證: ```toml ... [login] guest = true lists = ["test", "konglai"] ... ``` ## API:遊客登入 ### REST Client 測試 API `Request`: ```sh 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`: ```json { "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` ```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 | 使用者資訊除錯 | ```go 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 三方平臺新增賬號)| ```go 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號`),確定當前遊客是否已經註冊 ```go user, err := db.QueryGuestUser(data.AppID, data.Device.IMEI) ``` `db.QueryGuestUser`,會從 `register` 或 `user` 表中去查詢使用者是否存在。 相關 `protocol` 的定義: `protocol/login.go` ```golang type LoginRequest struct { AppID string `json:"appId"` //使用者來自於哪一個應用 ChannelID string `json:"channelId"` //使用者來自於哪一個渠道 IMEI string `json:"imei"` Device Device `json:"device"` } ``` `protocol/common.go` ```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 } ``` 2. 如果沒有註冊,則生成一個新使用者,並且註冊一條使用者記錄 涉及到的相關 `db` 常量的定義: `db/const.go` ```go const ( StatusNormal = 1 //正常 StatusDeleted = 2 //刪除 StatusFreezed = 3 //凍結 StatusBound = 4 //繫結 ) const ( UserOffline = 1 //離線 UserOnline = 2 //線上 ) // Users表中role欄位的取值 const ( RoleTypeAdmin = 1 //管理員賬號 RoleTypeThird = 2 //三方平臺賬號 ) ``` 生成一個新使用者: ```go const defaultCoin = 10 // 預設給的房卡數量是 10 user = &model.User{ Status: db.StatusNormal, IsOnline: db.UserOffline, Role: db.RoleTypeThird, Coin: defaultCoin, } db.InsertUser(user) ``` 註冊一條使用者記錄 ```go db.RegisterUserLog(user, data.Device, data.AppID, data.ChannelID, protocol.RegTypeThird) //註冊記錄 ``` 3. 構造 `login` 響應資料 相關 `protocol` 的定義: `protocol/login.go` ```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` ```go type ( ClubItem struct { Id int64 `json:"id"` Name string `json:"name"` Desc string `json:"desc"` Member int `json:"member"` MaxMember int `json:"maxMember"` } // .... ) ``` 4. 插入登入記錄,返回客戶端所需資料 ```go device := protocol.Device{ IP: ip(r.RemoteAddr), Remote: r.RemoteAddr, } db.InsertLoginLog(user.Id, device, data.AppID, data.ChannelID) return resp, nil ``` 5. 一圖勝千言,秒懂 ![](https://img2020.cnblogs.com/blog/436453/202102/436453-20210217194548557-808716901.jpg) 關於遊戲伺服器登入與 Nano 遊戲伺服器通訊相關程式碼實戰,我們下篇再詳細討論。 ``` 我是為少 微信:uuhells123 公眾號:黑客下午茶 加我微信(互相學習交流),關注公眾號(獲取更多學習資料~