基于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接口之前执行
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

智_永无止境

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

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

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

打赏作者

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

抵扣说明:

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

余额充值