SpringCloud之OpenFeign实现服务间请求头数据传递(OpenFeign拦截器RequestInterceptor的使用)

简介: SpringCloud之OpenFeign实现服务间请求头数据传递(OpenFeign拦截器RequestInterceptor的使用)

一、前言

在前面的文章:

SpringCloud之Feign实现声明式客户端负载均衡详细案例

我们聊了OpenFeign的概述、为什么会使用Feign代替Ribbon、Feign和OpenFeign的区别、以及详细的OpenFeign实现声明式客户端负载均衡案例。

在一些业务场景中,微服务间相互调用需要做鉴权,以保证我们服务的安全性。即:服务A调用服务B的时候需要将服务B的一些鉴权信息传递给服务B,从而保证服务B的调用也可以通过鉴权,进而保证整个服务调用链的安全。

本文我们就讨论如果通过openfeign的拦截器RequestInterceptor实现服务调用链中上下游服务请求头数据的传递。

二、实现RequestInterceptor

通过RequestInterceptor 拦截器拦截我们的openfeign服务请求,将上游服务的请求头或者请求体中的数据封装到我们的openfeign调用的请求模板中,从而实现上游数据的传递。

1、RequestInterceptor实现类

1)RequestInterceptor实现类

package com.saint.feign.config;

import feign.RequestInterceptor;
import feign.RequestTemplate;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;
import java.util.Objects;

/**
 * 自定义的Feign拦截器
 *
 * @author Saint
 */
@Slf4j
public class MyFeignRequestInterceptor implements RequestInterceptor {
    /**
     * 这里可以实现对请求的拦截,对请求添加一些额外信息之类的
     *
     * @param requestTemplate
     */
    @Override
    public void apply(RequestTemplate requestTemplate) {
        // 1. obtain request
        final ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();

        // 2. 兼容hystrix限流后,获取不到ServletRequestAttributes的问题(使拦截器直接失效)
        if (Objects.isNull(attributes)) {
            log.error("MyFeignRequestInterceptor is invalid!");
            return;
        }
        HttpServletRequest request = attributes.getRequest();

        // 2. obtain request headers,and put it into openFeign RequestTemplate
        Enumeration<String> headerNames = request.getHeaderNames();
        if (Objects.nonNull(headerNames)) {
            while (headerNames.hasMoreElements()) {
                String name = headerNames.nextElement();
                String value = request.getHeader(name);
                requestTemplate.header(name, value);
            }
        }

        // todo 需要传递请求参数时放开
        // 3. obtain request body, and put it into openFeign RequestTemplate
//        Enumeration<String> bodyNames = request.getParameterNames();
//        StringBuffer body = new StringBuffer();
//        if (bodyNames != null) {
//            while (bodyNames.hasMoreElements()) {
//                String name = bodyNames.nextElement();
//                String value = request.getParameter(name);
//                body.append(name).append("=").append(value).append("&");
//            }
//        }
//        if (body.length() != 0) {
//            body.deleteCharAt(body.length() - 1);
//            requestTemplate.body(body.toString());
//            log.info("openfeign interceptor body:{}", body.toString());
//        }
    }
}

2)使RequestInterceptor生效(均已验证)

使RequestInterceptor生效的方式有四种;

1> 代码方式全局生效

直接在Spring可以扫描到的路径使用@Bean方法将RequestInterceptor实现类注入到Spring容器;

package com.saint.feign.config;

import feign.RequestInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author Saint
 */
@Configuration
public class MyConfiguration {

    @Bean
    public RequestInterceptor requestInterceptor() {
        return new MyFeignRequestInterceptor();
    }
}

2> 配置方式全局生效

feign:
  client:
    config:
      default:
        connectTimeout: 5000
        readTimeout: 5000
        loggerLevel: full
        # 拦截器配置(和@Bean的方式二选一)
        requestInterceptors:
          - com.saint.feign.config.MyFeignRequestInterceptor

3> 代码方式针对某个服务生效

直接在@FeignClient注解中指定configuration属性为RequestInterceptor实现类

在这里插入图片描述

4、配置方式针对某个服务生效

feign:
  client:
    config:
      SERVICE-A:
        connectTimeout: 5000
        readTimeout: 5000
        loggerLevel: full
        # 拦截器配置(和@Bean的方式二选一)
        requestInterceptors:
          - com.saint.feign.config.MyFeignRequestInterceptor

2、效果验证

1)feign-server服务改造

在文章 SpringCloud之Feign实现声明式客户端负载均衡详细案例的基础下,我们修改feign-server项目,添加一个MVC拦截器(用于获取请求头中的数据)
在这里插入图片描述

1> MvcInterceptor

package com.saint.feign.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * 自定义MVC拦截器
 *
 * @author Saint
 */
@Slf4j
public class MvcInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        String token = request.getHeader("token-saint");
        log.info("obtain token is : {}", token);
        return true;
    }

}

2> MvcInterceptorConfig

设置MVC拦截器会拦截哪些路径的请求,这里是所有的请求全部拦截。

package com.saint.feign.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
 * MVC拦截器配置
 *
 * @author Saint
 */
@Configuration
public class MvcInterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new MvcInterceptor())
                .addPathPatterns("/**");
    }
}

2)结果验证

1> 执行请求:
在这里插入图片描述在这里插入图片描述

2> feign-consumer中的日志:
在这里插入图片描述

3> feign-server中的日志:
在这里插入图片描述

==结果显示,RequestInterceptor生效了==

三、结合Hystrix限流使用时的坑(仅做记录)

==此处OpenFeign依赖的SpringCloud版本是2020.X之前。==

在application.yaml文件中做如下配置开启了Hystrix限流:

feign:
  hystrix:
    enabled: true
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 30000

做完上述配置后,Feign接口的熔断机制为:线程模式

如果我们自定义了一个RequestInterceptor实现类,就会导致hystrix熔断机制失效,接口调用异常(404、null);

1、原因分析

  • 在feign调用之前,会走RequestInterceptor拦截器,拦截器中使用了ServletRequestAttributes获取请求数据;
  • 默认feign使用的是线程池模式,当开启熔断的时候,负责熔断的线程和执行Feign接口的线程不是同一个线程,ServletRequestAttributes取到的将会是空值。

2、解决方案

将hystrix熔断方式从线程模式改为信号量模式;

feign:
  hystrix:
    enabled: true
hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 30000
          strategy: SEMAPHORE

3、Hystrix线程和信号量隔离区别

在这里插入图片描述

4、线程和信号量隔离的使用场景?

1> 线程池隔离

  • 请求并发量大,并且耗时长(一般是计算量大或者读数据库);
  • 采用线程池隔离,可以保证大量的容器线程可用,不会由于其他服务原因,一直处于阻塞或者等待状态,快速失败返回。

2> 信号量隔离

  • 请求并发量大,并且耗时短(一般是计算量小,或读缓存);
  • 采用信号量隔离时的服务的返回往往非常快,不会占用容器线程太长时间;
  • 其减少了线程切换的一些开销,提高了缓存服务的效率 。
相关文章
|
2月前
|
缓存 NoSQL Java
【Azure Redis 缓存】示例使用 redisson-spring-boot-starter 连接/使用 Azure Redis 服务
【Azure Redis 缓存】示例使用 redisson-spring-boot-starter 连接/使用 Azure Redis 服务
|
26天前
|
消息中间件 存储 Java
SpringCloud基础9——服务异步通信-高级篇
消息可靠性、死信交换机、惰性队列、MQ集群
SpringCloud基础9——服务异步通信-高级篇
|
14天前
|
Java API 对象存储
微服务魔法启动!Spring Cloud与Netflix OSS联手,零基础也能创造服务奇迹!
这段内容介绍了如何使用Spring Cloud和Netflix OSS构建微服务架构。首先,基于Spring Boot创建项目并添加Spring Cloud依赖项。接着配置Eureka服务器实现服务发现,然后创建REST控制器作为API入口。为提高服务稳定性,利用Hystrix实现断路器模式。最后,在启动类中启用Eureka客户端功能。此外,还可集成其他Netflix OSS组件以增强系统功能。通过这些步骤,开发者可以更高效地构建稳定且可扩展的微服务系统。
31 1
|
2月前
|
JSON Java API
哇塞!Spring Boot 中的 @DateTimeFormat 和 @JsonFormat,竟能引发数据时间大变革!
【8月更文挑战第29天】在Spring Boot开发中,正确处理日期时间至关重要。
34 1
|
2月前
|
Java Spring
【Azure Spring Cloud】Spring Cloud Azure 4.0 调用Key Vault遇见认证错误 AADSTS90002: Tenant not found.
【Azure Spring Cloud】Spring Cloud Azure 4.0 调用Key Vault遇见认证错误 AADSTS90002: Tenant not found.
|
2月前
|
运维 Java Nacos
Spring Cloud应用框架:Nacos作为服务注册中心和配置中心
Spring Cloud应用框架:Nacos作为服务注册中心和配置中心
|
2月前
|
XML JSON Java
使用IDEA+Maven搭建整合一个Struts2+Spring4+Hibernate4项目,混合使用传统Xml与@注解,返回JSP视图或JSON数据,快来给你的SSH老项目翻新一下吧
本文介绍了如何使用IntelliJ IDEA和Maven搭建一个整合了Struts2、Spring4、Hibernate4的J2EE项目,并配置了项目目录结构、web.xml、welcome.jsp以及多个JSP页面,用于刷新和学习传统的SSH框架。
40 0
使用IDEA+Maven搭建整合一个Struts2+Spring4+Hibernate4项目,混合使用传统Xml与@注解,返回JSP视图或JSON数据,快来给你的SSH老项目翻新一下吧
|
2月前
|
Java Spring 开发者
掌握Spring事务管理,打造无缝数据交互——实用技巧大公开!
【8月更文挑战第31天】在企业应用开发中,确保数据一致性和完整性至关重要。Spring框架提供了强大的事务管理机制,包括`@Transactional`注解和编程式事务管理,简化了事务处理。本文深入探讨Spring事务管理的基础知识与高级技巧,涵盖隔离级别、传播行为、超时时间等设置,并介绍如何使用`TransactionTemplate`和`PlatformTransactionManager`进行编程式事务管理。通过合理设计事务范围和选择合适的隔离级别,可以显著提高应用的稳定性和性能。掌握这些技巧,有助于开发者更好地应对复杂业务需求,提升应用质量和可靠性。
35 0
|
2月前
|
Java Spring 容器
【Azure Spring Cloud】在Azure Spring Apps上看见 App Memory Usage 和 jvm.menory.use 的指标的疑问及OOM
【Azure Spring Cloud】在Azure Spring Apps上看见 App Memory Usage 和 jvm.menory.use 的指标的疑问及OOM
|
2月前
|
存储 Java Spring
【Azure Spring Cloud】Azure Spring Cloud服务,如何获取应用程序日志文件呢?
【Azure Spring Cloud】Azure Spring Cloud服务,如何获取应用程序日志文件呢?