Skip to content

L2ncE/go-chess

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

41 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

go-chess

红岩网校工作站2022春季后端期末考核:双人象棋

目录

功能实现

基础功能

  • 用户登陆注册更改密码

  • 加入房间

    • 玩家能主动开房,能够指定房间号加入房间
  • 同一房间最多两人进入

  • 房间内

    • 玩家可以切换准备和未准备状态
  • 两玩家都处于准备状态时可以自行开启游戏(本地)

  • 游戏对战

    • 以坐标的形式处理棋子位置信息
  • 固定的棋盘大小

  • 双方轮流着棋

  • 判断获胜条件,在获胜条件出现后结束游戏

  • 部署

    • 使用Docker部署
  • 客户端显示效果

    • 使用Ebiten第三方库实现

加分项

功能类

  • 房间
    • 房间内玩家聊天
    • 对低俗玩家踢出房间(说脏话超过三次)
    • 可以多房间同时进行,一名用户也可以同时进入多个房间

技术类

  • 服务拆分,用户中心使用gRPC重构,并使用etcd进行服务发现
  • 使用redis缓存,使服务能够承受更高的负载
  • 撰写Dockerfile生成仅有18m的镜像
  • 使用pprof进行性能调优
  • 使用Viper进行项目配置,并支持热重载配置
  • 使用cron定时任务进行无用缓存的删除

接口说明

https://www.postman.com/yuanxinhao/workspace/chess

注册 POST

42.192.155.29:6666/user/register

BODY

KEY DESCRIPTION
username 必填
password 必填
question 可选
answer 可选

登录 POST

42.192.155.29:6666/user/login

BODY

KEY DESCRIPTION
username 必填
password 必填

改密码 PUT

42.192.155.29:6666/user/password

HEADER

KEY DESCRIPTION
TOKEN 必填

BODY

KEY DESCRIPTION
username 必填
old_password 必填
new_password 必填

切换准备状态 GET

42.192.155.29:6666/ready/:room_id

HEADER

KEY DESCRIPTION
TOKEN 必填

PARAM

KEY DESCRIPTION
room_id 必填

加入房间 WebSocket

ws://42.192.155.29:6666/?room_id=red

HEADER

KEY DESCRIPTION
TOKEN 必填

PARAM

KEY DESCRIPTION
room_id 必填

加分项实现

WebSocekt禁言操作

// 不合法信息3次,判断是否有不合法信息,没有进行信息发布
   if c.limitNum >= 3 {
      h.kickoutroom <- m
      log.Println("素质太低,给你踢出去")
      _ = c.ws.Close() //
   } else //没有超过三次,可以继续
   {
      baseStr := "死傻操" //违法字符
      testStr := string(msg[:])
      for _, word := range testStr {
         //遍历是否有违法字符
         res := strings.Contains(baseStr, string(word))
         if res == true {
            c.limitNum += 1
            c.forbiddenWord = true //禁言
            //记录禁言开始时间
            c.timeLog = time.Now().Unix()
            h.warnings <- m
            break
         }
      }
      // 不禁言,消息合法 可以发送
      if c.forbiddenWord != true {
         // 通过所有检查,进行广播

         m := message{msg, m.roomId, m.name, c}
         h.broadcast <- m
      }
       
   }

gRPC + ETCD

撰写proto文件

syntax = "proto3";

package user;

option go_package = "./user";

message RegisterReq{
  string username = 1;
  string password = 2;
  string question = 3;
  string answer = 4;
  string uuid = 5;
}

message RegisterRes{
  bool status = 1;
  string description = 2;
}

message LoginReq{
  string username = 1;
  string password = 2;
}

message LoginRes{
  bool status = 1;
  string token = 2;
  string description = 3;
}

message changeReq {
  string old_password = 1;
  string new_password = 2;
  string username = 3;
}

message changeRes {
  bool status = 1;
  string description = 2;
}

service UserCenter{
  rpc Register(RegisterReq) returns (RegisterRes);
  rpc Login(LoginReq) returns(LoginRes);
  rpc changePW (changeReq) returns (changeRes);
}

通过./一个cmd文件自动生成代码

protoc --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative ./user.proto

接下来就是进行分层,然后分别撰写server.go以及client.go并server中将服务加入到etcd中

redis缓存 cron定时任务

在房间模块中使用redis,当你选择进入房间后会在redis中产生此房间的集合(并且最多能容纳两个人),退出时会将你从房间中删掉,定时任务会每个小时扫一遍,房间为空则删除。下次又有用户进入此房间的话则再次生成此房间集合

准备状态也是使用redis实现,也就是使用的最经典的点赞系统那套,点一次是准备,再点一次就取消,是根据集合中是否有你这个用户而实现的(依赖集合中成员不可重复的特性)

样例

//cron
_, err := c.AddFunc("@every 1h", func() {
   err := redis.DeleteEmptyRoom()
   if err != nil {
      log.Println("cron err", err)
      return
   }
})
//redis
func DeleteEmptyRoom() error {
   set, err := rdb.SMembers("room").Result()
   if err != nil {
      log.Println("len of room get error:", err)
      return err
   }
   for _, v := range set {
      es, err := rdb.SMembers("room_" + v).Result()
      if err != nil {
         log.Println(err)
         return err
      }
      if len(es) <= 0 {
         rdb.Del("room_" + v)
      }
   }
   return nil
}

Docker

就是摈弃掉多余的内容,使用镜像

FROM golang:alpine AS builder

LABEL stage=gobuilder

ENV CGO_ENABLED 0
ENV GOPROXY https://goproxy.cn,direct

WORKDIR /build

ADD go.mod .
ADD go.sum .
RUN go mod download
COPY . .
RUN go build -ldflags="-s -w" -o /app/main ./main.go


FROM scratch

ENV TZ Asia/Shanghai

WORKDIR /app
COPY --from=builder /app/main /app/main

EXPOSE 6666
CMD ["./main"]

pprof性能优化

func InitPprofMonitor() {
   go func() {
      log.Println(http.ListenAndServe(":9990", nil))
   }()
}

进入界面 http://localhost:9990/debug/pprof/

image-20220612113027339

此节目可以查询详细参数,若想更加可视化一点可以安装graphviz,并在命令行中输入

$ go tool pprof -http=:8080 "http://localhost:9990/debug/pprof/heap //或其他

image-20220612113241221

可以更加直观,或者进入火焰图界面,效果也很好

image-20220612113314188

参考 https://link.juejin.cn/?target=https%3A%2F%2Fgithub.com%2Fwolfogre%2Fgo-pprof-practice

Viper配置

使用viper配置并进行热重载设置

配置模板 setting-dev.yaml

# settings-dev.yaml
name: "go-chess"
port: 1234

gorm:
  name: "xx"
  host: "xx.xx.xx.xx"
  port: 3306
  password: "xxxxxxxx"
  dbName: "xx"

redis:
  host: "xx.xx.xx.xx"
  port: 6379
  password: ":.xxxx@:xxx?Zx"
  DB: 10

etcd:
  addr: "xxxx.0.x:2379"
func InitConfig() {
   // 实例化viper
   v := viper.New()
   //文件的路径如何设置
   v.SetConfigFile("./setting-dev.yaml")
   err := v.ReadInConfig()
   if err != nil {
      log.Println(err)
   }
   serverConfig := model.ServerConfig{}
   //给serverConfig初始值
   err = v.Unmarshal(&serverConfig)
   if err != nil {
      log.Println(err)
   }
   // 传递给全局变量
   global.Settings = serverConfig

   //热重载配置
   v.OnConfigChange(func(e fsnotify.Event) {
      log.Printf("config file:%s Op:%s\n", e.Name, e.Op)
   })
   v.WatchConfig()

快速开始

若要在本地运行需要本地有Go C ETCD Redis环境

$ go clone https://github.com/L2ncE/go-chess
确保已配置setting-dev.yaml
$ go run main.go
$ cd ./rpc/user/server
$ etcd
$ go run userserver.go -addr 127.0.0.1:50001 //把ETCD跑起来

按照接口文档在同一个房间中加入两名用户

需要C编译器,因为 Ebitengine 不仅使用 Go,还使用 C。

$ apt install gcc
$ sudo apt install libc6-dev libglu1-mesa-dev libgl1-mesa-dev libxcursor-dev libxi-dev libxinerama-dev libxrandr-dev libxxf86vm-dev libasound2-dev pkg-config

PS:房间red中已有两名用户,可直接开启游戏进行测试

进入到chess包中运行main.go若房间人满则可开始游戏

游戏逻辑以及棋盘设计参考 https://wangqianhong.com/tag/%e4%b8%ad%e5%9b%bd%e8%b1%a1%e6%a3%8b

每一次点击棋盘都会有坐标传出

QQ录屏20220612120701

image-20220612122658899

About

Chinese chess implemented in golang

Topics

Resources

License

Stars

Watchers

Forks

Languages