gRPC中interceptor拦截器的总结和实践

简介: gRPC中的interceptor拦截器分为客户端拦截器和服务端拦截器,分别是在客户端和服务端的请求被发送出去之前进行处理的逻辑。常见的使用场景有:(1)请求日志记录及监控;(2)添加请求头数据、以便代理转发使用;(3)请求或者结果重写。

一、使用场景

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,对于注册了相应拦截器的服务端调用,都要经过这个方法。

参数:

  1. call:ServerCall 对象,包含客户端请求的 MethodDescriptor
  2. headers:请求头信息
  3. next:处理链条上的下一个处理。

三、代码实践

1.实现功能

通过代码实现以下功能:

  1. 使用grpc打印日志;
  2. 获取header信息,返回header信息;
  3. 使用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.验证结果

服务端请求结果:

E2B6C26B-1689-40D7-A180-4E9019DA0EDA

客户端请求结果:

11FC41DE-706F-45FB-8DDD-D3469FB093D2

代码地址:https://github.com/yangnk/SpringBoot_Learning/tree/master/GRPCDemo


参考资料

  1. java版gRPC实战之二:服务发布和调用:https://cloud.tencent.com/developer/article/1892790
  2. gRPC 拦截器能做些什么?:https://www.cnblogs.com/niejunlei/p/14995433.html
  3. grpc java 拦截器的使用(包含server&client):https://blog.csdn.net/mengxb12138/article/details/77991472
  4. gRPC客户端拦截器的踩坑心得:https://juejin.cn/post/7145375285091762207
目录
相关文章
|
JSON 负载均衡 Java
Springboot整合gRPC
Springboot整合gRPC
976 0
|
编解码 Java 编译器
【Protobuf】Protobuf中的Message语法规范
在Message中定义一个或者多个字段,FieldType是字段的数据类型,可以是基本类型(如int32、string、bool等)或其他定义的Message类型。fieldName是字段的名称,可以根据需求自定义。fieldNumber是字段的唯一标识号,用于在消息的二进制编码中标识字段。
1176 0
|
JSON Java Maven
SpringBoot整合gRPC踩坑回顾
本文回顾了作者在引入gRPC过程中遇到的挑战与解决方案。首先,由于SpringBoot 3.0暂不支持相关starter,导致初期预研不足,项目无法启动。接着,在编写代码时因对微服务架构理解不清,出现多个设计错误。最后,解决依赖冲突问题,特别是`protobuf-java-util`与现有依赖的冲突,通过Maven Helper插件有效排查并修复问题。此次经历加深了作者对微服务架构的理解,并为后续项目拆分打下基础。
696 2
|
6月前
|
Java 测试技术 编译器
@GrpcService使用注解在 Spring Boot 中开始使用 gRPC
本文介绍了如何在Spring Boot应用中集成gRPC框架,使用`@GrpcService`注解实现高效、可扩展的服务间通信。内容涵盖gRPC与Protocol Buffers的原理、环境配置、服务定义与实现、测试方法等,帮助开发者快速构建高性能的微服务系统。
1339 0
|
前端开发 Java Maven
使用 @GrpcClient 实现客户端
使用 @GrpcClient 实现客户端
310 0
|
存储 中间件 测试技术
gRPC(六)进阶:拦截器 interceptor
拦截器本质上就是一个特定类型的函数,所以实现拦截器只需要实现对应类型方法(方法签名相同)即可。
1562 1
gRPC(六)进阶:拦截器 interceptor
|
测试技术 API 数据库
gRPC Status 状态码枚举类型 介绍文档 (更新 gRPC Status 状态码 实操 代码技巧介绍)
gRPC Status 状态码枚举类型 介绍文档 (更新 gRPC Status 状态码 实操 代码技巧介绍)
489 5
|
存储 C++
gRPC 四模式之 双向流RPC模式
gRPC 四模式之 双向流RPC模式
1284 0
|
运维 安全 Linux
深入理解Docker自定义网络:构建高效的容器网络环境
深入理解Docker自定义网络:构建高效的容器网络环境
785 6
|
安全 Go
gRPC(七)进阶:自定义身份验证
gRPC为每个gRPC方法调用提供了Token认证支持,可以基于用户传入的Token判断用户是否登陆、以及权限等,实现Token认证的前提是,需要定义一个结构体,并实现credentials.PerRPCCredentials接口。
1826 1
gRPC(七)进阶:自定义身份验证

热门文章

最新文章