五邑隐侠,本名关健昌,12年游戏生涯。 本教程以Go语言为例。
P2P网络为服务进程间、服务进程与客户端间通信提供了便利,在这个基础上可以搭建服务。
在服务层,通信包可以通过定义协议号来确定该包怎样处理。每个协议号对应一个Processor,Processor定义为一个interface
type Processor interface {
OnHandleRequest(req *Request, resp *Response) (int32, error)
}
协议号又可以拆分为模块号和命令号。一个模块对应一个Service,该模块下每个命令对应一个Processor,通过map结构做映射
type Service struct {
mapCmd2Processor map[uint16]Processor
}
Service提供方法添加、删除、获取Processor
func AddProcessor(cmd uint16, p Processor) error
func GetProcessor(cmd uint16) Processor
func RemoveProcessor(cmd uint16) error
提供个入口方法,把该模块的Service的请求分发到对应的Processor
func (s *Service) OnHandleRequest(req *Request, resp *Response) (int32, error) {
p, ok := s.mapCmd2Processor[req.Cmd]
if ok {
return p.OnHandleRequest(req, resp)
}
}
服务 Server 是 Service 的集合,通过模块号进行映射
type Server struct {
mapMod2Service map[uint16]*Service
rpc *Rpc
}
同样提供方法添加、删除、获取Service
func AddService(mod uint16, srv *Service) error
func GetService(mod uint16) *Service
func RemoveService(mod uint16) error
这样当收到一个请求包,对协议号拆分为模块号和命令号,通过模块号获取到对应的Service,调用Service的 OnHandleRequest 进行处理
serv := s.GetService(req.Mod)
if serv != nil {
code, err := serv.OnHandleRequest(req, resp)
}
Server 基于 P2P 层,P2P的包是通用的格式,不同的游戏服务端进程,都有可能有不同的包格式,例如,客户端包格式和服务内部通信的包格式是有差别的。为了让服务通用,引入服务适配器,在请求和响应时做预处理,转化为通用的 Request 和 Response
type ServerAdapter interface {
OnRequest(payload []byte) (*Request, error)
OnResponse(pack *Response) ([]byte, error)
} type Server struct {
mapMod2Service map[uint16]*Service
rpc *Rpc adapter ServerAdapter
}
这样在服务层都是基于 Request 和 Response进行处理,由具体业务对业务包做转换
type Request struct {
Mod uint16
Cmd uint16
Payload []byte
} type Response struct {
Mod uint16
Cmd uint16
Payload []byte
}
服务 Server 还可以提供拦截器,拦截器的好处是对一些阶段进行统一处理,而且可以通过插入新拦截器进行扩展,随时都可以替换这个阶段的逻辑(例如从 json 包变成 proto 包、增加二进制头等),拦截器可以针对3个阶段: 请求处理前,请求处理后,响应发送后
type Interceptor interface {
OnPreHandle(req *Request, resp *Response) (int32, error)
OnHandleCompletion(req *Request, resp *Response) (int32, error)
OnResponseCompletion(req *Request, resp *Response) error
}
除了全局拦截器,还可以添加针对某个模块 Service 的拦截器,对某个 Service 做特殊拦截处理
type InterceptorList []Interceptor type Server struct {
mapMod2Service map[uint16]*Service
rpc *Rpc adapter ServerAdapter
globalInterceptors InterceptorList
mapMod2Interceptors map[uint16]InterceptorList
}
这样一个请求的处理过程就变成
// adapter
req, err := s.adapter.OnRequest(payload)
resp := NewResponse(req)
// prehandle
interList, ok := s.mapMod2Interceptors[req.Mod]
s.prehandle(interList, ok, req, resp)
// handle
serv := s.GetService(req.Mod)
code, err := serv.OnHandleRequest(req, resp)
// handle complete
s.handleCompletion(interList, ok, req, resp)
s.push(resp)
// response complete
s.responseCompletion(interList, ok, req, resp)
服务机制介绍到这里,接下来聊聊 RPC 机制