五邑隐侠,本名关健昌,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 机制