Go gRPC 学习系列:

  1. 跟我一起学Go系列:gRPC 入门必备

第一篇内容我们已经基本了解到 gRPC 如何使用 、对应的三种流模式。现在已经可以让服务端和客户端互相发送消息。本篇仍然讲解功能性的使用说明:如何使用拦截器。使用过 Java 的同学知道 Spring 或者 Dubbo,这两个框架都提供了拦截器的支持,拦截器的作用无需多言,鉴权,Tracing,数据统计等等。

在 gRPC 中拦截器的实现会稍微有所不同,原因在于 gRPC 多了一种流式数据传输模式。所以这种拦截器的处理也变得相对复杂。

拦截器类型

  1. UnaryServerInterceptor 服务端拦截,在服务端接收请求的时候进行拦截。
  2. UnaryClientInterceptor 这是一个客户端上的拦截器,在客户端真正发起调用之前,进行拦截。
  3. StreamClientInterceptor 在流式客户端调用时,通过拦截 clientstream 的创建,返回一个自定义的 clientstream, 可以做一些额外的操作。
  4. StreamServerInterceptor 在服务端接收到流式请求的时候进行拦截。

拦截器使用

普通拦截器

在 gRPC 中拦截器被定义成一个变量:

type UnaryServerInterceptor func(ctx context.Context, req interface{}, info *UnaryServerInfo, handler UnaryHandler) (resp interface{}, err error)

参数含义如下:

  • ctx context.Context:请求上下文
  • req interface{}:RPC 方法的请求参数
  • info *UnaryServerInfo:RPC 方法的所有信息
  • handler UnaryHandler:RPC 方法真正执行的逻辑

它本质是一个方法,拦截器的应用是在服务端启动的时候要注册上去,从 grpc.NewServer(opts...) 这里开始,这里需要一个 ServerOption 对象:

//注册拦截器 创建gRPC服务器
s := grpc.NewServer(grpc.UnaryInterceptor(LoggingInterceptor))

gRPC 在 v1.28.0 版本增加了多 interceptor 支持,可以在不借助第三方库(go-grpc-middleware)的情况下添加多个 interceptor。看一下 grpc.UnaryInterceptor() 方法的定义:

func UnaryInterceptor(i UnaryServerInterceptor) ServerOption {
return newFuncServerOption(func(o *serverOptions) {
if o.unaryInt != nil {
panic("The unary server interceptor was already set and may not be reset.")
}
o.unaryInt = i
})
}

参数为一个 UnaryServerInterceptor ,即传入 UnaryServerInterceptor 类型的方法即可,所以自定义一个拦截器就变得很简单,只需要定义一个 UnaryServerInterceptor 类型的方法。比如我们实现一个打印日志的拦截器:

//拦截器 - 打印日志
func LoggingInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler) (interface{}, error) {
fmt.Printf("gRPC method: %s, %v", info.FullMethod, req)
resp, err := handler(ctx, req)
fmt.Printf("gRPC method: %s, %v", info.FullMethod, resp)
return resp, err
}

可以看到只需要按照 UnaryServerInterceptor 方法的参数去构造即可。然后就是应用,在 server 注册的时候:

grpc.NewServer(grpc.UnaryInterceptor(LoggingInterceptor))  // 创建gRPC服务器
......
......
......

将拦截器注册上就行,是不是很简单。

流拦截器

流拦截器过程和一元拦截器有所不同,同样可以分为3个阶段:

  • 预处理(pre-processing)
  • 调用RPC方法(invoking RPC method)
  • 后处理(post-processing)

预处理阶段的拦截只是在流式请求第一次 发起的时候进行拦截,后面的流式请求不会再进入到处理逻辑。

后面两种情况对应着 Streamer api 提供的两个扩展方法来进行,分别是 SendMsg 和 RecvMsg 方法。

正常情况下实现一个流式拦截器与普通拦截器一样,实现这个已经定义好的拦截器方法即可:

type StreamServerInterceptor func(srv interface{}, ss ServerStream, info *StreamServerInfo, handler StreamHandler) error

如果是想在发消息之前和之进行处理, 则实现 SendMsg 和 RecvMsg:

type wrappedStream struct {
grpc.ServerStream
} func newWrappedStream(s grpc.ServerStream) grpc.ServerStream {
return &wrappedStream{s}
} func (w *wrappedStream) RecvMsg(m interface{}) error {
fmt.Printf("Receive a message (Type: %T) at %s", m, time.Now().Format(time.RFC3339))
return w.ServerStream.RecvMsg(m)
} func (w *wrappedStream) SendMsg(m interface{}) error {
fmt.Printf("Send a message (Type: %T) at %v", m, time.Now().Format(time.RFC3339))
return w.ServerStream.SendMsg(m)
}

首先我们自己包装一个 grpc.ServerStream ,然后去实现它的 SendMsg 和 RecvMsg 方法。然后就是将这个 ServerStream 应用到拦截器中去:

//发消息前后流式调用拦截器
func SendMsgCheckStreamServerInterceptor(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo,
handler grpc.StreamHandler) error {
fmt.Printf("gRPC method: %s,", info.FullMethod)
err := handler(srv, newWrappedStream(ss))
fmt.Printf("gRPC method: %s", info.FullMethod)
return err
}

这里注意到参数 ss 使用我们自己定义的 ServerStream 包装一下。

相关测试代码都在这里,点击查看

跟我一起学 Go 系列:gRPC 拦截器的更多相关文章

  1. ASP.NET Core 3.0 gRPC 拦截器

    目录 ASP.NET Core 3.0 使用gRPC ASP.NET Core 3.0 gRPC 双向流 ASP.NET Core 3.0 gRPC 拦截器 一. 前言 前面两篇文章给大家介绍了使用g ...

  2. gRPC 拦截器能做些什么?

    什么是拦截器? 拦截器是一种横切维度的功能延展. 具象说明一下,高速收费站就是一种拦截器.它可以做什么?收费,查证,交通控制等等,面向所有穿行过往的车辆. gRPC 拦截器主要分为两种:客户端拦截器( ...

  3. 源码解析Grpc拦截器(C#版本)

    前言 其实Grpc拦截器是我以前研究过,但是我看网上相关C#版本的源码解析相对少一点,所以笔者借这篇文章给大家分享下Grpc拦截器的实现,废话不多说,直接开讲(Grpc的源码看着很方便,包自动都能还原 ...

  4. Android 开发 框架系列 OkHttp拦截器

    前言 此篇博客只讲解okhttp的拦截器功能的详细使用,如果你还不太了解okhttp可以参考我另外一篇博客 Android 开发 框架系列 OkHttp使用详解 添加Interceptor的简单例子 ...

  5. 从 1 开始学 JVM 系列 | JVM 类加载器(一)

    从 1 开始学 JVM 系列 类加载器,对于很多人来说并不陌生.我自己第一次听到这个概念时觉得有点"高大上",觉得只有深入 JDK 源码才会触碰到 ClassLoader,平时都是 ...

  6. 从零开始学 Java - Spring AOP 拦截器的基本实现

    一个程序猿在梦中解决的 Bug 没有人是不做梦的,在所有梦的排行中,白日梦最令人伤感.不知道身为程序猿的大家,有没有睡了一觉,然后在梦中把睡之前代码中怎么也搞不定的 Bug 给解决的经历?反正我是有过 ...

  7. Java内功修炼系列一拦截器

    在动态代理中,我们知道在代理类中,执行真实对象的方法前后可以增加一些其他的逻辑,这些逻辑并不是真实对象能够实现的方法,比如一个租房的用户希望租一套公寓,但是中介所代理的这个房东并没有可以出租的公寓,那 ...

  8. Struts2-学习笔记系列(14)-拦截器

    6.1对action 的拦截 自定义拦截器: public class MyInterceptor extends AbstractInterceptor { private String name; ...

  9. 跟我一起学Go系列:Go gRPC 安全认证机制-SSL/TLS认证

    Go gRPC 系列: 跟我一起学Go系列:gRPC 拦截器使用 跟我一起学Go系列:gRPC 入门必备 第一篇入门说过 gRPC 底层是基于 HTTP/2 协议的,HTTP 本身不带任何加密传输功能 ...

随机推荐

  1. [BD] 阿里云部署hadoop集群

    安装方式 rpm包安装:下载rpm文件后离线装,安装过程中会下载相应依赖 bin文件安装:在线安装 tar包安装 步骤 下载安装文件:买香港机器,按量付费,传到windows电脑 购买三台,按需付费, ...

  2. [刷题] 226 Invert Binary Tree

    要求 翻转一棵二叉树 实现 翻转左右子树,交换左右子树的根节点 1 class Solution { 2 public: 3 TreeNode* invertTree(TreeNode* root) ...

  3. 攻防世界(九)PHP2

    攻防世界系列:PHP2  1.打开什么信息也没有. 尝试各种首页index.php index.html 加 [F12]没有结果,最后发现是index.phps .phps文件是什么? phps文件就 ...

  4. ELK日志收集分析平台部署使用

    一.ELK介绍 开源实时日志分析ELK平台能够完美的解决我们上述的问题,ELK由ElasticSearch.Logstash和Kiabana三个开源工具组成: 1.ElasticSearch是一个基于 ...

  5. 【转】Jquery 使用Ajax获取后台返回的Json数据后,页面处理

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  6. linux系统ifconfig中网卡名和网卡配置文件名称不同的解决办法

    比如我的配置文件, cd /etc/sysconfig/network-scripts/ifcfg-eth1是这个名称,但是我使用ifconfig显示的信息却是 eth0,很明显这不是我配置文件的名称 ...

  7. 3分钟 Markdown 快速入门(超详细)(Day_33)

    Markdown 快速入门 (这个贼重要)注:所有符号要在英文状态下完成哦,中文是没有效果的. 1.标题 # 表示一级标题 ## 表示二级标题 ### 表示三级标题 #### 表示四级标题 ##### ...

  8. 『言善信』Fiddler工具 — 4、Fiddler面布局详解【工具栏】

    目录 (一)工具栏详细介绍 1.第一组工具: 2.第二组工具: 3.第三组工具: 4.第四组工具: (二)工具栏使用说明 1.Fiddler修改代理端口: 2.过滤Tunnel to...443请求链 ...

  9. jenkins配置基于角色的项目权限管理设置步骤

    jenkins配置基于角色的项目权限管理设置步骤 本文链接:https://blog.csdn.net/russ44/article/details/52276222 由于jenkins默认的权限管理 ...

  10. AutomicBoolean

    AutomicBoolean 介绍 java并发包下提供的原子变量,是原子类其中之一.基本特性是在多线程环境下,多个线程同时执行这些类的实例包含的方法时,具有排他性 当某个线程进入方法,不会被其他线程 ...