基于Feign接口的全链路拦截器

本文介绍了如何在SpringCloud分布式架构中,利用Feign的RequestInterceptor实现跨服务调用时传递参数,如用户信息。首先,通过Web拦截器将登录信息存入ThreadLocal,然后在Feign调用前,将ThreadLocal中的信息放入请求头,最后在服务端获取请求头中的信息。同时,对比了HandlerInterceptor和RequestInterceptor的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、前言

        单体应用时,我们经常会把一些共享数据,比如登录信息等放在session里面,当然也可以放在ThreadLocal里面。随着业务复杂度的提高,分布式应用越来越主流。单机的存储的思想已经不适用了,共享session应运而生,比如nosql、session复制等技术方案。

        分布式架构中SpringCloud使用尤为广泛。如果想通过Feign接口的调用隐式的传递一些参数,比如用户ID、名称,接口的验签等,我们应该怎么实现呢。Feign为我们提供全链路Request拦截器:feign.RequestInterceptor

        今天通过传递用户名到feign服务端,来使用 RequestInterceptor 来实现, 同时对比一下web的拦截器 HandlerInterceptor 的区别。

2、实现思路

        调用端和服务端是两个不同的服务,ThreadLocal的方式不能横跨里两个服务。feign.RequestInterceptor 接口可以将请求转发到Feign接口对应的服务中。

  • 创建web拦截器,模拟登录请求,将用户名存放到ThreadLocal中。
  •  调用Feign接口之前,会调用 feign.RequestInterceptor接口,将ThreadLocal中信息存放到Feign接口的Request中。
  •  通过Feign接口的Request对象就可以获取用户名

3、最佳实践-调用端

        3.1 调用端-web拦截器模拟登录

public class IWebRequestInterceptor implements AsyncHandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 模拟登录
        Map<String, String> stringStringMap = new HashMap<>();
        stringStringMap.put("username", "admin");
        RequestHolder.set(stringStringMap);
        return AsyncHandlerInterceptor.super.preHandle(request, response, handler);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        RequestHolder.remove();
        AsyncHandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}

        3.2 调用端-RequestHolder

public class RequestHolder {

    private RequestHolder() {}

    private static final ThreadLocal<Map<String, String>> THREAD_LOCAL = new ThreadLocal<Map<String, String>>();

    public static void set(Map<String, String> map){
        THREAD_LOCAL.set(map);
    }

    public static Map<String, String> get(){
        return THREAD_LOCAL.get() == null ? new HashMap<String, String>() : THREAD_LOCAL.get();
    }

    public static void remove() {
        THREAD_LOCAL.remove();
    }
    
}

        3.3 调用端-调用Feign之前的feign.RequestInterceptor

@Configuration
public class IRequestInterceptor implements RequestInterceptor {

    @Override
    public void apply(RequestTemplate template) {
        Map<String, String> stringStringMap = RequestHolder.get();
        if (stringStringMap != null) {
            for (Map.Entry<String, String> entry : stringStringMap.entrySet()){
                template.header(entry.getKey(), entry.getValue());
            }
        }
    }
}

        3.4 调用端-拦截器的配置

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new IWebRequestInterceptor()).addPathPatterns("/**");
        WebMvcConfigurer.super.addInterceptors(registry);
    }
}

        调用端的实现,已经实现了用户名传递到服务端。

4、测试结果

服务端结果获取

结果打印:

4、最佳实践-服务端

        服务端本身需要配置就可以从Request中获取Header中的用户名。但是服务端也可能调用其他服务端,资深作为调用端。所以服务端也应该要有和调用端一样的配置,最终把用户名保存再自己的服务的ThreadLocal里面。既可以从ThreadLocal里面去也可以从Request里面取。

        配置同调用端。web拦截的的配置略有差异,需要从Request中湖区参数,放到服务端的ThreadLocal中。

public class IWebRequestInterceptor implements AsyncHandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        Map<String, String> stringStringMap = new HashMap<>();
        String username = request.getHeader("username");
        stringStringMap.put("username", username);
        RequestHolder.set(stringStringMap);
        return AsyncHandlerInterceptor.super.preHandle(request, response, handler);
    }
}

5、HandlerInterceptor 和 RequestInterceptor 的区别

  • HandlerInterceptor是 org.springframework.web.servlet(spring-webmvc)包下,而RequestInterceptor 是feign(feign-core)包下。
  • HandlerInterceptor拦截器是需要配置拦截的请求,RequestInterceptor 不用,只需要交给Spring管理即可。
  • HandlerInterceptor优先与RequestInterceptor 执行,RequestInterceptor 在调用Feign接口之前执行
### Feign 常见面试问题及答案 #### 1. Feign 是什么? Feign 是一种声明式的 Web Service 客户端,它使得编写 HTTP API 的客户端变得更加简单。开发者只需创建一个接口并用注解来配置即可[^2]。 #### 2. Feign 如何实现请求拦截功能? Feign 支持请求拦截器的功能,可以通过实现 `RequestInterceptor` 接口,在发送请求前添加自定义逻辑。例如可以用来设置公共的请求头或者对请求参数进行加密等操作。具体实现方式如下所示: ```java @Configuration public class FeignConfig { @Bean public RequestInterceptor headerInterceptor() { return template -> { template.header("Key", "Value"); // 添加自定义头部 // 可以在此处加入其他自定义逻辑 }; } } ``` 上述代码展示了如何通过 Feign 配置类注入一个拦截器 Bean 来完成请求拦截的任务。 #### 3. Spring Cloud 中断路器的作用是什么? Spring Cloud 断路器的主要作用是在分布式系统中保护服务调用方免受级联失败的影响。当某个远程服务不可用或响应缓慢时,断路器能够快速返回错误而不是长时间等待,从而提高系统的稳定性和可用性[^1]。 虽然此问题是关于 Spring Cloud 断路器的内容,但在实际开发过程中,如果使用了 Hystrix 或 Resilience4j 等工具作为断路器组件,则可能也会集成到 Feign 请求链路当中去共同发挥作用。 #### 4. 在微服务架构下,Feign 和 RestTemplate 的区别有哪些? 两者都是用于发起 HTTP 调用的技术方案,但它们之间存在一些显著差异: - **编程模型**:RestTemplate 属于命令式风格;而 Feign 则采用的是声明式设计模式。 - **易用程度**:相比手动构建 URL 并拼接参数等方式来说,基于接口定义的服务调用形式更加直观简洁明了。 - **性能表现**:由于内部实现了连接池管理机制等原因,默认情况下 Feign 性能优于 RestTemplate。 #### 5. Provider/Consumer 是否共享某些特定类型的全局设定项? 是的,在实际项目部署期间为了便于维护以及保持一致性原则通常会将诸如服务超时时间、重试策略等相关属性集中存储起来供生产者消费者双方共同访问利用[^3]。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

智_永无止境

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值