天天看點

OpenFeign遠端調用丢失請求頭問題以及異步調用丢失上下文問題

一,OpenFeign遠端調用丢失請求頭問題

OpenFeign遠端調用丢失請求頭問題以及異步調用丢失上下文問題

原理

下面是OpenFeign遠端調用的方法的源碼部分,可以看到,是Spring調用buildTemplateFromArgs.create(argv)方法,建構了一個新的請求,這個請求沒有任何的請求頭資訊,也就是我們請求頭的資料會丢失.

OpenFeign遠端調用丢失請求頭問題以及異步調用丢失上下文問題
OpenFeign遠端調用丢失請求頭問題以及異步調用丢失上下文問題
OpenFeign遠端調用丢失請求頭問題以及異步調用丢失上下文問題

但是,我們看到,在建構這個新請求的時候,會進入到executeAndDecode(template, options)方法,進入到這個方法以後,我們看到,繼續調用調用Request request = targetRequest(template)對請求做了處理,

OpenFeign遠端調用丢失請求頭問題以及異步調用丢失上下文問題

可以看到,周遊我們所有的攔截器Interceptor,給構造的請求附加參數,是以,給我們的容器中加上一個攔截器Interceptor就可以解決請求頭丢失問題,代碼如下

@Configuration
public class NaisiFeignConfig {

    @Bean
    public RequestInterceptor requestInterceptor(){
        return new RequestInterceptor() {
            @Override
            public void apply(RequestTemplate template) {
                //2,RequestContextHolder拿到剛進來的這個請求
                //RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
                ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
                System.out.println("RequestInterceptor線程"+Thread.currentThread().getId());
                //老請求
                //空指針Exception
                HttpServletRequest request = requestAttributes.getRequest();
                if (request != null){
                    //同步請求頭資料,Cookie
                    String cookie = request.getHeader("Cookie");
                    //給新請求同步了老請求的Cookie
                    template.header("Cookie",cookie);
                }
            }
        };
    }
}
           

這時,新請求一進來就會先調用我們的攔截器,我們在攔截器給請求設定上所需要的Header請求頭資訊就可以.

二,異步調用丢失上下文問題

OpenFeign遠端調用丢失請求頭問題以及異步調用丢失上下文問題

上面是OpenFeign遠端調用丢失請求頭問題,此時,我們發現,如果是異步請求,我們的攔截器就不能設定上所需要的Header資訊.

原因

因為我們是異步請求,請求不一樣

這裡是使用ThreadLocal,即可以拿到同一個線程攜帶的資料,但是不同線程的資料,我們是拿不到的,

OpenFeign遠端調用丢失請求頭問題以及異步調用丢失上下文問題

解決方法

我們在發遠端調用請求之前,給異步請求添加上上下文資訊即可

//擷取之前的請求
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        //1,遠端查詢所有的收貨位址清單
        CompletableFuture<Void> getAddress = CompletableFuture.runAsync(() -> {
            System.out.println("address線程"+Thread.currentThread().getId());
            //每一個線程都來共享之前請求的資料
            RequestContextHolder.setRequestAttributes(requestAttributes);
            List<MemberAddressVo> address = memberFeignService.getAddress(memberRespVo.getId());
            confirmVo.setAddress(address);
        }, executor);
           

此時就可以解決上下文丢失問題