SpringCloud学习 Feign远程调用 异步调用CompletableFuture 丢失请求头问题的解决
1.使用Feign进行远程调用丢失请求头问题
当使用Feign进行远程调用时,Feign会自动构造一个新的http请求再发送.因此会默认丢掉原请求的请求头信息.导致一些重要的数据丢失(Cookie)
解决方法: 添加Feign远程调用拦截器,在拦截器中进行请求头信息的同步.
原理:Feign在构造http远程调用请求时,会检查是否有添加拦截器RequestInterceptor(默认没有),如果有,在构造时会先执行拦截器的apply方法,因此在apply方法中实现请求头信息的同步可以解决原请求头信息丢失的问题.
设置拦截器:
import feign.RequestInterceptor;
import feign.RequestTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
y
@Configuration
public class MyFeignConfigration {
//设置feign发送请求的拦截器,解决feign远程调用请求头丢失的问题
@Bean
public RequestInterceptor requestInterceptor(){
return new RequestInterceptor() {
//设置以后,Feign在远程调用之前,会先执行apply方法
@Override
public void apply(RequestTemplate requestTemplate) {
//1.使用RequestContextHolder获取在Controller层进入的请求的所有属性,底层是使用ThreadLocal的机制
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
//2.同步请求头数据 (Cookie)
String cookie = request.getHeader("Cookie");
requestTemplate.header("Cookie",cookie);
}
};
}
}
2. 使用Feign+CompletableFuture 异步调用丢失请求头的问题
场景:当使用Feign+CompletableFuture进行异步(多个线程之间)远程调用时,因为线程发送了改变.导致之前添加拦截器RequestInterceptor失效,(因为RequestContextHolder底层使用ThreadLocal进行同一线程的数据共享),而异步情况下线程改变,导致无法获取到原来的请求.
错误代码: 在CompletableFuture异步任务中进行Feign调用
CompletableFuture<Void> addrFuture = CompletableFuture.runAsync(() -> {
//远程查询所有的收获地址
List<MemberAddr> memberAddrs = memberFeignService.infoById(memberResVo.getId());
confirmData.setAddress(memberAddrs);
}, executor);
CompletableFuture<Void> cartFuture = CompletableFuture.runAsync(() -> {
//远程查询购物车所有选中的购物项
List<OrderItemVo> userCartItems = cartFeignService.getUserCartItems();
confirmData.setItems(userCartItems);
}, executor);
解决方法:使用RequestInterceptor进行拦截的前提下,在feign调用前手动添加请求信息
//获取原线程的请求参数
RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
CompletableFuture<Void> addrFuture = CompletableFuture.runAsync(() -> {
//设置请求原线程下的参数
RequestContextHolder.setRequestAttributes(attributes);
//远程查询所有的收获地址
List<MemberAddr> memberAddrs = memberFeignService.infoById(memberResVo.getId());
confirmData.setAddress(memberAddrs);
}, executor);
CompletableFuture<Void> cartFuture = CompletableFuture.runAsync(() -> {
//设置请求原线程下的参数
RequestContextHolder.setRequestAttributes(attributes);
//远程查询购物车所有选中的购物项
List<OrderItemVo> userCartItems = cartFeignService.getUserCartItems();
confirmData.setItems(userCartItems);
}, executor);
ignService.getUserCartItems();
confirmData.setItems(userCartItems);
}, executor);