概述
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 一、为什么会丢失请求头
- 二、解决方法
- 1.单线程
- 2.异步远程调用时,请求头丢失
- 总结
一、为什么会丢失请求头
问题描述:在微服务项目中,我们做了单点登录,在项目使用feign 调用另一个模块的远程服务时,发现提示无权限调用。
//1.在远程调用的方法上打个断点
List<MemberAddressVo> address = memberFeignService.getAddress(memberRespVo.getId());
//2.进入方法内部 ReflectiveFeign.class
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//判断调用是不是equal方法
if (!"equals".equals(method.getName())) {
//判断是不是调用hashCode
if ("hashCode".equals(method.getName())) {
return this.hashCode();
} else {
//判断是不是调用toString 都不是就执行 ((MethodHandler)this.dispatch.get(method)).invoke(args);
return "toString".equals(method.getName()) ? this.toString() : ((MethodHandler)this.dispatch.get(method)).invoke(args);
}
} else {
try {
Object otherHandler = args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
return this.equals(otherHandler);
} catch (IllegalArgumentException var5) {
return false;
}
}
}
//3. ((MethodHandler)this.dispatch.get(method)).invoke(args);
//点击进入invoke 方法 SynchronousMethodHandler.class
public Object invoke(Object[] argv) throws Throwable {
//就是在这 构建了一个新的RequestTemplate ,而浏览器带给我们的请求头都会丢失
RequestTemplate template = this.buildTemplateFromArgs.create(argv);
Retryer retryer = this.retryer.clone();
while(true) {
try {
//在这即将执行该方法
return this.executeAndDecode(template);
} catch (RetryableException var8) {
RetryableException e = var8;
try {
retryer.continueOrPropagate(e);
} catch (RetryableException var7) {
Throwable cause = var7.getCause();
if (this.propagationPolicy == ExceptionPropagationPolicy.UNWRAP && cause != null) {
throw cause;
}
throw var7;
}
if (this.logLevel != Level.NONE) {
this.logger.logRetry(this.metadata.configKey(), this.logLevel);
}
}
}
}
至此,我们找到了feign远程调用请求头丢失的原因
二、解决方法
1.单线程
继续深入executeAndDecode方法 查看原因
Object executeAndDecode(RequestTemplate template) throws Throwable {
//这里 它会对我们的请求进行一些包装
Request request = this.targetRequest(template);
if (this.logLevel != Level.NONE) {
this.logger.logRequest(this.metadata.configKey(), this.logLevel, request);
}
long start = System.nanoTime();
Response response;
try {
response = this.client.execute(request, this.options);
} catch (IOException var15) {
if (this.logLevel != Level.NONE) {
this.logger.logIOException(this.metadata.configKey(), this.logLevel, var15, this.elapsedTime(start));
}
throw FeignException.errorExecuting(request, var15);
}
//下面我们查看一下targetRequest方法
Request targetRequest(RequestTemplate template) {
//拿到对应的所有请求拦截器的迭代器
Iterator var2 = this.requestInterceptors.iterator();
//遍历所有的请求拦截器
while(var2.hasNext()) {
RequestInterceptor interceptor = (RequestInterceptor)var2.next();
//这里是每个请求拦截器 依次对该方法进行包装
interceptor.apply(template);
}
return this.target.apply(template);
}
//我们发现它是一个接口 所以可以重写一下这个方法 对我们的请求做一些包装 借鉴一下别的实现方法
public interface RequestInterceptor {
void apply(RequestTemplate var1);
}
public class BasicAuthRequestInterceptor implements RequestInterceptor {
public void apply(RequestTemplate template) {
template.header("Authorization", new String[]{this.headerValue});
}
}
//解决方法----》 我自己项目的你们可以借鉴
@Configuration
public class GuliFeignConfig {
@Bean("requestInterceptor")
public RequestInterceptor requestInterceptor(){
//给容器中放一个RequestInterceptor 重写apply 在里面给请求设置上自己需要的请求头等信息
return new RequestInterceptor() {
@Override
public void apply(RequestTemplate requestTemplate) {
//1.RequestContextHolder 拿到刚进来的这个请求
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();//老请求
if(request!=null){
//同步请求头数据,Cookie
String cookie = request.getHeader("Cookie");
//给新请求同步老请求的cookie
requestTemplate.header("Cookie",cookie);
}
System.out.println("feign 在远程调用之前先进行 RequestInterceptor.apply");
}
};
}
}
至此 问题完美解决
2.异步远程调用时,请求头丢失
上面 同步的情况下 我们已经解决请求头丢失的问题,然而 在实际项目中 我们很可能需要异步调用多个远程服务,这个时候 我们会发现 feign 请求头丢失的问题又出现了
//1.问题主要出在 RequestContextHolder.getRequestAttributes();上,点进这个方法 看下源码
@Nullable
public static RequestAttributes getRequestAttributes() {
//它是从requestAttributesHolder这里面取出来的
RequestAttributes attributes = (RequestAttributes)requestAttributesHolder.get();
if (attributes == null) {
attributes = (RequestAttributes)inheritableRequestAttributesHolder.get();
}
return attributes;
}
//2.接着追 我们发现requestAttributesHolder是一个NamedThreadLocal对象
private static final ThreadLocal<RequestAttributes> requestAttributesHolder = new NamedThreadLocal("Request attributes");
//3.我们发现NamedThreadLocal继承自ThreadLocal
//而ThreadLocal是一个线程局部变量,在不同线程之间是独立的所以我们获取不到 原先主线程的请求属性,即给请求头添加cookie失败
public class NamedThreadLocal<T> extends ThreadLocal<T> {
private final String name;
public NamedThreadLocal(String name) {
Assert.hasText(name, "Name must not be empty");
this.name = name;
}
public String toString() {
return this.name;
}
}
//4.解决方法(这里只提供了一种 变量复制)
//获取之前的请求
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
CompletableFuture<Void> getAddressFuture = CompletableFuture.runAsync(() -> {
//每一个线程都共享之前的请求数据
RequestContextHolder.setRequestAttributes(requestAttributes);
//1.远程查询所有的收货地址列表
List<MemberAddressVo> address = memberFeignService.getAddress(memberRespVo.getId());
confirmVo.setAddress(address);
System.out.println("副线程1。。。"+Thread.currentThread().getId());
}, executor);
总结
Feign远程调用请求头丢失问题目前就了解了这么多,有兴趣的同学可以一起探讨探讨,也可以自己去谷粒学院看看 (ps:我是看视频学到的方法)
最后
以上就是心灵美糖豆为你收集整理的Feign远程调用请求头丢失问题一、为什么会丢失请求头二、解决方法总结的全部内容,希望文章能够帮你解决Feign远程调用请求头丢失问题一、为什么会丢失请求头二、解决方法总结所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复