什么是拦截器?

拦截器是一种横切维度的功能延展。

具象说明一下,高速收费站就是一种拦截器。它可以做什么?收费,查证,交通控制等等,面向所有穿行过往的车辆。

gRPC 拦截器主要分为两种:客户端拦截器(ClientInterceptor),服务端拦截器(ServerInterceptor),顾名思义,分别于请求的两端执行相应的前拦截处理。

一、客户端拦截器

1、作用时机?

请求被分发出去之前。

2、可以做什么?

a)、请求日志记录及监控

b)、添加请求头数据、以便代理转发使用

c)、请求或者结果重写

通常,如果要提供认证信息的话,可以使用 CallCredentials 实现,虽然,拦截器里也可以通过设置  CallOptions 来提供。

3、ClientInterceptor 源码

@ThreadSafe
public interface ClientInterceptor { <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next);
}

它只有一个方法:interceptCall,对于注册了相应拦截器的客户端调用,都要经过这个方法,

参数:

1、method:MethodDescriptor 类型,标示请求方法。包括方法全限定名称、请求服务名称、请求、结果、序列化工具、幂等等。

2、callOptions:此次请求的附带信息。

3、next:执行此次 RPC 请求的抽象链接管道(Channel)

返回结果:

ClientCall,包含请求及结果信息,并且不为null

4、最简单的实现

什么也不做:

public class MyGrpcClientInterceptor implements ClientInterceptor {
@Override
public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) {
return next.newCall(method, callOptions);
}
}

可以看到我们的实现里,没有实现任何逻辑,直接执行了 next.newCall 继续执行客户端的此次调用。

next.newCall 只能在当前上下文中执行,每次调用以及返回都必须是一个完整地回路,逃逸使用会导致不必要的内存泄漏问题。

5、延伸使用

通过 callOption 设置超时及认证信息

public class MyGrpcClientInterceptor implements ClientInterceptor {
@Override
public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) {
return next.newCall(method, callOptions
.withDeadlineAfter(500, TimeUnit.MILLISECONDS) //设置超时
.withCallCredentials(new CallCredentials() { //设置认证信息
@Override
public void applyRequestMetadata(RequestInfo requestInfo, Executor appExecutor, MetadataApplier applier) {
Metadata metadata = new Metadata();
metadata.put(Metadata.Key.of("id", Metadata.ASCII_STRING_MARSHALLER), "king");
applier.apply(metadata);
} @Override
public void thisUsesUnstableApi() {}
}));
}
}

看着是不是很熟悉,stub 调用时设置,只不过在这里换了一个设置场景。

日志记录:

public class MyGrpcClientInterceptor implements ClientInterceptor {
@Override
public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) {
CallOptions myCallOptions = callOptions
.withDeadlineAfter(500, TimeUnit.MILLISECONDS) //设置超时
.withCallCredentials(new CallCredentials() { //设置认证信息
@Override
public void applyRequestMetadata(RequestInfo requestInfo, Executor appExecutor, MetadataApplier applier) {
Metadata metadata = new Metadata();
metadata.put(Metadata.Key.of("id", Metadata.ASCII_STRING_MARSHALLER), "king");
applier.apply(metadata);
} @Override
public void thisUsesUnstableApi() {}
});
return new ForwardingClientCall.SimpleForwardingClientCall<ReqT, RespT>(next.newCall(method, myCallOptions)) {
@Override
public void sendMessage(ReqT message) {
System.out.println("request method: " + method.getFullMethodName());
System.out.println("request param:" + message.toString()); super.sendMessage(message);
}
};
}
}

ForwardingClientCall:ClientCall 的一个抽象实现类,用以请求的代理转发。为什么我们这里要用这个类呢?

其实我们完全可以直接使用 ClientCall 实现,只不过作为顶级抽闲类,我们必须要实现很多方法。而使用 ForwardingClientCall,则我们只需要去重写我们需要的方法就可以。

如上代码:

sendMessage 发送消息到请求服务器,可能会执行多次。此处我们记录相应的请求参数信息。

二、服务端拦截器

1、作用时机?

请求被具体的Handler相应前。

2、可以做什么?

a)访问认证

b)请求日志记录及监控

c)代理转发

3、ServerInterceptor 源码

@ThreadSafe
public interface ServerInterceptor { <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
ServerCall<ReqT, RespT> call,
Metadata headers,
ServerCallHandler<ReqT, RespT> next);
}

参数:

call:ServerCall 对象,包含客户端请求的 MethodDescriptor

headers:请求头信息

next:处理链条上的下一个处理。

4、最简单的实现

public class MyGrpcServerInterceptor implements ServerInterceptor {

    @Override
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next) {
return next.startCall(call, headers);
}
}

ServerCallHandler:定义用以实现请求处理的接口类。

5、延伸使用

public class MyGrpcServerInterceptor implements ServerInterceptor {

    @Override
public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(ServerCall<ReqT, RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next) {
//提取认证信息
String id = headers.get(Metadata.Key.of("id", Metadata.ASCII_STRING_MARSHALLER));
return new ForwardingServerCallListener.SimpleForwardingServerCallListener<ReqT>(next.startCall(call, headers)) {
private long startTime = 0; //处理开始时间
private ReqT request;
private boolean valid = false; //认证状态 @Override
public void onComplete() {
//记录请求参数及耗时
System.out.println("process cost: " + (System.nanoTime() - startTime));
System.out.println("process param: " + request.toString());
super.onComplete();
} @Override
public void onMessage(ReqT message) {
startTime = System.nanoTime();
request = message;
if (StringUtils.equals("king", id)) {
super.onMessage(message);
} else {
valid = false;
}
} @Override
public void onHalfClose() {
//验证失败则返回 Status.UNAUTHENTICATED
if (!valid) {
call.close(Status.UNAUTHENTICATED.withDescription("auth failed"), new Metadata());
} else {
super.onHalfClose();
}
}
};
}
}

onMessage:接收到请求时进行相应处理,我们这记录处理开始时间,及请求参数,同时根据提取的认证信息进行访问验证,验证通过则继续后续处理,否则设置认证状态为 false。

onHalfClose:处理认证标示及返回。

onComplete:处理结束记录请求参数及耗时。

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拦截器(C#版本)

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

  3. 跟我一起学 Go 系列:gRPC 拦截器

    Go gRPC 学习系列: 跟我一起学Go系列:gRPC 入门必备 第一篇内容我们已经基本了解到 gRPC 如何使用 .对应的三种流模式.现在已经可以让服务端和客户端互相发送消息.本篇仍然讲解功能性的 ...

  4. springMVC 拦截器如何做登录检查及页面跳转

    一个非常简单的登录权限拦截器 问题一:登录页面的提交请求肯定是要过滤掉的,目前采用在xml里配置<mvc:mapping path="/supplier/*"/>来过滤 ...

  5. asp.net拦截器

    一 拦截器又称过滤器. asp.net mvc本身是自带3种拦截器:Action拦截器.Result拦截器.Exception拦截器. 应用中常见的拦截器有日志拦截器(Action拦截器)和异常处理拦 ...

  6. spring拦截器的简单实现Interceptor

    原文链接:http://lixuanbin.iteye.com/blog/2250100 1. 需求描述 某内部管理系统采用Spring MVC搭建,用户可以登录系统进行CRUD以及其他的一些日常管理 ...

  7. axios拦截器的使用方法

    很多时候我们需要在发送请求和响应数据的时候做一些页面处理,比如在请求服务器之前先判断以下用户是登录(通过token判断),或者设置请求头header,或者在请求到数据之前页面显示loading等等,还 ...

  8. SpringBoot 拦截器获取http请求参数

    SpringBoot 拦截器获取http请求参数-- 所有骚操作基础 目录 SpringBoot 拦截器获取http请求参数-- 所有骚操作基础 获取http请求参数是一种刚需 定义拦截器获取请求 为 ...

  9. Mybatis拦截器 mysql load data local 内存流处理

    Mybatis 拦截器不做解释了,用过的基本都知道,这里用load data local主要是应对大批量数据的处理,提高性能,也支持事务回滚,且不影响其他的DML操作,当然这个操作不要涉及到当前所lo ...

随机推荐

  1. jmeter中beanshell postprocessor结合fastjson库提取不确定个数的json参数

    在项目实践中,遇到了这样一个问题.用jmeter作http接口测试,需要的接口参数个数是不确定的.也就是说,在每次测试中,根据情况不同,可能页面中的列表中所含的参数个数是不确定的,那么要提取的参数个数 ...

  2. Java 常量值的数据类型

    Java 常量值(也叫字面量)和变量一样,也是有数据类型的. 经常有面试题考察你对 Java 常量值数据类型的理解,如下: float a = 3.3; 问你这一行代码是否正确?答案肯定是不正确.为什 ...

  3. Centos7 Samba共享服务搭建

    Centos7 Samba共享服务搭建 1.安装启动和端口 ---------------------------------------------------------------------- ...

  4. Linux 系统日志和系统信息常用命令介绍

    日志文件 日 志 文 件 说 明 /var/log/message 系统启动后的信息和错误日志,是Red Hat Linux中最常用的日志之一 /var/log/secure 与安全相关的日志信息 / ...

  5. Python数模笔记-StatsModels 统计回归(1)简介

    1.关于 StatsModels statsmodels(http://www.statsmodels.org)是一个Python库,用于拟合多种统计模型,执行统计测试以及数据探索和可视化. 2.文档 ...

  6. Navigation 实现不同fragment之间的view的共享(含动画过渡)

    以imageView的共享举例 两个fragment都要有各自的imageview视图,id可以不同,但transitonName一定要相同, 都要指定相同的src 例如: fragment A &l ...

  7. 关于Linux的一些基础命令

    今天学习scala语言,在linux系统上运行,发现对Linux的命令不太熟悉,为了熟悉掌握,也便于查询,这些命令主要是为了收藏备用,,希望能帮助到大家 linux20个常用命令是: 1.显示日期的指 ...

  8. GO学习-(2) 从零开始搭建Go语言开发环境

    从零开始搭建Go语言开发环境 一步一步,从零搭建Go语言开发环境. 安装Go语言及搭建Go语言开发环境 下载 下载地址 Go官网下载地址:https://golang.org/dl/ Go官方镜像站( ...

  9. nginx 重定向到系统维护页面

    上周末兄弟项目准备扩展服务器以便提供更好的服务,兄弟项目有一些功能是实时提供到我这边的,需要我这边暂时把对应系统功能屏蔽,因为使用nginx,所以可以直接配置nginx重定向到固定系统维护页面. ng ...

  10. HAL库|神器cubemx的正确打开方式

    前言 工欲善其事,必先利其器.HAL库的开发不一定必须使用cubemx,但是使用了cubemx,你绝对不会后悔.基于一些小伙伴对cubemx的使用还有一些疑问,本次小飞哥从新建工程到生成工程,编写应用 ...