单体应用时,我们经常会把一些共享数据,比如登录信息等放在session里面,当然也可以放在ThreadLocal里面。随着业务复杂度的提高,分布式应用越来越主流。单机的存储的思想已经不适用了,共享session应运而生,比如nosql、session复制等技术方案。
分布式架构中SpringCloud使用尤为广泛。如果想通过Feign接口的调用隐式的传递一些参数,比如用户ID、名称,接口的验签等,我们应该怎么实现呢。Feign为我们提供全链路Request拦截器:feign.RequestInterceptor
今天通过传递用户名到feign服务端,来使用 RequestInterceptor 来实现, 同时对比一下web的拦截器 HandlerInterceptor 的区别。
调用端和服务端是两个不同的服务,ThreadLocal的方式不能横跨里两个服务。feign.RequestInterceptor 接口可以将请求转发到Feign接口对应的服务中。
3.1 调用端-web拦截器模拟登录
- public class IWebRequestInterceptor implements AsyncHandlerInterceptor {
-
- @Override
- public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
- // 模拟登录
- Map
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
-
- public static void set(Map
map) { - THREAD_LOCAL.set(map);
- }
-
- public static Map
get(){ - return THREAD_LOCAL.get() == null ? new HashMap
() : 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
stringStringMap = RequestHolder.get(); - if (stringStringMap != null) {
- for (Map.Entry
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);
- }
- }
调用端的实现,已经实现了用户名传递到服务端。
服务端结果获取

结果打印:

服务端本身需要配置就可以从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
stringStringMap = new HashMap<>(); - String username = request.getHeader("username");
- stringStringMap.put("username", username);
- RequestHolder.set(stringStringMap);
- return AsyncHandlerInterceptor.super.preHandle(request, response, handler);
- }
- }