写给go开发者的Tars教程-拦截器

2024-01-07
3分钟阅读时长

本篇为【写给go开发者的Tars教程】系列第三篇

第一篇:Tars协议基础

第二篇:通信模式

第三篇:拦截器

TarsGo拦截器和其他框架的拦截器(也称middleware)作用是一样的。利用拦截器我们可以在不侵入业务逻辑的前提下修改或者记录服务端或客户端的请求与响应,利用拦截器我们可以实现诸如日志记录、权限认证、限流、链路追踪等诸多功能。拦截器可以分别应用在服务端和客户端,所以TarsGo总共为我们提供了多种拦截器,下面我们进行一一介绍。

服务端拦截器(过滤器)

处理前后拦截器(过滤器)

拦截器定义如下:

// Dispatch server side Dispatch
type Dispatch func(context.Context, interface{}, *requestf.RequestPacket, *requestf.ResponsePacket, bool) error

// ServerFilter is used for add Filter for server dispatcher ,for implementing plugins like opentracing.
type ServerFilter func(ctx context.Context, d Dispatch, f interface{},
	req *requestf.RequestPacket, resp *requestf.ResponsePacket, withContext bool) (err error)

注册服务端拦截器的方法如下:

func RegisterServerFilter(f ServerFilter) // 注册服务端拦截器,只能注册一个
func RegisterPreServerFilter(f ServerFilter) // 注册服务端处理前拦截器,可多个
func RegisterPostServerFilter(f ServerFilter) // 注册服务端处理后拦截器,可多个

不推荐上面三种注册方法来注册拦截件,将在1.5版本后彻底废弃移除,推荐使用下面介绍的中间件实现对应的逻辑。

示例:

// 注册服务端过滤器
tars.RegisterServerFilter(func(ctx context.Context, d tars.Dispatch, f interface{}, req *requestf.RequestPacket, resp *requestf.ResponsePacket, withContext bool) (err error) {
    // TODO: 增加服务端处理前业务逻辑
    // 这里可以增加:日志记录、权限认证、限流、链路追踪等
    s := time.Now()
    err = d(ctx, f, req, resp, withContext)
    // TODO: 增加服务端处理后业务逻辑
    log.Printf("ServantName: %s, FuncName: %s, req: %v, resp: %v, latency: %s\n",
               req.SServantName, req.SFuncName, req, resp, time.Now().Sub(s))
    return err
})

tars.RegisterPreServerFilter(func(ctx context.Context, d tars.Dispatch, f interface{}, req *requestf.RequestPacket, resp *requestf.ResponsePacket, withContext bool) (err error) {
    // TODO: 增加服务端处理前业务逻辑
    return err // 此处返回err,服务端会继续处理,只会记录对应的错误日志
})

tars.RegisterPostServerFilter(func(ctx context.Context, d tars.Dispatch, f interface{}, req *requestf.RequestPacket, resp *requestf.ResponsePacket, withContext bool) (err error) {
    // TODO: 增加服务端处理前业务逻辑
    return err // 此处返回err,服务端会继续处理,只会记录对应的错误日志
})

中间件(过滤器)

中间件定义如下:

// ServerFilterMiddleware is used for add multiple filter middlewares for dispatcher, for using multiple filter such as breaker, rate limit and trace.
type ServerFilterMiddleware func(next ServerFilter) ServerFilter

注册服务端中间件的方法如下:

func UseServerFilterMiddleware(sfm ...ServerFilterMiddleware) // 注册服务端中间件,可多个

示例:

tars.UseServerFilterMiddleware(func(next tars.ServerFilter) tars.ServerFilter {
    return func(ctx context.Context, d tars.Dispatch, f interface{}, req *requestf.RequestPacket, resp *requestf.ResponsePacket, withContext bool) (err error) {
        // TODO: 增加服务端处理前业务逻辑
        // 这里可以增加:日志记录、权限认证、限流、链路追踪等
        s := time.Now()
        err = next(ctx, d, f, req, resp, withContext)
        // TODO: 增加服务端处理后业务逻辑
        log.Printf("ServantName: %s, FuncName: %s, latency: %s\n",
                   req.SServantName, req.SFuncName, time.Now().Sub(s))
        return err
    }
})

上面集中方法注册的拦截件或中间件会有优先级问题,其中一个生效后其他方法注册的拦截器将不会执行。优先级顺序如下:

  1. RegisterServerFilter
  2. UseServerFilterMiddleware
  3. RegisterPreServerFilter/RegisterPostServerFilter

存在优先级顺序的真相如下:

image-20240106170154133

客户端拦截器(过滤器)

请求前后拦截器(过滤器)

拦截器定义如下:

// Invoke is used for Invoke tars server service
type Invoke func(ctx context.Context, msg *Message, timeout time.Duration) (err error)

// ClientFilter is used for filter request & response for client, for implementing plugins like opentracing
type ClientFilter func(ctx context.Context, msg *Message, invoke Invoke, timeout time.Duration) (err error)

注册客户端拦截器的方法如下:

func RegisterClientFilter(f ClientFilter) // 注册客户端拦截器,只能注册一个
func RegisterPreClientFilter(f ClientFilter) // 注册请求前拦截器,可多个
func RegisterPostClientFilter(f ClientFilter) // 注册请求后拦截器,可多个

不推荐上面三种注册方法来注册拦截件,将在1.5版本后彻底废弃移除,推荐使用下面介绍的中间件实现对应的逻辑。

示例:

tars.RegisterClientFilter(func(ctx context.Context, msg *tars.Message, invoke tars.Invoke, timeout time.Duration) (err error) {
    // TODO: 增加请求前业务逻辑
    // 这里可以增加:日志记录、权限认证、限流、链路追踪等
    s := time.Now()
    err = invoke(ctx, msg, timeout)
    // TODO: 增加请求后业务逻辑
    log.Printf("ServantName: %s, FuncName: %s, req: %v, resp: %v, latency: %s\n",
               msg.Req.SServantName, msg.Req.SFuncName, msg.Req, msg.Resp, time.Now().Sub(s))
    return err
})

tars.RegisterPreClientFilter(func(ctx context.Context, msg *tars.Message, invoke tars.Invoke, timeout time.Duration) (err error) {
    // TODO: 增加请求前业务逻辑
    return err // 此处返回err,客户端会继续处理,只会记录对应的错误日志
})

tars.RegisterPreClientFilter(func(ctx context.Context, msg *tars.Message, invoke tars.Invoke, timeout time.Duration) (err error) {
    // TODO: 增加请求后业务逻辑
    return err // 此处返回err,客户端会继续处理,只会记录对应的错误日志
})

中间件(过滤器)

中间件定义如下:

// ClientFilterMiddleware is used for add multiple filter middleware for client, for using multiple filter such as breaker, rate limit and trace.
type ClientFilterMiddleware func(next ClientFilter) ClientFilter

注册客户端中间件的方法如下:

func UseClientFilterMiddleware(cfm ...ClientFilterMiddleware) // 注册客户端中间件,可多个

示例:

tars.UseClientFilterMiddleware(func(next tars.ClientFilter) tars.ClientFilter {
    return func(ctx context.Context, msg *tars.Message, invoke tars.Invoke, timeout time.Duration) (err error) {
        // TODO: 增加请求前业务逻辑
        // 这里可以增加:日志记录、权限认证、限流、链路追踪等
        s := time.Now()
        err = next(ctx, msg, invoke, timeout)
        // TODO: 增加请求后业务逻辑
        log.Printf("ServantName: %s, FuncName: %s, latency: %s\n",
                   msg.Req.SServantName, msg.Req.SFuncName, time.Now().Sub(s))
        return err
    }
})

上面集中方法注册的拦截件或中间件会有优先级问题,其中一个生效后其他方法注册的拦截器将不会执行。优先级顺序如下:

  1. RegisterClientFilter
  2. UseClientFilterMiddleware
  3. RegisterPreClientFilter/RegisterPostClientFilter

存在优先级顺序的真相如下:

image-20240106165734587

总结

相信经过上面的讲解,读者已对TarsGo的拦截器(过滤器、中间件)的使用和实现原理有深入的了解,对今后的开发可以事半功倍,在合适的地方使用中间件实现相应的业务逻辑。

示例代码

https://github.com/lbbniu/TarsGo-tutorial/blob/interceptors/client/main.go

https://github.com/lbbniu/TarsGo-tutorial/blob/interceptors/main.go

关注公众号获得更多精彩文章

公众号:程序员大兵