Feign远程调用丢失请求头
场景铺垫
A服务通过Feign远程调用服务B,但是B服务又做了身份校验!主要就是通过用户的Cookie来判断用户身份的,但是A服远程调用时结果请求头到B服就丢失了
默认的Feign远程调用执行流程就是这样的
那么这样的流程肯定是无法满足我们的业务需求滴!!!
那么我们修改一下流程,添加一个拦截器!
单线程业务场景
1.配置拦截器
@Configuration
@Slf4j
public class MyFeignConfig {
@Bean("requestInterceptor")
public RequestInterceptor requestInterceptor() {
return new RequestInterceptor() {
@Override
public void apply(RequestTemplate requestTemplate) {
log.info("进入feign拦截器...");
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();// 老请求
String cookie = request.getHeader("Cookie");
log.info(cookie);
requestTemplate.header("Cookie", cookie);
}
};
}
}
2.业务层远程调用
@RequestMapping("/openGetCookie")
public String openGetCookie(HttpServletRequest requests) throws ExecutionException, InterruptedException {
log.info("进入demo服务器的openGetCookie方法");
String res=demoFeign.getCookie();
log.info(res);
return "demo";
}
3.本地服/远程服务控制台输出
实际上在业务层调用的时候,并没什么不同,但是在拦截器中
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
这行代码就是关键,这个是spring提供获取请求上下文的包装类!在同一个线程中能共享数据!其实也就是对ThreadLocal包装了
这里有一篇关于ThreadLocal-同一个线程共享数据的简单介绍
注意:配置拦截器并且开启Hystrix熔断保护后,会自动触发服务熔断
Feign配置拦截器后直接触发Hystrix服务熔断
多线程业务场景
这样貌似搞定了,但是还有问题,在远程调用时,如果存在多个远程调用时往往会通过异步编排任务来执行业务逻辑,线程-CompletableFutrue-异步编排那么多线程会不会存在什么问题呢!
1.使用CompletableFutrue进行任务编排
@RequestMapping("/openGetCookie")
public String openGetCookie(HttpServletRequest requests) throws ExecutionException, InterruptedException {
log.info("进入demo服务器的openGetCookie方法");
CompletableFuture<Void> f1 = CompletableFuture.runAsync(() -> {
log.info("进入f1");
log.info("模拟远程调用其他服务");
log.info("f1远程调用结果ok");
});
CompletableFuture<Void> f2 = CompletableFuture.runAsync(() -> {
log.info("进入f2");
String res=demoFeign.getCookie();
log.info("f2远程调用结果"+res);
});
CompletableFuture.allOf(f1, f2).get();
log.info("f1、f2两个编排任务结束");
return "demo";
}
2.修改拦截器代码
@Configuration
@Slf4j
public class MyFeignConfig {
@Bean("requestInterceptor")
public RequestInterceptor requestInterceptor() {
return template -> {
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
log.info("进入feign拦截器...");
if (!ObjectUtils.isEmpty(requestAttributes)) {
HttpServletRequest request = requestAttributes.getRequest();// 老请求
if (!ObjectUtils.isEmpty(request)) {
String cookie = request.getHeader("Cookie");
log.info(cookie);
template.header("Cookie", cookie);
}
}
};
}
}
其实也就添加了几行非空判断,反正逻辑报错罢了
3.测试访问
在这里采用异步编排后,远程服务又出现了同样的问题,请求头丢失了!!!
问题在上面的日志中已经打印出来了,这里使用异步编排,远程调用的时和请求进来的时候,并不是同一个线程,而上面刚开始也描述了RequestContextHolder就是对ThreadLocal的包装,而ThreadLocal又只能在当前线程共享数据,所以问题就再这里!
4.从新使用CompletableFutrue进行任务编排
@RequestMapping("/openGetCookie")
public String openGetCookie(HttpServletRequest requests) throws ExecutionException, InterruptedException {
log.info("进入demo服务器的openGetCookie方法");
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
CompletableFuture<Void> f1 = CompletableFuture.runAsync(() -> {
RequestContextHolder.setRequestAttributes(requestAttributes);
log.info("进入f1");
log.info("模拟远程调用其他服务");
log.info("f1远程调用结果ok");
});
CompletableFuture<Void> f2 = CompletableFuture.runAsync(() -> {
RequestContextHolder.setRequestAttributes(requestAttributes);
log.info("进入f2");
String res=demoFeign.getCookie();
log.info("f2远程调用结果"+res);
});
CompletableFuture.allOf(f1, f2).get();
log.info("f1、f2两个编排任务结束");
return "demo";
}
在当前远程调用前获取请求的上下文,这是还在同一个线程中,所以是能获取到请求上下文内容的!
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
然后在异步编排任务中调用远程服务之前,将获取到的请求上下文注入给当前的异步任务,也就是注入给当前线程,共享数据!
RequestContextHolder.setRequestAttributes(requestAttributes);
5.访问测试
这里从控制台中打印的线程也不是同一个线程,请求上下文也能实现数据共享,那么问题也就解决了!
这里还有个小坑,就是Feign配置拦击器后并且开启Hystrix服务熔断的时候,Hystrix的隔离策略会自动触发服务熔断,导致我们的请求还没到达远程服务是就已经自己熔断了!
Feign配置拦截器后直接触发Hystrix服务熔断
上一篇: 融资租赁与经营租赁的区别 sapFICO
下一篇: 预算模块(FM-BCS)的一点总结。