这是 github.com/liasece/micserver
的示例程序。
首先编译该例程:
make
直接使用./test.sh
或者使用如下命令行:
../bin/chaos -p gate001,player001,login001
chaos/main.go
是程序的入口。
你需要先初始化一个App
作为Moudle
的容器:
app, err := micserver.SetupApp(GetInitManger().GetConfigPath())
初始化管理器GetInitManger()
需要做的事情你可以直接参照chaos/init.go
的实现。
然后,只需要将实现了module.IModule
接口的模块传入 App 运行入口即可:
app.RunAndBlock(GetInitManger().GetProgramModuleList())
App 会先初始化各个模块的底层连接,然后调用模块的AfterInitModule()
方法,你可以在这里面执行你业务的初始化操作。
配置文件是必须的,最简单的配置文件如config/config.json
所示。由于 JSON 没有注释语法,以//
为名的键视为注释。
作为服务器,需要向客户端提供一个入口,本例中的网关模块位于gatemodule
下。你可以在这里找到一个最简单的网关模块实现,以及如何劫持底层协议实现使用websocket
与客户端通信。
客户端在连接到服务器网络后,除了Gate
能取到客户端的实际连接Client
外,其他模块只能通过客户端的Session
操作客户端,你可以在loginmodule/msghandler_client.go
中找到Session
最基础的应用,可以在Session
中保存一些重要且简单的数据,或者通过Session.SendMsg()
向客户端连接发送数据。如playermodule/boxes/player.go
中的实现:
func (this *Player) SendMsg(msg interface{}) {
btop := ccmd.GetSCTopLayer(msg)
// 使用当前Module作为发送者,向该Session绑定的gate类型模块发送数据,由管理该连接的Gate向真正的客户端连接发送数据。
this.Session.SendMsg(this.mod, "gate", 0, btop)
}
Remote Object Call.
ROC ,是 micserver 重要的分布式目标调用思想。如果一个对象,例如房间/商品/玩家/工会,需要提供一个可供远程执行的操作,这在以前称之为 RPC 调用,在 micserver 中,任意一个构造了这种对象的模块,均可以通过 BaseModule.GetROC(objtype).GetOrRegObj(obj) 来 注册一个 ROC 对象,在其他模块中,只需要通过 BaseModule.ROCCallNR 等方法,提供 目标对象的类型及 ID,即可发起针对该对象的远程操作。因此,在任意模块中,发起的任意针对其他模块的调用,都不应该使用模块 ID 来操作,因为使用统一的 ROC 至少包含以下好处:
- 无需知道目标对象在哪个模块上;
- 只需要关心目标对象的 ID(目标的类型你当然是知道的);
- 在模块更新时,可以统一将该模块的 ROC 对象迁移到新版本模块中实现热更;
- 可以将对象存储到数据库并且在其他模块中加载(基于第一点好处);
- 对象的位置/调用路由等由底层系统维护,可提供一个高可用/强一致/易维护的分布式网络。
你需要先在Module
中注册一种类型的 ROC,例如playermodule/module.go
中:
func (this *PlayerModule) AfterInitModule() {
this.BaseModule.AfterInitModule()
// 初始化Player ROC
this.BaseModule.NewROC(ccmd.ROCTypePlayer)
}
然后就是在你需要增加一个ccmd.ROCTypePlayer
类型的 ROC 对象的时候,注册这个类型的对象。你可以在playermodule/manager/playerdoc_manager.go
中找到最简单的 ROC 对象注册方式:
vi, isLoad := this.playerRoc.GetOrRegObj(uuid, playerobj)
其中,playerobj
必须实现roc.IObj
接口,你可以在playermodule/boxes/player.go
中找到这些实现:
// ROC对象接口实现
func (this *Player) GetROCObjID() string {
return this.Account.UUID
}
// ROC对象接口实现
func (this *Player) GetROCObjType() roc.ROCObjType {
return ccmd.ROCTypePlayer
}
// ROC对象接口实现
func (this *Player) OnROCCall(path *roc.ROCPath, arg []byte) ([]byte, error) {
this.Info("ROC调用执行:[%s],%+v", path.String(), arg)
switch path.Move() {
case "GateClose":
this.OnGateClose()
}
return nil, nil
}
只要 ROC 对象完成了注册GetOrRegObj()
,在其他任何连接到该模块的模块中,都可以通过以下方式发起调用到playerobj.OnROCCall()
:
module.ROCCallNR(roc.O(ccmd.ROCTypePlayer,uuid).F("Func"),data)
你可以在gatemodule/module.go
的OnCloseClient()
中找到最简单的示例。