本笔记内容为尚硅谷谷粒商城远程调用和异步调用丢失请求头问题部分
目录
一、Feign远程调用丢失请求头
二、Feign异步调用丢失请求头问题
一、Feign远程调用丢失请求头
问题:
feign在远程调用之前要构造请求,调用了很多的拦截器。
浏览器发送请求,请求头自动带来了cookie 到order服务。
order服务通过feign远程调用 cart服务,底层会给我们生成一个新的request请求去请求被调用的服务,并且这个请求不携带任何请求头,从而导致了在被调用方法中获取不到请求头cookie的问题。没有带请求头,cart服务就认为没登录。
解决:加上feign远程调用的请求拦截器
进入targetRequest方法,发现如果我们有拦截器的话他先会去执行
所以我们只需要在容器配置一个RequestInterceptor
类型的拦截器,将原请求中携带的请求头Cookie封装到新的请求中即可。
@Configuration
public class GulimallFeignConfig {
@Bean("requestInterceptor")
public RequestInterceptor requestInterceptor(){
return new RequestInterceptor() {
@Override
public void apply(RequestTemplate template) {
//1、RequestContextHolder拿到刚进来的这个请求
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest(); //旧请求
//同步请求头数据,Cookie、
String cookie = request.getHeader("Cookie");
//给新请求同步了旧请求的cookie
template.header("Cookie",cookie);
}
};
}
}
注意:RequestContextHolder是基于本地线程线程作用域(TreadLocal)获取的,如果请求映射到的controller控制器方法和执行到这里使用的线程不同,这里是获取不到原请求的。
二、Feign异步调用丢失请求头问题
问题:
在一个请求的执行过程中,如果有一个新的异步请求出来执行别的任务,那么在该新的请求中是拿不到老的请求的上下文环境的。
解决:
异步情况下保证主线程的ThreadLocal和附线程用同一个线程,可以在新的上下文环境中将老的上下文环境赋值过去
@Override
public OrderConfirmVo confirmOrder() throws ExecutionException, InterruptedException {
OrderConfirmVo confirmVo = new OrderConfirmVo();
MemberRespVo memberRespVo = LoginUserInterceptor.loginUser.get();
System.out.println("主线程..."+Thread.currentThread().getId());
//获取之前的请求
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
CompletableFuture<Void> getAddressFuture = CompletableFuture.runAsync(() -> {
//1、远程查询所有的收货地址列表
System.out.println("member线程..."+Thread.currentThread().getId());
//每一个线程都来共享之前的请求数据
RequestContextHolder.setRequestAttributes(requestAttributes);
List<MemberAddressVo> address = memberFeignService.getAddress(memberRespVo.getId());
confirmVo.setAddress(address);
}, executor);
CompletableFuture<Void> cartFuture = CompletableFuture.runAsync(() -> {
//2、远程查询购物车所有选中的购物项
System.out.println("cart线程..."+Thread.currentThread().getId());
//每一个线程都来共享之前的请求数据
RequestContextHolder.setRequestAttributes(requestAttributes);
List<OrderItemVo> items = cartFeignService.getCurrentUserCartItems();
confirmVo.setItems(items);
//feign在远程调用之前要构造请求,调用很多的拦截器
//RequestInterceptor interceptor : requestInterceptors
}, executor);
//3、查询用户积分
Integer integration = memberRespVo.getIntegration();
confirmVo.setIntegration(integration);
//4、其他数据自动计算
//TODO 5、防重令牌
CompletableFuture.allOf(getAddressFuture,cartFuture).get();
return confirmVo;
}
结束!