feign远程调用丢失请求头

2022-10-23
0 1,366

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 requestInterceptors;
我们往容器中放的interceptor就会被执行,request中就会有请求头了。

2、feign异步情况丢失上下文

原因:异步请求,request不是同一个,threadLocal中获取不到。

解决方案:在异步编排时手动在新线程中共享下数据:RequestContextHolder.setAttribute()。