探索 Nano 内置分布式游戏服务器方案测试用例

简介: 探索 Nano 内置分布式游戏服务器方案测试用例

Nano 分布式游戏服务器方案


Nano包含内置的分布式系统解决方案,可让您轻松创建分布式游戏服务器。

How to build distributed system with Nano


示例仓库

  • 笔者改过后的 Test Case:node_test.go


开始探索


笔者本地环境


go version
# go version go1.14.2 darwin/amd64


PingCap 测试套件


pingcap/check

Suite:Suite 将给定的值注册为要运行的测试套件。在给定值中以Test前缀开头的任何方法都将被视为测试方法。

TestingT:TestingT 运行所有用 Suite 函数注册的测试套件,将结果打印到 stdout,并将任何失败报告给 "testing" 包。

新建一个 demo_test.go,我们感受一下:


package cluster_test
import (
  "testing"
  . "github.com/pingcap/check"
)
type demoSuite struct{}
var _ = Suite(&demoSuite{})
func (s *demoSuite) TestNodeStartup(c *C) {
  c.Assert(nil, IsNil)
}
// 运行测试
func TestDemo(t *testing.T) {
  TestingT(t)
}


如果你是 VSCode + VSCode-GO,可以直接 run test

image.gif

默认使用 protobuf 进行数据交换

掘金 tag:protobuf,很多掘友会手把手教。

我们可以查看下benchmark/testdata/test.proto


syntax = "proto3";
package t;
message Ping {
    string Content = 1;
}
message Pong {
    string Content = 2;
}


就是按照 protobuf 的规范定义消息,生成相关语言的代码,用来交换数据。


测试用例源码分析


package cluster_test
import (
  "strings"
  "testing"
  "github.com/lonng/nano/benchmark/io"
  "github.com/lonng/nano/benchmark/testdata"
  "github.com/lonng/nano/cluster"
  "github.com/lonng/nano/component"
  "github.com/lonng/nano/scheduler"
  "github.com/lonng/nano/session"
  . "github.com/pingcap/check"
)
type nodeSuite struct{}
// 注册测试套件
var _ = Suite(&nodeSuite{})
type (
  // 定义运行在 Master(主) 服务器上的 Nano 组件
  MasterComponent struct{ component.Base }
  // 定义运行在 Gate(网关) 服务器上的 Nano 组件
  GateComponent struct{ component.Base }
  // 定义运行在 Game(游戏业务逻辑) 服务器上的 Nano 组件
  GameComponent struct{ component.Base }
)
// Master 服务器上名为 `Test` Handler 方法,对外提供服务
func (c *MasterComponent) Test(session *session.Session, _ []byte) error {
  // 推送消息
  return session.Push("test", &testdata.Pong{Content: "master server pong"})
}
// Gate 服务器上名为 `Test` Handler 方法,对外提供服务
func (c *GateComponent) Test(session *session.Session, ping *testdata.Ping) error {
  // 推送消息
  return session.Push("test", &testdata.Pong{Content: "gate server pong"})
}
// Gate 服务器上名为 `Test2` Handler 方法,对外提供服务
func (c *GateComponent) Test2(session *session.Session, ping *testdata.Ping) error {
  // 响应消息
  return session.Response(&testdata.Pong{Content: "gate server pong2"})
}
// Game 服务器上名为 `Test` Handler 方法,对外提供服务
func (c *GameComponent) Test(session *session.Session, _ []byte) error {
  // 推送消息
  return session.Push("test", &testdata.Pong{Content: "game server pong"})
}
// Game 服务器上名为 `Test2` Handler 方法,对外提供服务
func (c *GameComponent) Test2(session *session.Session, ping *testdata.Ping) error {
  return session.Response(&testdata.Pong{Content: "game server pong2"})
}
func TestNode(t *testing.T) {
  TestingT(t)
}
func (s *nodeSuite) TestNodeStartup(c *C) {
  // 开启一个全局的任务调度
  go scheduler.Sched()
  // 结束前清理
  defer scheduler.Close()
  // 注册 Master 服务器的 Components
  masterComps := &component.Components{}
  masterComps.Register(&MasterComponent{})
  // 集群 Master 服务器配置
  masterNode := &cluster.Node{
    // 服务地址
    ServiceAddr: "127.0.0.1:4450",
  }
  masterNode.Options = cluster.Options{
    IsMaster:   true,        // 设定为 Master Node
    Components: masterComps, // 设置 Master Components
  }
  // 启动 Master 节点服务器
  err := masterNode.Startup()
  // 断言启动是否有错误
  c.Assert(err, IsNil)
  // 断言 LocalService 是否是我们定义的 MasterComponent
  masterHandler := masterNode.Handler()
  c.Assert(masterHandler.LocalService(), DeepEquals, []string{"MasterComponent"})
  // 注册 Gate 服务器节点的 Components
  gateComps := &component.Components{}
  gateComps.Register(&GateComponent{})
  // 网关服务器配置
  gateNode := &cluster.Node{
    // 提供给其它服务远程调用的地址
    ServiceAddr: "127.0.0.1:14451",
  }
  gateNode.Options = cluster.Options{
    // Master 服务器地址
    AdvertiseAddr: "127.0.0.1:4450",
    // Client 连接地址
    ClientAddr: "127.0.0.1:14452",
    // 网关组件
    Components: gateComps,
  }
  // 启动网关服务器
  err = gateNode.Startup()
  // 断言是否有启动错误
  c.Assert(err, IsNil)
  gateHandler := gateNode.Handler()
  // 断言 Master 服务器自身服务
  c.Assert(masterHandler.LocalService(), DeepEquals, []string{"MasterComponent"})
  // 断言 Master 服务器可进行远程调用的服务
  c.Assert(masterHandler.RemoteService(), DeepEquals, []string{"GateComponent"})
  // 断言 Gate 服务器自身服务
  c.Assert(gateHandler.LocalService(), DeepEquals, []string{"GateComponent"})
  // 断言 Gate 服务器可进行远程调用的服务
  c.Assert(gateHandler.RemoteService(), DeepEquals, []string{"MasterComponent"})
  // 注册 Game 服务器的 Components
  gameComps := &component.Components{}
  gameComps.Register(&GameComponent{})
  // 游戏服务器配置
  gameNode := &cluster.Node{
    // 提供给其它服务远程调用的地址
    ServiceAddr: "127.0.0.1:24451",
  }
  gameNode.Options = cluster.Options{
    // Master 服务器地址
    AdvertiseAddr: "127.0.0.1:4450",
    // 游戏服务器组件
    Components: gameComps,
  }
  // 启动游戏服务器
  err = gameNode.Startup()
  // 断言启动是否有错误
  c.Assert(err, IsNil)
  gameHandler := gameNode.Handler()
  // 断言 Master 服务器自身服务
  c.Assert(masterHandler.LocalService(), DeepEquals, []string{"MasterComponent"})
  // 断言 Master 服务器可进行远程调用的服务
  c.Assert(masterHandler.RemoteService(), DeepEquals, []string{"GameComponent", "GateComponent"})
  // 断言 Gate 服务器自身服务
  c.Assert(gateHandler.LocalService(), DeepEquals, []string{"GateComponent"})
  // 断言 Gate 服务器可进行远程调用的服务
  c.Assert(gateHandler.RemoteService(), DeepEquals, []string{"GameComponent", "MasterComponent"})
  // 断言 Game 服务器自身服务
  c.Assert(gameHandler.LocalService(), DeepEquals, []string{"GameComponent"})
  // 断言 Game 服务器可进行远程调用的服务
  c.Assert(gameHandler.RemoteService(), DeepEquals, []string{"GateComponent", "MasterComponent"})
  // 新建一个连接器
  connector := io.NewConnector()
  // 创建一个等待连接成功的 channel
  chWait := make(chan struct{})
  connector.OnConnected(func() {
    // 触发等待连接成功的 channel
    chWait <- struct{}{}
  })
  // 连接到网关服务器
  if err := connector.Start("127.0.0.1:14452"); err != nil {
    // 断言一下是否有连接错误
    c.Assert(err, IsNil)
  }
  // 订阅等待连接成功的 channel
  <-chWait
  // 创建一个订阅服务器消息的 channel
  onResult := make(chan string)
  // 订阅一下来自服务器的 Push
  connector.On("test", func(data interface{}) {
    onResult <- string(data.([]byte))
  })
  // 通知 Gate 服务器的 Test 服务
  err = connector.Notify("GateComponent.Test", &testdata.Ping{Content: "ping"})
  c.Assert(err, IsNil)
  // 断言服务器返回结果
  c.Assert(strings.Contains(<-onResult, "gate server pong"), IsTrue)
  // 通知 Game 服务器的 Test 服务
  err = connector.Notify("GameComponent.Test", &testdata.Ping{Content: "ping"})
  c.Assert(err, IsNil)
  c.Assert(strings.Contains(<-onResult, "game server pong"), IsTrue)
  // 请求 Gate 服务器的 Test2 服务
  err = connector.Request("GateComponent.Test2", &testdata.Ping{Content: "ping"}, func(data interface{}) {
    onResult <- string(data.([]byte))
  })
  c.Assert(err, IsNil)
  c.Assert(strings.Contains(<-onResult, "gate server pong2"), IsTrue)
  // 请求 Game 服务器的 Test2 服务
  err = connector.Request("GameComponent.Test2", &testdata.Ping{Content: "ping"}, func(data interface{}) {
    onResult <- string(data.([]byte))
  })
  c.Assert(err, IsNil)
  c.Assert(strings.Contains(<-onResult, "game server pong2"), IsTrue)
  // 通知 Master 服务器的 Test 服务
  err = connector.Notify("MasterComponent.Test", &testdata.Ping{Content: "ping"})
  c.Assert(err, IsNil)
  c.Assert(strings.Contains(<-onResult, "master server pong"), IsTrue)
}


小结


通过示例,我们知道服务器有三种:主服务器网关服务器游戏业务逻辑服务器

客户端一般连接到 网关服务器

启动服务器,一般会有如下操作:


  • 定义组件 struct Comp { component.Base }
  • 注册组件 components.Register(Comp)
  • 设置服务器启动选项 &cluster.Node{}
  • 启动服务器 node.Startup()
相关文章
|
1月前
|
定位技术
GPS北斗卫星同步时钟(时间同步服务器)建设施工部署方案
GPS北斗卫星同步时钟(时间同步服务器)建设施工部署方案
GPS北斗卫星同步时钟(时间同步服务器)建设施工部署方案
|
1月前
|
监控 容灾 定位技术
云服务器的容灾方案
云服务器的容灾方案
|
2月前
|
网络协议 安全 测试技术
手撕测试tcp服务器效率工具——以epoll和io_uring对比为例
手撕测试tcp服务器效率工具——以epoll和io_uring对比为例
41 2
|
26天前
|
存储 Java 应用服务中间件
【分布式技术专题】「架构实践于案例分析」盘点互联网应用服务中常用分布式事务(刚性事务和柔性事务)的原理和方案
【分布式技术专题】「架构实践于案例分析」盘点互联网应用服务中常用分布式事务(刚性事务和柔性事务)的原理和方案
50 0
|
26天前
|
canal 消息中间件 关系型数据库
【分布式技术专题】「分布式技术架构」MySQL数据同步到Elasticsearch之N种方案解析,实现高效数据同步
【分布式技术专题】「分布式技术架构」MySQL数据同步到Elasticsearch之N种方案解析,实现高效数据同步
76 0
|
1月前
|
存储 测试技术 C++
P2P网络下分布式文件共享场景的测试
P2P网络下分布式文件共享场景的测试
33 6
|
1月前
|
弹性计算 分布式计算 DataWorks
DataWorks报错问题之ecs自建数据库连通性测试报错如何解决
DataWorks是阿里云提供的一站式大数据开发与管理平台,支持数据集成、数据开发、数据治理等功能;在本汇总中,我们梳理了DataWorks产品在使用过程中经常遇到的问题及解答,以助用户在数据处理和分析工作中提高效率,降低难度。
|
1月前
|
弹性计算 缓存 测试技术
阿里云2核4g服务器(费用价格/性能测试/支持人数)
阿里云2核4g服务器能支持多少人访问?2核4G服务器并发数性能测试,阿小云账号下的2核4G服务器支持20人同时在线访问,然而应用不同、类型不同、程序效率不同实际并发数也不同,2核4G服务器的在线访问人数取决于多个变量因素
|
1月前
|
弹性计算 缓存 测试技术
2核4g服务器能支持多少人访问?阿里云2核4G服务器并发数测试
2核4g服务器能支持多少人访问?阿里云2核4G服务器并发数测试,2核4G服务器并发数性能测试,阿小云账号下的2核4G服务器支持20人同时在线访问,然而应用不同、类型不同、程序效率不同实际并发数也不同,2核4G服务器的在线访问人数取决于多个变量因素
|
2月前
|
存储 算法 NoSQL
全网最全的分布式ID生成方案解析
全网最全的分布式ID生成方案解析
101 0