1、feign 远程调用丢失请求头的问题
原因:feign不会同步request的请求头。
解决方案:使用interceptor将用户请求的请求头设置给feign代理的请求
/**
* @DESCRIPTION feign的请求拦截器,用于将用户请求头设置给feign代理后的请求,防止代理后丢失请求头
* @Author yaya
* @DATE 2022/10/7
*/
@Configuration
public class FeignInterceptor {
@Bean("requestInterceptor")
public RequestInterceptor requestInterceptor(){
return new RequestInterceptor() {
@Override
public void apply(RequestTemplate requestTemplate) {
// 通过RequestContextHolder拿到用户的request
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();
String cookie = request.getHeader("Cookie");
// 将用户的cookie设置给feign代理后的请求
requestTemplate.header("Cookie", cookie);
}
};
}
}
源代码跟踪:
1、feign远程调用首先会进ReflectiveFeign.invoke方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (!"equals".equals(method.getName())) {
if ("hashCode".equals(method.getName())) {
return this.hashCode();
} else {
// 如果不是hashCode、toString方法则会调用目标方法
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;
}
}
}
2、然后会进到SynchronousMethodHandler.invoke方法
public Object invoke(Object[] argv) throws Throwable {
RequestTemplate template = this.buildTemplateFromArgs.create(argv);
Options options = this.findOptions(argv);
Retryer retryer = this.retryer.clone();
while(true) {
try {
return this.executeAndDecode(template, options);
} catch (RetryableException var9) {
RetryableException e = var9;
try {
retryer.continueOrPropagate(e);
} catch (RetryableException var8) {
Throwable cause = var8.getCause();
if (this.propagationPolicy == ExceptionPropagationPolicy.UNWRAP && cause != null) {
throw cause;
}
throw var8;
}
if (this.logLevel != Level.NONE) {
this.logger.logRetry(this.metadata.configKey(), this.logLevel);
}
}
}
}
Object executeAndDecode(RequestTemplate template, Options options) 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, options);
response = response.toBuilder().request(request).requestTemplate(template).build();
} catch (IOException var13) {
if (this.logLevel != Level.NONE) {
this.logger.logIOException(this.metadata.configKey(), this.logLevel, var13, this.elapsedTime(start));
}
throw FeignException.errorExecuting(request, var13);
}
long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);
if (this.decoder != null) {
return this.decoder.decode(response, this.metadata.returnType());
} else {
CompletableFuture<Object> resultFuture = new CompletableFuture();
this.asyncResponseHandler.handleResponse(resultFuture, this.metadata.configKey(), response, this.metadata.returnType(), elapsedTime);
try {
if (!resultFuture.isDone()) {
throw new IllegalStateException("Response handling not done");
} else {
return resultFuture.join();
}
} catch (CompletionException var12) {
Throwable cause = var12.getCause();
if (cause != null) {
throw cause;
} else {
throw var12;
}
}
}
}
在executeAndDecode方法中会执行一行代码Request request = this.targetRequest(template);这个方法就是处理request的逻辑。
3、targetRequest处理request
Request targetRequest(RequestTemplate template) {
// 遍历执行interceptor
Iterator var2 = this.requestInterceptors.iterator();
while(var2.hasNext()) {
RequestInterceptor interceptor = (RequestInterceptor)var2.next();
interceptor.apply(template);
}
return this.target.apply(template);
}
在targetRequest方法中会遍历执行interceptor。
private final List
我们往容器中放的interceptor就会被执行,request中就会有请求头了。
2、feign异步情况丢失上下文
原因:异步请求,request不是同一个,threadLocal中获取不到。
解决方案:在异步编排时手动在新线程中共享下数据:RequestContextHolder.setAttribute()。