最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Feign遠(yuǎn)程調(diào)用丟失請求頭問題

 更新時(shí)間:2024年09月29日 15:02:12   作者:頭未禿  
本文介紹了在服務(wù)端項(xiàng)目中如何解決資源訪問限制問題,首先介紹了問題的產(chǎn)生,然后詳細(xì)解析了源碼,最后提出了解決方案,解決方案包括同步和異步兩種,同步時(shí)直接向Spring容器注入RequestInterceptor攔截器

前言

我們在寫服務(wù)端項(xiàng)目的時(shí)候,總會(huì)限制對某些資源的訪問,最常見的就是要求用戶先登錄才能訪問資源,當(dāng)用戶登錄后就會(huì)將此次會(huì)話信息保存進(jìn)session,同時(shí)返回給瀏覽器指定的cookie鍵值,下次瀏覽器再次訪問,請求頭中就會(huì)攜帶這個(gè)cookie,我們也以次來識別用戶的登錄狀態(tài),做出正確響應(yīng)。

問題

有時(shí)候,我們先行登錄,然后訪問服務(wù)A的某個(gè)方法,請求頭中攜帶cookie,標(biāo)識我們已經(jīng)登錄。

但若是我們訪問的目標(biāo)方法在執(zhí)行過程中使用feign進(jìn)行遠(yuǎn)程調(diào)用服務(wù)B,而服務(wù)B也要先判斷登錄狀態(tài),我們可能發(fā)現(xiàn)服務(wù)B會(huì)調(diào)用失敗,或者說拿不到數(shù)據(jù),理由是服務(wù)B認(rèn)為我們并未登錄。

而這時(shí),如果我們直接從瀏覽器訪問服務(wù)B的這個(gè)方法卻能得到一個(gè)成功的響應(yīng)。

查看源碼

1、使用feign進(jìn)行遠(yuǎn)程調(diào)用時(shí),首先判斷目標(biāo)方法類型,如果是 toString(),hashCode(),equals()這幾個(gè)方法,那就是本地直接完成了

2、執(zhí)行真正的遠(yuǎn)程調(diào)用的方法

3、根據(jù)模板template來創(chuàng)建請求request(默認(rèn)的情況feign是不會(huì)幫我們把原請求頭參數(shù)復(fù)制到新請求的請求頭上的),然后進(jìn)行客戶端client調(diào)用

4、但是創(chuàng)建請求時(shí),會(huì)先調(diào)用所有的攔截器RequestInterceptors

5、所以我們可以自己向容器中注冊一個(gè)攔截器RequestInterceptor,在這個(gè)攔截器中重寫apply方法,在apply方法中把老請求的cookie復(fù)制到新request的請求頭中,完成請求頭的同步。(然后targetRequest 方法就會(huì)調(diào)用攔截器的apply方法,進(jìn)行返回)

總結(jié):

feign遠(yuǎn)程調(diào)用,自己創(chuàng)建一個(gè)新的request對象,按照指定的路徑和參數(shù)發(fā)起新的請求,并得到響應(yīng)結(jié)果。但是這個(gè)新的request對象請求頭為空,所以丟失了原先請求中的數(shù)據(jù)。

feign在創(chuàng)建新的request對象時(shí),會(huì)調(diào)用一系列容器中的RequestInterceptor對象,執(zhí)行其apply方法,對這個(gè)創(chuàng)建好的request進(jìn)行增強(qiáng),再去真正執(zhí)行請求。但是默認(rèn)情況下容器中不存在這類攔截器對象。

我們可以自己向容器中注冊一個(gè)RequestInterceptor,在其apply方法體內(nèi),獲取到原始request,將其數(shù)據(jù)取出,賦值到新的request中,完成請求頭的同步。RequestContextHolder借助ThreadLocal將每一個(gè)原始請求與tomcat為其分配的線程綁定,之后,只要在同個(gè)線程內(nèi),隨時(shí)隨地都可輕易獲取到原始request。而我們是在apply方法體內(nèi),通過 RequestContextHolder.getRequestAttributes() 獲取的。RequestContextHolder是借助ThreadLocal將每一個(gè)原始請求與tomcat為其分配的線程綁定,之后,只要在同個(gè)線程內(nèi),隨時(shí)隨地都可輕易獲取到原始request。

解決

1、同步時(shí)

直接向spring容器注入RequestInterceptor攔截器即可

/**
* Feign
* @return
*/
@Bean
public RequestInterceptor requestInterceptor(){
   return requestTemplate -> {
       //1、從RequestContextHolder獲取原始請求的請求數(shù)據(jù)(請求參數(shù)、請求頭等)
       ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
       HttpServletRequest request = attributes.getRequest();
       //2、同步請求頭數(shù)據(jù)
       Enumeration<String> headerNames = request.getHeaderNames();
       if (headerNames != null) {
           while (headerNames.hasMoreElements()) {
               String name = headerNames.nextElement();
               String values = request.getHeader(name);
               // 跳過 content-length,沒有跳過會(huì)導(dǎo)致請求參數(shù)無法接收
               if (HttpHeaderConsts.CONTENT_LENGTH.equalsIgnoreCase(name)) {
                   continue;
               }
               requestTemplate.header(name, values);
           }
       }
   };
}

2、異步時(shí)

如果請求中使用了異步,也就是多線程,就算配置了上面的配置也會(huì)導(dǎo)致feign請求頭丟失,因?yàn)檎埱箢^信息是通過threadLocal保存的,也就是只有在同一個(gè)線程中才能使用請求中的請求頭并且同步初始請求頭信息到feign請求中

解決辦法

在異步調(diào)用時(shí)主動(dòng)將請求頭覆蓋到異步線程的請求上下文中

// 通過請求上下文獲取請求信息
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
CompletableFuture<Void> addressFuture = CompletableFuture.runAsync(() -> {
    // 遠(yuǎn)程查詢所有的收貨地址列表
    // 在該線程中添加請求信息
    RequestContextHolder.setRequestAttributes(requestAttributes);
    List<MemberAddressVo> address = memberFeignService.getAddress(memberRespVo.getId());
    orderConfirmVo.setAdress(address);
}, executor);
?
CompletableFuture<Void> cartFuture = CompletableFuture.runAsync(() -> {
    // 遠(yuǎn)程查詢購物車所有選中的購物想
    // 在該線程中添加請求信息
    RequestContextHolder.setRequestAttributes(requestAttributes);
    List<OrderItemVo> currentUserItems = cartFeignService.getCurrentUserItems();
    orderConfirmVo.setItems(currentUserItems);
}, executor);

總結(jié)

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

最新評論

安徽省| 景东| 马龙县| 阳泉市| 新河县| 娄烦县| 湘潭市| 孝义市| 柘荣县| 嵊州市| 平乐县| 惠水县| 垫江县| 富顺县| 安乡县| 张家界市| 六安市| 沈阳市| 黄山市| 达拉特旗| 榆林市| 渑池县| 西畴县| 沐川县| 二连浩特市| 中超| 台东县| 普安县| 绥化市| 丰宁| 清河县| 深水埗区| 关岭| 龙胜| 宁德市| 恩平市| 西华县| 凤翔县| 莆田市| 德保县| 固始县|