【Pitaya游戏服务器实战---注册登录】1.1前后端服务器间通信

第一章,我们先一起完成一个简单的登录流程。虽说简单,但也涉及到了多个服务器和数据库,所以借此功能,我们刚好了解如何通过 pitaya 建立前后端服务器、进行服务器间通信。

本节代码:xyq10612/PitayaGame at chapter1.1-前后端服务期间通信 (github.com)

注册登录服务器框架

先画一个简单的服务器框架图(只包括与注册登录有关的框架,其他服务器例如匹配、战斗、聊天等以后再加入,前期不要复杂度拉太高):
在这里插入图片描述

  1. Client 通过 socket 连接到 proxyServer(在 proxyServer 创建 Acceptor 监听);
  2. proxyServer 作为代理服务器,隔绝客户端与逻辑服务,更加安全,也方便做逻辑层的负载均衡。目前开发中为了方便测试,我们只开放一个 proxyServer,让客户端直连上来,之后可以多开代理服,使用 ngnix 之类的服务来做外层的负载均衡;
  3. 服务器内部使用 pitaya 框架提供的 RPC 功能来实现进程见通信;
  4. proxyServer 和 lobbyServer 都需要与 redis 建立链接,用于实现登录功能,保证登录的原子性(这个在我们实现登录逻辑的时候再细说);
  5. 这套实战项目采用 mongdb 作为落地服务器,所有 lobbyServer 直连 db,其他服务器不直接修改玩家存盘数据,均交由 lobbyServer 来处理。

实现前后端服务器通信

1. 建立工程

先建立工程,我这里命名是 “PitayaGame”,起名困难户……
同时把我们的两个服务器文件夹、go.mod go.work啥的都创建好:

在这里插入图片描述

2. 创建 proxyServer 服务器

参考 pitaya 的其他 demo 写法,创建默认的 Builder,添加 Acceptor 等:

// main.go
var app pitaya.Pitaya

func main() {
	serverType := "proxy"

	port := flag.Int("port", 40000, "the port to listen")
	flag.Parse()

	logrus.SetLevel(logrus.DebugLevel)

	config := config.NewDefaultBuilderConfig()
	builder := pitaya.NewDefaultBuilder(true, serverType, pitaya.Cluster, map[string]string{}, *config)
	builder.AddAcceptor(newAcceptor(*port))

	app = builder.Build()

	defer app.Shutdown()

	app.Start()
}

func newAcceptor(port int) acceptor.Acceptor {
	tcp := acceptor.NewTCPAcceptor(fmt.Sprintf(":%d", port))
	return tcp

serverType 作为路由的一部分是不会修改的,在代码里可以写死,端口需要可配置,所以开放到命令行参数里。
go run 把服务器跑起来,没有问题~(注意 etcd 和 nats-server 记得开启,开启方法参考:0.准备工作)

3. 定义 proto

有些通信协议是多个服务器都需要使用的,为了方便复用,我们干脆把所有的 proto 都放在一个公共包里:

在这里插入图片描述

define 目录用来存放 pb 定义,proto 目录用来存放生成的 go 文件。

我们先来为 注册 功能定义相应的 pb,顺便抽取出一些可复用的部分,比如 errorCode 等。
err.proto

syntax = "proto3";

package proto;
option go_package="../proto";

enum ErrCode {
    OK = 0;
    ERR = -1;

    // 1000 - 1999 通用错误
    UpParam = 1000; // 上行参数错误

    // 2000 - 2999 用户错误
    AccountRegister_NameInvalid = 2000; // 用户名不合法
    AccountRegister_PwdInvalid = 2001; // 密码不合法
    AccountRegister_NameExist = 2002; // 用户名已存在
}

common.proto

syntax = "proto3";

package proto;
option go_package = "../proto";

import "err.proto";

message CommonResponse {
  ErrCode err = 1;
}

message ErrorMessage {
  ErrCode err = 1;
  string msg = 2;
}

account.proto

syntax = "proto3";

package proto;
option go_package = "../proto";

import "err.proto";

message RegisterRequest {
  string account = 1;
  string password = 2;
}

PS:后续如果再有 pb 相关的代码段,就不再给出前面几行 package、option 之类的设置,不然代码太长了。

顺便在 define 目录下写一个生成脚本:
generate.bat

protoc --go_out=. ^
    common.proto ^
    err.proto ^
    account.proto

pause

exit

PS: 关于如何生成 protobuf go 代码,网上也很多参考文章,我就不写了,懒……

跑完 bat 脚本,在 proto 目录下生成 go 代码。

4. proxy 账号服务 AccountService

在 proxyServer 里建立 service 文件夹,在这个示例中, service 作为服务提供方,用来处理消息,还有一种命名是 handler,这里沿用 pitaya demo 中的用法 —— service

  1. 实现一个简单的服务,没有实际逻辑,仅仅测试一下通信
// proxyServer/service/accountService.go
// AccountService 账号服务
type AccountService struct {
	component.Base
	app pitaya.Pitaya
}

func NewAccountService(app pitaya.Pitaya) *AccountService {
	return &AccountService{app: app}
}

func (s *AccountService) Register(ctx context.Context, req *proto.RegisterRequest) (*proto.CommonResponse, error) {
	logrus.Debugf("register request: %v", req)

	rsp := &proto.CommonResponse{Err: proto.ErrCode_OK}

	return rsp, nil
  1. 在 main 中注册服务
// main.go
func main() {
  //...
  defer app.Shutdown()

  initServices()
  
  app.Start()
}

func initServices() {
	account := service.NewAccountService(app)
	app.Register(account,
		component.WithName("account"),
		component.WithNameFunc(strings.ToLower))
}

5. 测试 proxy 服务器的消息处理

开启服务器,使用 pitaya-cli 测试一下账号服务的消息处理:

>pitaya-cli
Pitaya REPL Client
>>> connect 127.0.0.1:40000
Using json client
connected!
>>> request proxy.account.register {"account":"test1", "password":"ttt"}
>>> sv->{}

客户端发送请求,服务端正常打印了日志:

level=debug msg="SID=4, Data={\"account\":\"test1\",\"password\":\"ttt\"}" requestId=ATMuGsStlRAk9pZQ26bumE route=proxy.account.register source=pitaya userId=

OK,初步调通。

6. 创建 lobbyServer 服务器

lobby 服务器作为后端服务器,是不需要对外的,不需要添加 Acceptor 监听。

// main.go
func main() {
	serverType := "lobby"

	logrus.SetLevel(logrus.DebugLevel)

	config := config.NewDefaultBuilderConfig()
	builder := pitaya.NewDefaultBuilder(false, serverType, pitaya.Cluster, map[string]string{}, *config)

	app = builder.Build()

	defer app.Shutdown()

	initServices()

	app.Start()
}

func initServices() {
	account := service.NewAccountService(app)
	app.Register(account,
		component.WithName("account"),
		component.WithNameFunc(strings.ToLower))
	app.RegisterRemote(account,
		component.WithName("account"),
		component.WithNameFunc(strings.ToLower))
}

7. lobbyServer 账号服务 AccountService

先测试前后端服务器的通信,没有具体逻辑,打印个log:

type AccountService struct {
	component.Base
	app pitaya.Pitaya
}

func NewAccountService(app pitaya.Pitaya) *AccountService {
	return &AccountService{
		app: app,
	}
}

func (s *AccountService) Register(ctx context.Context, req *proto.RegisterRequest) (*proto.CommonResponse, error) {
	logrus.Debugf("register request: %v", req)

	logrus.Debugf("do something in lobby")

	return &proto.CommonResponse{Err: proto.ErrCode_OK}, nil
}

8. 在 proxyServer 中远程调用 lobbyServer

第 4 步里,我们是直接把注册请求返回的,现在我们修改一下代码,让 proxyServer 的 Register 远程调用 lobbyServer 的 Register
远程调用可以使用 RPCRPCTo,后者需要指定 ServerId,这里使用 RPC,底层会调用默认的路由方法 defaultRoute 随机选择一个该类型服务器。

// proxyServer/service/accountService.go
func (s *AccountService) Register(ctx context.Context, req *proto.RegisterRequest) (*proto.CommonResponse, error) {
	logrus.Debugf("register request: %v", req)

	logrus.Debugf("do something in proxy")

	rsp := &proto.CommonResponse{}

	err := s.app.RPC(ctx, "lobby.account.register", rsp, req)
	if err != nil {
		return nil, err
	}

	return rsp, nil
}

9. 测试前后端通信

记得开启 etcd 和 nats-server,框架 RPC 默认就是通过 nats 实现的,对底层实现感兴趣的可以看看这篇文章:cluster grpc demo

开启 1 个 proxyServer 和 2 个 lobbyServer,使用 2 个 pitaya-cli 测试:

pitaya-cli
Pitaya REPL Client
>>> connect 127.0.0.1:40000
Using json client
connected!
>>> request proxy.account.register {"account":"test1", "password":"ttt"}
>>> sv->{}

在 2 个 client 端都发出以上请求,根据默认的路由策略,这些请求会随机分配到 lobby1 或者 lobby2,lobby 侧输出了日志:

level=debug msg="register request: account:\"test1\"  password:\"ttt\""
level=debug msg="do something in lobby"

你也可以多开几个 client,看看这些请求最终都分配到哪个 lobby 上。

小结

本节主要是对注册登录所涉及到的服务器框架有了一个大致的了解,并搭建了代理服务器 proxyServer 和大厅服务器 lobbyServer,并且测试了前后端服务器之间的通信。
下一节,我们一起来封装 mongodb 操作,完善注册逻辑,实现玩家数据入库。

  • 5
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值