从零开始实现一个RPC框架(二)-基础支撑部分

升级版的Client和Server
首先让我们来重新定义Client和Server:SGClient和SGServer。SGClient封装了上一节定义的RPCClient的操作,提供服务治理的相关特性;SGServer则由上一节定义的RPCServer升级而来,支持服务治理的相关特性。这里的SG(service governance)表示服务治理。
这里直接贴上相关的定义:

type SGClient interface {
	Go(ctx context.Context, ServiceMethod string, arg interface{}, reply interface{}, done chan *Call) (*Call, error)
	Call(ctx context.Context, ServiceMethod string, arg interface{}, reply interface{}) error
}
type sgClient struct {
	shutdown  bool
	option    SGOption
	clients   sync.Map //map[string]RPCClient
	serversMu sync.RWMutex
	servers   []registry.Provider
}
type RPCServer interface {
	Register(rcvr interface{}, metaData map[string]string) error
	Serve(network string, addr string) error
	Services() []ServiceInfo
	Close() error
}
type SGServer struct { //原来的RPCServer
	codec      codec.Codec
	serviceMap sync.Map
	tr         transport.ServerTransport
	mutex      sync.Mutex
	shutdown   bool
	Option Option
}

拦截器
在之前的文章提到过,我们需要提供过滤器一样的使用方式,来达到对扩展开放对修改关闭的目标。我们这里采用高阶函数的方式来定义方切面和法拦截器,首先定义几个切面:

//客户端切面
type CallFunc func(ctx context.Context, ServiceMethod string, arg interface{}, reply interface{}) error
type GoFunc func(ctx context.Context, ServiceMethod string, arg interface{}, reply interface{}, done chan *Call) *Call
//服务端切面
type ServeFunc func(network string, addr string) error
type ServeTransportFunc func(tr transport.Transport)
type HandleRequestFunc func(ctx context.Context, request *protocol.Message, response *protocol.Message, tr transport.Transport)


以上几个是RPC调用在客户端和服务端会经过的几个函数,我们将其定义为切面,然后再定义对应的拦截器:

//客户端拦截器
packege client
type Wrapper interface {
	WrapCall(option *SGOption, callFunc CallFunc) CallFunc
	WrapGo(option *SGOption, goFunc GoFunc) GoFunc
}
//f服务端拦截器
package server
type Wrapper interface {
	WrapServe(s *SGServer, serveFunc ServeFunc) ServeFunc
	WrapServeTransport(s *SGServer, transportFunc ServeTransportFunc) ServeTransportFunc
	WrapHandleRequest(s *SGServer, requestFunc HandleRequestFunc) HandleRequestFunc
}

这样一来,用户可以通过实现Wapper接口来对客户端或者服务端的行为进行增强,比如将请求参数和结果记录到日志里,动态的修改参数或者响应等等。我们的框架自身 的相关功能也可以通过Wrapper实现。目前客户端实现了用于封装元数据的MetaDataWrapper和记录请求和响应的LogWrapper;服务端目前在DefaultWrapper实现了用于服务注册、监听退出信号以及请求计数的逻辑。
因为go并不提供抽象类的方式,所以对于某些实现类可能并不需要拦截所有切面(比如只拦截Call不想拦截Go),这种情况直接返回参数里的函数对象就可以了。

上一篇:Kubernetes 升级不弃 Docker:KubeKey 的丝滑之道


下一篇:HarmonyOS 开发-自定义视图实现Tab效果