gRPC中interceptor拦截器的使用教程

 更新时间:2023年08月10日 11:20:04   作者:yangnk  
gRPC中的interceptor拦截器分为客户端拦截器和服务端拦截器,分别是在客户端和服务端的请求被发送出去之前进行处理的逻辑,下面就跟随小编一起学习一下interceptor拦截器的具体使用吧

一、使用场景

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拦截器的资料请关注脚本之家其它相关文章!

相关文章

  • mysql+spring+mybatis实现数据库读写分离的代码配置

    mysql+spring+mybatis实现数据库读写分离的代码配置

    今天小编就为大家分享一篇关于mysql+spring+mybatis实现数据库读写分离的代码配置,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-03-03
  • java通过方法来交换实参的值实现方式

    java通过方法来交换实参的值实现方式

    文章介绍了Java方法参数传递方式以及如何通过数组、自定义包装类或返回结果来实现变量交换,同时,讨论了内部类中static关键字的作用和使用场景
    2025-12-12
  • Java中Druid连接池连接超时获取不到连接的解决

    Java中Druid连接池连接超时获取不到连接的解决

    这篇文章主要介绍了Java中Druid连接池连接超时获取不到连接的解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-11-11
  • IDEA强制清除Maven缓存的方法示例

    IDEA强制清除Maven缓存的方法示例

    这篇文章主要介绍了IDEA强制清除Maven缓存的方法示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-06-06
  • Java代理模式之静态代理与动态代理详解

    Java代理模式之静态代理与动态代理详解

    Java代理分为静态代理和动态代理,静态代理需手动编写代理类,适合少量对象,动态代理运行时自动生成代理类,适用于大量对象,如Spring AOP,这篇文章主要介绍了Java代理模式之静态代理与动态代理的相关资料,需要的朋友可以参考下
    2026-01-01
  • Java正则表达式基础语法详解

    Java正则表达式基础语法详解

    这篇文章主要介绍了Java正则表达式语法,包括常用正则表达式、匹配验证-验证Email是否正确以及字符串中查询字符或者字符串,感兴趣的小伙伴们可以参考一下
    2021-10-10
  • SpringBoot 中html的页面间跳转问题小结

    SpringBoot 中html的页面间跳转问题小结

    这篇文章主要介绍了SpringBoot 中html的页面间跳转问题小结,本文给大家分享两种方法,结合实例代码给大家介绍的非常详细,需要的朋友可以参考下
    2023-10-10
  • Feign踩坑源码解读之@FeignClient注入容器

    Feign踩坑源码解读之@FeignClient注入容器

    文章主要介绍了FeignClientsRegistrar类的作用和注册Feign客户端的过程,以及FeignClientFactoryBean的使用和目标对象的获取
    2024-11-11
  • 关于Java中你所不知道的Integer详解

    关于Java中你所不知道的Integer详解

    这篇文章主要给大家介绍了关于Java中你所不知道的一些关于Integer的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。
    2017-12-12
  • PowerJob的AliOssService工作流程源码解读

    PowerJob的AliOssService工作流程源码解读

    这篇文章主要介绍了PowerJob的AliOssServiceg工作流程源码解读,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2024-01-01

最新评论