从0到1实现grpc 拦截器

前言

拦截器简单来说就是我们可以在执行业务代码前执行一段代码,和gin 的中间件的功能是一样的。我们常常在拦截器会实现限流,熔断器,auth 认证等功能。首先我们自己可以实现一个拦截器。

type handle func(ctx context.Context)
​
  var h = func(ctx context.Context) {
    fmt.Println("业务")
  }
  type interceptor func(ctx context.Context, handle2 handle)
  inter1 := func(ctx context.Context, h handle) {
    fmt.Println("1")
    h(ctx)
  }
  inter2 := func(ctx context.Context, h handle) {
    fmt.Println("2")
    h(ctx)
  }
  for _, tx := range []interceptor{inter1, inter2} {
    tx(context.Background(), h)
  }

下面是运行的结果

1
业务
2
业务

这个肯定不对呀,inter1和 inter2 是两个拦截器 h 是业务代码,业务代码应该只执行一次呀。grpc 是怎么实现的呢。

grpc 拦截器的实现

下面是我粘贴的grpc 源码

// chainUnaryClientInterceptors chains all unary client interceptors into one.
func chainUnaryClientInterceptors(cc *ClientConn) {
  interceptors := cc.dopts.chainUnaryInts
  // Prepend dopts.unaryInt to the chaining interceptors if it exists, since unaryInt will
  // be executed before any other chained interceptors.
  if cc.dopts.unaryInt != nil {
    interceptors = append([]UnaryClientInterceptor{cc.dopts.unaryInt}, interceptors...)
  }
  var chainedInt UnaryClientInterceptor
  if len(interceptors) == 0 {
    chainedInt = nil
  } else if len(interceptors) == 1 {
    chainedInt = interceptors[0]
  } else {
    chainedInt = func(ctx context.Context, method string, req, reply any, cc *ClientConn, invoker UnaryInvoker, opts ...CallOption) error {
      return interceptors[0](ctx, method, req, reply, cc, getChainUnaryInvoker(interceptors, 0, invoker), opts...)
    }
  }
  cc.dopts.unaryInt = chainedInt
}
​
// getChainUnaryInvoker recursively generate the chained unary invoker.
func getChainUnaryInvoker(interceptors []UnaryClientInterceptor, curr int, finalInvoker UnaryInvoker) UnaryInvoker {
  if curr == len(interceptors)-1 {
    return finalInvoker
  }
  return func(ctx context.Context, method string, req, reply any, cc *ClientConn, opts ...CallOption) error {
    return interceptors[curr+1](ctx, method, req, reply, cc, getChainUnaryInvoker(interceptors, curr+1, finalInvoker), opts...)
  }
}

chainUnaryClientInterceptors 这个函数主要功能就是把拦截器放在切片里,getChainUnaryInvoker 递归执行拦截器,到最后一个拦截器执行完时也就是 curr == len(interceptors)-1 执行业务函数,噢,我明白了,只要我这么做也是可以的,自己实现的拦截器。

从0到1实现拦截器

type handle func(ctx context.Context)
​
type invoke func(ctx context.Context, h handle, inter []interceptor) error
​
type interceptor func(ctx context.Context, invoker invoke, h handle) error
​
func invokers(ctx context.Context, inter []interceptor, cur int, ivk invoke) invoke {
  if cur == len(inter)-1 {
    return ivk
  }
  return func(ct context.Context, h handle, intr []interceptor) error {
    return inter[cur+1](ctx, invokers(ctx, inter, cur+1, ivk), h)
  }
}
​
func intersStart(ctx context.Context, interceptor2 []interceptor, invoker invoke) interceptor {
  if len(interceptor2) == 0 {
    return nil
  }
  if len(interceptor2) == 1 {
    return interceptor2[0]
  }
  return func(ctx context.Context, invoker invoke, h handle) error {
    return interceptor2[0](ctx, invokers(ctx, interceptor2, 0, invoker), h)
  }
}
​
func main() {
  var inters []interceptor
  inter1 := func(ctx context.Context, invoker invoke, h1 handle) error {
    fmt.Println("拦截器一")
    return invoker(ctx, h1, inters)
  }
  inter2 := func(ctx context.Context, invoker invoke, h1 handle) error {
    fmt.Println("拦截器二")
    return invoker(ctx, h1, inters)
​
  }
  inters = []interceptor{inter1, inter2}
  var invorkd = func(ctx context.Context, h handle, inter []interceptor) error {
    fmt.Println("业务")
    return nil
  }
  ctx := context.Background()
  start := intersStart(ctx, inters, invorkd)
  start(ctx, invorkd, nil)
}

执行结果

拦截器一
拦截器二
业务

我们可以看到多个拦截器,业务代码只执行了一次,也不是很难吧,最主要是 intersStart 分情况执行拦截器,多个的话 invokers 递归执行,到了最后一个拦截器执行成功了,在执行业务函数。比较绕,可以复制代码,打断点执行。

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值