gRPC中interceptor拦截器的使用教程
作者:yangnk
一、使用场景
gRPC中的interceptor拦截器分为客户端拦截器和服务端拦截器,分别是在客户端和服务端的请求被发送出去之前进行处理的逻辑。常见的使用场景有:(1)请求日志记录及监控;(2)添加请求头数据、以便代理转发使用;(3)请求或者结果重写。
二、原理分析
1.interceptor介绍
拦截器是调用在还没有到达目的地之前进行处理的逻辑,类似于Spring框架中存在的Interceptor。
gRPC 拦截器主要分为两种:客户端拦截器(ClientInterceptor),服务端拦截器(ServerInterceptor),顾名思义,分别于请求的两端执行相应的前拦截处理。
2.使用方法说明
2.1.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。
2.2.ServerInterceptor 源码
@ThreadSafe public interface ServerInterceptor { <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall( ServerCall<ReqT, RespT> call, Metadata headers, ServerCallHandler<ReqT, RespT> next); }
它只有一个方法:interceptCall,对于注册了相应拦截器的服务端调用,都要经过这个方法。
参数:
- call:ServerCall 对象,包含客户端请求的 MethodDescriptor
- headers:请求头信息
- next:处理链条上的下一个处理。
三、代码实践
1.实现功能
通过代码实现以下功能:
- 使用grpc打印日志;
- 获取header信息,返回header信息;
- 使用grpc获取ip信息,获取客户端传递过来的信息;
2.服务端实现代码
(1)实现自定义ServerGrpcInterceptor
只需要实现ServerInterceptor接口,只需要重写interceptCall方法
import io.grpc.*; import io.grpc.netty.shaded.io.netty.util.internal.StringUtil; import lombok.extern.slf4j.Slf4j; import java.util.HashMap; import java.util.Map; /** * @author yangnk * @desc * @date 2023/08/07 23:17 **/ @Slf4j public class MyServerGrpcInterceptor implements ServerInterceptor { @Override public ServerCall.Listener interceptCall(ServerCall serverCall, Metadata metadata, ServerCallHandler serverCallHandler) { //1.打印请求方法 log.info("请求方法:{}", serverCall.getMethodDescriptor()); //2.从请求的属性中获取远程地址 String remoteAddr = serverCall.getAttributes().get(Grpc.TRANSPORT_ATTR_REMOTE_ADDR).toString(); log.info("远程地址为:{}", remoteAddr); //3.获取header中的参数进行业务处理 Map map = new HashMap(); map.put("00000001", "admin"); //获取header中参数 Metadata.Key token = Metadata.Key.of("token", Metadata.ASCII_STRING_MARSHALLER); Metadata.Key userId = Metadata.Key.of("userId", Metadata.ASCII_STRING_MARSHALLER); String tokenStr = metadata.get(token); if (StringUtil.isNullOrEmpty(tokenStr)){ System.err.println("未收到客户端token,关闭此连接"); serverCall.close(Status.DATA_LOSS,metadata); } //获得token去中查询 String userInfo = map.get(metadata.get(userId)); if(StringUtil.isNullOrEmpty(userInfo)){ System.err.println("客户端token错误,关闭此连接"); serverCall.close(Status.DATA_LOSS,metadata); } //服务端写回参数 ServerCall newServerCall = new ForwardingServerCall.SimpleForwardingServerCall(serverCall) { @Override public void sendHeaders(Metadata headers) { headers.put(userId,userInfo); super.sendHeaders(headers); } }; return serverCallHandler.startCall(newServerCall, metadata); } }
(2)全局配置ServerGrpcInterceptor
通过@GrpcGlobalServerInterceptor注解配置Interceptor
import com.yangnk.grpcserver.dialoutService.DialoutGrpcInterceptor; import com.yangnk.grpcserver.dialoutService.MyServerGrpcInterceptor; import io.grpc.ServerInterceptor; import net.devh.boot.grpc.server.interceptor.GrpcGlobalServerInterceptor; import org.springframework.context.annotation.Configuration; @Configuration(proxyBeanMethods = false) public class GlobalInterceptorConfiguration { @GrpcGlobalServerInterceptor ServerInterceptor myServerInterceptor() { return new MyServerGrpcInterceptor(); } }
3.客户端实现代码
(1)实现自定义ClientGrpcInterceptor
只需要实现ClientInterceptor接口,只需要重写interceptCall方法
import io.grpc.*; import lombok.extern.slf4j.Slf4j; /** * @author yangnk * @desc * @date 2023/08/08 00:15 **/ @Slf4j public class MyClientGrpcInterceptor implements ClientInterceptor { @Override public ClientCall interceptCall(MethodDescriptor method, CallOptions callOptions, Channel next) { Metadata.Key token = Metadata.Key.of("token", Metadata.ASCII_STRING_MARSHALLER); Metadata.Key userId = Metadata.Key.of("userId", Metadata.ASCII_STRING_MARSHALLER); //1.打印日志 log.info("请求名称:{}", method.getFullMethodName()); //2.请求参数放到header中 return new ForwardingClientCall.SimpleForwardingClientCall(next.newCall(method, callOptions)) { @Override public void start(Listener responseListener, Metadata headers) { //此处为你登录后获得的token的值 headers.put(userId, "00000001"); headers.put(token, "A2D05E5ED2414B1F8C6AEB19F40EF77C"); super.start(new ForwardingClientCallListener.SimpleForwardingClientCallListener(responseListener) { @Override public void onHeaders(Metadata headers) { log.info("请求返回信息为:" + headers); super.onHeaders(headers); } }, headers); } }; } }
(2)全局配置ClientGrpcInterceptor
通过@GrpcGlobalClientInterceptor注解配置Interceptor
import io.grpc.ClientInterceptor; import net.devh.boot.grpc.client.interceptor.GrpcGlobalClientInterceptor; import org.springframework.context.annotation.Configuration; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; @Order(Ordered.LOWEST_PRECEDENCE) @Configuration(proxyBeanMethods = false) public class GlobalClientInterceptorConfiguration { @GrpcGlobalClientInterceptor ClientInterceptor myClientInterceptor() { return new MyClientGrpcInterceptor(); } }
4.验证结果
服务端请求结果:
客户端请求结果:
代码地址:https://github.com/yangnk/SpringBoot_Learning/tree/master/GRPCDemo
以上就是gRPC中interceptor拦截器的使用教程的详细内容,更多关于interceptor拦截器的资料请关注脚本之家其它相关文章!