最近项目的后端需求是全球同服的,在使用语言方面确定了为golang之后,了解了一下当前的一些goalng游戏服务器框架,终于在leaf/pitaya/ 等众多框架中选择了 Origin, 主要是因为它是分布式框架,微服务架构,比较匹配做全球同服需求下的功能模块分离。
|
(后续2021.08.04补充更正一下,Origin的服务发现功能现已实现,参看Origin服务发现详解)
目前基本已经上手,现在总结归纳一下一些需要注意和记住的点:
1、服务的安装
步骤如下:
- 服务的实现
定义一个组合了service.Service的结构体,它就是一个服务了 - 服务的预加载
在服务实现的package的init函数中使用 node.setup就可以预加载服务了, 预加载之前根据需要设置服务的Name, 与后面配置servicelist中的名字对应,默认是服务结构体的名称. - 服务配置
在config/cluster.json 中可以配置每个节点的servicelist, 添加节点想要装载的服务名即可. - main.go中导入服务package
这点是很容易忽略的点,一不小心就容易搞忘记了, 一定要在main.go中import
2、rpc的使用细节
在Origin中rpc的接口及注释如下:
// 异步, rpc首选, 不会阻塞本服务
AsyncCall(serviceMethod string,args interface{},callback interface{}) error
// 同步等待调用结果
Call(serviceMethod string,args interface{},reply interface{}) error
// 无结果,不阻塞
Go(serviceMethod string,args interface{}) error
// 在明确节点时调用,可以稍微减少开销; Service名相同时, 避免广播
AsyncCallNode(nodeId int,serviceMethod string,args interface{},callback interface{}) error
CallNode(nodeId int,serviceMethod string,args interface{},reply interface{}) error
GoNode(nodeId int,serviceMethod string,args interface{}) error
// 原数据,减少参数/结果的序列化和反序列化, 大量转发时使用.
RawGoNode(rpcProcessorType RpcProcessorType,nodeId int,rpcMethodId uint32,serviceName string,rawArgs IRawInputArgs) error
// 广播
CastGo(serviceMethod string,args interface{})
RPC函数的定义方式看过官方文档了就大致清楚了,这里强调一下, 有时候被调用方可能是一个很费时的操作,这个时候就需要考虑如下这种方式的RPC函数实现:
// RPC 函数的另一种定义方式, 第一个参数为 rpc.Responder(rpc.requesthandler)类型, 主要可以用来 异步返回调用结果.
func (slf *LoginService) RPC_AuthLoginEx(resp rpc.Responder, req *msgpb.MsgLoginReq) error {
// 如果ret是一个比较耗时的外部调用的结果,为了不阻塞本服务, 需要开启异步协程, 在异步结果到达时, 调用resp返回rpc结果
go func(){
time.Sleep(3*time.Second)
ret := &msgpb.MsgLoginRes{Ret: msgpb.ErrCode_ConnExceeded}
resp(ret, rpc.NilError)
}()
return nil
}
对应的调用方式不变:
r.gateService.Call("LoginService.RPC_AuthLoginEx", msgLoginReq, msgLoginRet)
r.gateService.AsyncCall("LoginService.RPC_AuthLoginEx", msgLoginReq, func(ret *msgpb.MsgLoginRes, err error){
})
3、Origin核心思想
这个得从config说起,config/cluster.json,这个是目前版本origin(作者Boyce正考虑完善服务发现功能)的集群节点、服务配置。 其中每个节点下有一个servicelist,这个是配置每个节点装载的服务的名字(私有的以下划线_开头),一开始我觉得根据node.setup的服务就可以知道当前节点装载了哪些服务没必要在这里配置,后来才知道错了。
因为origin是单程序,服务配置化的设计思想,即所有节点都使用同一个编译好的执行程序,每个节点根据配置的servicelist来最终装载自己的服务,而不是在每个service的实现文件里的init里的node.setup时装载的。 init里的setup只是预装载,只是把服务名和服务的实现代码绑定起来。最后真正装载服务是在cluster初始化时,解析了节点的servicelist之后才装载的。
这样做的好处目前想到的有如下几点:
1、所有节点的服务配置对外都是透明的,这样其他节点进行rpc时就有源可寻。
2、配置灵活,所有的服务都可以随意配置成单进程或集群模式,不同模式下,在rpc的支持下无需做其他修改。方便开发和生产环境服务器集群的伸缩。