介绍
这是一个系列
Nano 分布式游戏服务器方案
Nano包含内置的分布式系统解决方案,可让您轻松创建分布式游戏服务器。
How to build distributed system with Nano
示例仓库
- 笔者改过后的 Test Case:node_test.go
开始探索
笔者本地环境
go version
# go version go1.14.2 darwin/amd64
PingCap 测试套件
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
:
默认使用 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()