什么是拦截器

gRPC的拦截器(interceptor)类似各种Web框架里的请求中间件,请求中间件大家都知道是利用装饰器模式对最终处理请求的handler程序进行装饰,这样中间件就可以在处理请求前和完成处理后这两个时机上,拦截到发送给 handler 的请求以及 handler 返回给客户端的响应 。

中间件的最大的用处是可以把一些 handler 的前置和后置操作从 handler 程序中解耦出来,比如最常见的记录响应时长、记录请求和响应数据日志等操作往往是通过中间件程序实现的。

与 Web 框架的中间件同理,可以对gRPC的请求和响应进行拦截处理,而且既可以在客户端进行拦截,也可以对服务器端进行拦截。利用拦截器,可以对gRPC进行很好的扩展,把一些业务逻辑外的冗余操作从 handler 中抽离,提升项目的开发效率和扩展性。

怎么使用拦截器

gRPC的服务器和客户端都是分别可以添加一个单向调用 (Unary) 的拦截器和流式调用 (Stream) 的拦截器。

这两种调用方式的区别可以理解为HTTP和WebSocket的区别

对于客户端的单向调用的拦截,只需定义一个 UnaryClientInterceptor 方法:

type UnaryClientInterceptor func(ctx context.Context, method string, req, reply interface{}, cc *ClientConn, invoker UnaryInvoker, opts ...CallOption) error

而客户端流式调用的拦截,则需要定义一个 StreamClientInterceptor 方法:

type StreamClientInterceptor func(ctx context.Context, desc *StreamDesc, cc *ClientConn, method string, streamer Streamer, opts ...CallOption) (ClientStream, error)

同理,对于gRPC的服务端也有这两种调用的拦截器方法,分别是 UnaryServerInterceptorStreamServerInterceptor

type UnaryServerInterceptor func(ctx context.Context, req interface{}, info *UnaryServerInfo, handler UnaryHandler) (resp interface{}, err error)type StreamServerInterceptor func(srv interface{}, ss ServerStream, info *StreamServerInfo, handler StreamHandler) error

拦截器应用

下面简单演示一下,怎么用客户端和服务端拦截器来实现gRPC客户端调用日志,和gRPC服务器访问日志的。

首先我们定义一下客户端单向调用的拦截器方法:

func UnaryClientInterceptor(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) (err error) {start := time.Now()defer func() {in, _ := json.Marshal(req)out, _ := json.Marshal(reply)inStr, outStr := string(in), string(out)duration := int64(time.Since(start) / time.Millisecond)log.Println("grpc", method, "in", inStr, "out", outStr, "err", err, "duration/ms", duration, "remote_server", remoteServer)}()return invoker(ctx, method, req, reply, cc, opts...)
}

创建客户端的时候应用上这个方法:

var client routeGuideClientfunc init() {var err errorclient.cc, err = grpc.Dial("127.0.0.1:12305",grpc.WithInsecure(),grpc.WithUnaryInterceptor(UnaryClientInterceptor),)if err != nil {panic(err)}}

routeguide 这个服务名是自己起的,其实就是拿的 grpc 官方的示例稍微改动了一下做的试验。

接下来定义一个服务器端的拦截器:

func UnaryServerInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {remote, _ := peer.FromContext(ctx)remoteAddr := remote.Addr.String()in, _ := json.Marshal(req)inStr := string(in)log.Println("ip", remoteAddr, "access_start", info.FullMethod, "in", inStr)start := time.Now()defer func() {out, _ := json.Marshal(resp)outStr := string(out)duration := int64(time.Since(start) / time.Millisecond)log.Println("ip", remoteAddr, "access_end", info.FullMethod, "in", inStr, "out", outStr, "err", err, "duration/ms", duration)}()resp, err = handler(ctx, req)return
}

在服务器启动时应用上这个单向调用的拦截器:


var (port = flag.Int("p", 12305, "port")
)type server struct {}func (s server) Ping(ctx context.Context, request *routeguide.PingRequest) (reply *routeguide.PingReply, err error) {reply = &routeguide.PingReply{Reply:                "pong",}return
}func main() {lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))if err != nil {log.Fatalf("failed to listen: %v", err)}s := grpc.NewServer(grpc.UnaryInterceptor(UnaryServerInterceptor))routeguide.RegisterRouteGuideServer(s, &server{})s.Serve(lis)
}

启动服务器,用客户端调用后可以看到在服务器和客户端采集到的日志:

// 服务器日志
2021/04/24 17:24:44 ip 127.0.0.1:57258 access_start /routeguide.RouteGuide/Ping in {}
2021/04/24 17:24:44 ip 127.0.0.1:57258 access_end /routeguide.RouteGuide/Ping in {} out {"reply":"pong"} err <nil> duration/ms 0// 客户端日志
2021/04/24 17:41:11 grpc /routeguide.RouteGuide/Ping in {} out {"reply":"pong"} err <nil> duration/ms 1

与Web框架的中间件不同的是,Web框架可以给每个 handler 程序应用多个中间件,但是gRPC的客户端和服务器分别可以添加一个单向调用类型的拦截器和流式调用类型的拦截器。不过gRPC社区里Go gRPC Middleware[1] 这个软件包提供了拦截器的interceptor链式的功能,可以将多个拦截器组合成一个拦截器链。

var client routeGuideClientfunc init() {var err errorclient.cc, err = grpc.Dial("127.0.0.1:12305",grpc.WithInsecure(),grpc.WithUnaryInterceptor(grpc_middleware.ChainUnaryClient(UnaryClientInterceptor)), // 参数里可以添加多个拦截器)if err != nil {panic(err)}}

上面的示例程序为了便于理解做了部分删减,完整可运行的源码可以访问 GitHub 链接[2]获得。

社区里那些实用的拦截器

利用拦截器,可以对gRPC进行扩展,利用社区的力量将gRPC发展壮大,也可以让开发者更灵活地处理gRPC流程中的业务逻辑。下面列出了一些开源社区里发布的实用拦截器。

  1. Go gRPC Middleware[3]:提供了拦截器的interceptor链式的功能,可以将多个拦截器组合成一个拦截器链,当然它还提供了其它的功能,所以以gRPC中间件命名。

  2. grpc-multi-interceptor[4]: 是另一个interceptor链式功能的库,也可以将单向的或者流式的拦截器组合。

  3. grpc_auth[5]: 身份验证拦截器

  4. grpc_ctxtags[6]: 为上下文增加Tag map对象

  5. grpc_zap[7]: 支持zap日志框架

  6. grpc_logrus[8]: 支持logrus日志框架

  7. grpc_prometheus[9]: 支持 prometheus

  8. otgrpc[10]: 支持opentracing/zipkin

  9. grpc_opentracing[11]:支持opentracing/zipkin

  10. grpc_retry[12]: 为客户端增加重试的功能

  11. grpc_validator[13]: 为服务器端增加校验的功能

引用链接

[1]

Go gRPC Middleware: https://github.com/grpc-ecosystem/go-grpc-middleware

[2]

访问 GitHub 链接: https://github.com/kevinyan815/grpcdemo

[3]

Go gRPC Middleware: https://github.com/grpc-ecosystem/go-grpc-middleware

[4]

grpc-multi-interceptor: https://github.com/kazegusuri/grpc-multi-interceptor

[5]

grpc_auth: https://github.com/grpc-ecosystem/go-grpc-middleware/blob/master/auth

[6]

grpc_ctxtags: https://github.com/grpc-ecosystem/go-grpc-middleware/blob/master/tags

[7]

grpc_zap: https://github.com/grpc-ecosystem/go-grpc-middleware/blob/master/logging/zap

[8]

grpc_logrus: https://github.com/grpc-ecosystem/go-grpc-middleware/blob/master/logging/logrus

[9]

grpc_prometheus: https://github.com/grpc-ecosystem/go-grpc-prometheus

[10]

otgrpc: https://github.com/grpc-ecosystem/grpc-opentracing/tree/master/go/otgrpc

[11]

grpc_opentracing: https://github.com/grpc-ecosystem/go-grpc-middleware/blob/master/tracing/opentracing

[12]

grpc_retry: https://github.com/grpc-ecosystem/go-grpc-middleware/blob/master/retry

[13]

grpc_validator: https://github.com/grpc-ecosystem/go-grpc-middleware/blob/master/validator

Interceptor拦截器 -- gRPC生态里的中间件相关推荐

  1. springMVC之Interceptor拦截器

    转自:https://blog.csdn.net/qq_25673113/article/details/79153547 Interceptor拦截器用于拦截Controller层接口,表现形式有点 ...

  2. 框架:SpringMVC中Interceptor拦截器的两种实现

    Spring中使用Interceptor拦截器 SpringMVC 中的Interceptor 拦截器也是相当重要和相当有用的,它的主要作用是拦截用户的请求并进行相应的处理.比如通过它来进行权限验证, ...

  3. spring拦截器覆盖_Spring中使用Interceptor拦截器

    SpringMVC 中的Interceptor 拦截器也是相当重要和相当有用的,它的主要作用是拦截用户的请求并进行相应的处理.比如通过它来进行权限验证,或者是来判断用户是否登陆,或者是像12306 那 ...

  4. SpringMVC中使用Interceptor拦截器

    2019独角兽企业重金招聘Python工程师标准>>> SpringMVC 中的Interceptor 拦截器也是相当重要和相当有用的,它的主要作用是拦截用户的请求并进行相应的处理. ...

  5. interceptor拦截器典型应用实例----数据稽核

    interceptor拦截器典型应用实例----数据稽核 Interceptor拦截器的使用小结,程序实例是典型的 "数据稽核"过程,即在对数据库中的数据进行修改后会自动添加对应的 ...

  6. Mybatis Interceptor 拦截器

    拦截器(Interceptor)在 Mybatis 中被当做插件(plugin)对待,官方文档提供了 Executor(拦截执行器的方法),ParameterHandler(拦截参数的处理),Resu ...

  7. springboot Interceptor拦截器excludePathPatterns失效

    springboot Interceptor拦截器excludePathPatterns失效 使用jwt和HandlerInterceptorAdapter做登录的拦截放行失效,其实更多时候不是配置文 ...

  8. java url 拦截_Spring mvc设置某些url不被interceptor拦截器拦截的方法

    我们的Java类继承HandlerInterceptorAdapter类之后,实现里面的preHandle与postHandle方法,默认情况下所有的url都会被spring mvc拦截器所拦截,因为 ...

  9. 【Java代码】使用 org.apache.ibatis.plugin.Interceptor 拦截器实现全局 mapper.xml 参数注入(可用于切换数据库实例schema+Demo举例源码)

    1. why   项目没有使用MyBatis,进行数据操作时使用的是jdbc中默认的schema,现在项目要加入多租户,同一个数据库下不同租户使用不同的实例schema,这就要在mapper文件内所有 ...

最新文章

  1. CentOS 安装Python3
  2. 简单总结一下 XSS
  3. Android实现一个自己定义相机的界面
  4. 算法:多数元素,多种解法
  5. string操作小汇总
  6. CodeForces - 1017D The Wu
  7. python输入整数n计算并输出1+22+333_Python 经典练习题-016
  8. KubeEdge temperature 部署
  9. CAS的ABA问题及解决方案
  10. 【转】linux shell 中21含义
  11. PAT:1050. String Subtraction (20) AC
  12. 数据结构十大排序算法(python)
  13. 创建 tomcat 服务的镜像
  14. 图像分类模型AlexNet解读
  15. 以太坊(9)go-ethereum + mist 开发环境搭建 --mac
  16. 风寒感冒一般低烧或者不发烧,清鼻涕。风热感冒一般有发烧比较厉害,黄鼻涕的反应
  17. python猴子分桃_猴子分桃 - Ryan in C++ - 博客园
  18. 绿色建筑评定解析丨智行有嘉
  19. php如何实现简繁体互转
  20. oracle序列号、

热门文章

  1. 中小企业集群ntpd服务搭建
  2. 51CTO首页改版上线 欢迎大家拍砖!
  3. 6、Actor,Stage的学习
  4. Android手机编程初学遇到的问题及解决方法
  5. XP下安装SQL2000企业版
  6. 对于公司来说,企业内训是否真的有必要?
  7. xpath中如何使用变量
  8. 《Android 3D游戏开发技术宝典——OpenGL ES 2.0》——2.4节文件I/O
  9. NFS网络文件系统服务配置、验证及错误解决
  10. 与MQ通讯的完整JAVA程序