go-micro集成链路跟踪的方法和中间件原理( 二 )


服务端Wrap在go-micro中服务端处理请求的逻辑封装称为Handler,它的具体形式是一个func,定义为:
func(ctx context.Context, req Request, rsp interface{}) error这个部分就来看一下服务端Handler是怎么被Wrap的 。
HandlerWrapper要想Wrap一个Handler,必须创建一个HandlerWrapper类型,这其实是一个func,其定义如下:
type HandlerWrapper func(HandlerFunc) HandlerFunc它的参数和返回值都是HandlerFunc类型,其实就是上面提到的Handler的func定义 。
以本文链路跟踪中使用的 tracerHandler 为例,看一下HandlerWrapper是如何实现的:
func(h server.HandlerFunc) server.HandlerFunc {return func(ctx context.Context, req server.Request, rsp interface{}) error {...if err = h(ctx, req, rsp); err != nil {...}}从中可以看出,Wrap一个Hander就是定义一个新Handler,在它的的内部调用传入的原Handler 。
Wrap Handler创建了一个HandlerWrapper之后,还需要把它加入到服务端的处理过程中 。
go-micro在NewService的时候通过调用 micro.WrapHandler 设置这些 HandlerWrapper:
service := micro.NewService(...micro.WrapHandler(tracerHandler),)WrapHandler的实现是这样的:
func WrapHandler(w ...server.HandlerWrapper) Option {return func(o *Options) {var wrappers []server.Optionfor _, wrap := range w {wrappers = append(wrappers, server.WrapHandler(wrap))}o.Server.Init(wrappers...)}}它返回的是一个函数,这个函数会将我们传入的HandlerWrapper通过server.WrapHandler转化为一个server.Option,然后交给Server.Init进行初始化处理 。
这里的server.Option其实还是一个func,看一下WrapHandler的源码:
func WrapHandler(w HandlerWrapper) Option {return func(o *Options) {o.HdlrWrappers = append(o.HdlrWrappers, w)}}这个func将我们传入的HandlerWrapper添加到了一个切片中 。
那么这个函数什么时候执行呢?就在Server.Init中 。看一下Server.Init中的源码:
func (s *rpcServer) Init(opts ...Option) error {...for _, opt := range opts {opt(&s.opts)}if s.opts.Router == nil {r := newRpcRouter()r.hdlrWrappers = s.opts.HdlrWrappers...s.router = r}...}它会遍历传入的所有server.Option,也就是执行每一个func(o *Options) 。这样Options的切片HdlrWrappers中就添加了我们设置的HandlerWrapper,同时还把这个切片传递到了rpcServer的router中 。
可以看到这里的Options就是rpcServer.opts,HandlerWrapper切片同时设置到了rpcServer.router和rpcServer.opts中 。
还有一个问题:WrapHandler返回的func什么时候执行呢?
这个在micro.NewService -> newService -> newOptions中:
func newOptions(opts ...Option) Options {opt := Options{...Server:server.DefaultServer,...}for _, o := range opts {o(&opt)}...}遍历opts就是执行每一个设置func,最终执行到rpcServer.Init 。
到NewService执行完毕为止,我们设置的WrapHandler全部添加到了一个名为HdlrWrappers的切片中 。
再来看一下服务端Wrapper的执行过程是什么样的?
执行Handler的这段代码在rpc_router.go中:
func (s *service) call(ctx context.Context, router *router, sending *sync.Mutex, mtype *methodType, req *request, argv, replyv reflect.Value, cc codec.Writer) error {defer router.freeRequest(req)...for i := len(router.hdlrWrappers); i > 0; i-- {fn = router.hdlrWrappers[i-1](fn)}...// execute handlerreturn fn(ctx, r, rawStream)}根据前面的分析,可以知道router.hdlrWrappers中记录的就是所有的HandlerWrapper,这里通过遍历router.hdlrWrappers实现了HandlerWrapper的嵌套,注意这里遍历时索引采用了从大到小的顺序,后添加的先被Wrap,先添加在外层 。
实际执行时就是先调用到最先添加的HandlerWrapper,然后一层层向里调用,最终调用到我们注册的业务Handler,然后再一层层的返回,每个HandlerWrapper都可以在调用下一层前后做些自己的工作,比如链路跟踪这里的检测执行时间 。
客户端Wrap在客户端中远程调用的定义在Client中,它是一个接口,定义了若干方法:
type Client interface {...Call(ctx context.Context, req Request, rsp interface{}, opts ...CallOption) error...}我们这里为了讲解方便,只关注Call方法,其它的先省略 。
下面来看一下Client是怎么被Wrap的 。
XXXWrapper要想Wrap一个Client,需要通过struct嵌套这个Client,并实现Client接口的方法 。至于这个struct的名字无法强制要求,一般以XXXWrapper命名 。
这里以链路跟踪使用的 otWrapper 为例,它的定义如下:
type otWrapper struct {ot opentracing.Tracerclient.Client}func (o *otWrapper) Call(ctx context.Context, req client.Request, rsp interface{}, opts ...client.CallOption) error {...if err = o.Client.Call(ctx, req, rsp, opts...); err != nil {...}...


推荐阅读