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

解決SpringCloud?Feign異步調用傳參問題

 更新時間:2022年05月26日 10:45:11   作者:半島鐵板  
這篇文章主要介紹了SpringCloud?Feign異步調用傳參問題,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下

背景

各個子系統(tǒng)之間通過feign調用,每個服務提供方需要驗證每個請求header里的token。

public void invokeFeign() throws Exception {
    feignService1.method();
    feignService2.method();
    feignService3.method();
....
}

定義攔截每次發(fā)送feign調用攔截器RequestInterceptor的子類,每次發(fā)送feign請求前將token帶入請求頭

@Configuration
public class FeignTokenInterceptor implements RequestInterceptor {
    @Override
    public void apply(RequestTemplate template) {
        public void apply(RequestTemplate template) {
            //上下文環(huán)境保持器,拿到剛進來這個請求包含的數(shù)據(jù),而不會因為遠程數(shù)據(jù)請求頭被清除
            ServletRequestAttributes attributes = (ServletRequestAttributes)                  RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();//老的請求
            if (request != null) {
                //同步老的請求頭中的數(shù)據(jù),這里是獲取cookie
                String cookie = request.getHeader("token");
                template.header("token", cookie);
            }
        }
  .....
    }

這樣便能實現(xiàn)系統(tǒng)間通過同步方式feign調用的認證問題。但是如果需要在invokeFeign方法中feignService3的方法調用比較耗時,并且invokeFeign業(yè)務并不關心feignService3.method()方法的執(zhí)行結果,此時該怎么辦。

方案1:

修改feignService3.method()方法,將其內部實現(xiàn)修改為異步,這種方案依賴服務的提供方,如果feignService3服務是其他業(yè)務部門維護,并且無法修改實現(xiàn)為異步,此時只能采取方案2.

方案2:

通過線程池調用feignServie3.method()

public void invokeFeign() throws Exception {
    feignService1.method();
    feignService2.method();
    executor.submit(()->{
        feignService3.method();
    });
....
}

懷著期待的心情開啟了嘗試,你會發(fā)現(xiàn)調用feignService3方法并沒有成功,查看日志你將會發(fā)現(xiàn)是由于feign發(fā)送request請求的header中未攜帶token導致。于是百度了下feign異步調用傳參,網(wǎng)上大部分的解決方案,如下

public void invokeFeign() throws Exception {
        feignService1.method();
        feignService2.method();
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder
                .getRequestAttributes();
        executor.submit(()->{
            RequestContextHolder.setRequestAttributes(RequestContextHolder.getRequestAttributes(), true);
            feignService3.method();
        });
    }
}

添加了上面的代碼后,實測無效,此時確實有些束手無策。但是真的沒無效嗎?我仔細比對通過上述手段解決問題的博客,他們的業(yè)務代碼和我的代碼不同之處。確實有不同,比如http://m.fzitv.net/article/249407.htm這篇。其代碼如下

@Override
public OrderConfirmVo confirmOrder() throws ExecutionException, InterruptedException {
    OrderConfirmVo confirmVo = new OrderConfirmVo();
    MemberResVo memberResVo = LoginUserInterceptor.loginUser.get();
    //從主線程中獲得所有request數(shù)據(jù)
    RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
    CompletableFuture<Void> getAddressFuture = CompletableFuture.runAsync(() -> {
        //1、遠程查詢所有地址列表
        RequestContextHolder.setRequestAttributes(requestAttributes);
        List<MemberAddressVo> address = memberFeignService.getAddress(memberResVo.getId());
        confirmVo.setAddress(address);
    }, executor);

    //2、遠程查詢購物車所選的購物項,獲得所有購物項數(shù)據(jù)
    CompletableFuture<Void> cartFuture = CompletableFuture.runAsync(() -> {
        //放入子線程中request數(shù)據(jù)
        RequestContextHolder.setRequestAttributes(requestAttributes);
        List<OrderItemVo> items = cartFeginService.getCurrentUserCartItems();
        confirmVo.setItem(items);
    }, executor).thenRunAsync(()->{
        RequestContextHolder.setRequestAttributes(requestAttributes);
        List<OrderItemVo> items = confirmVo.getItem();
        List<Long> collect = items.stream().map(item -> item.getSkuId()).collect(Collectors.toList());
        //遠程調用查詢是否有庫存
        R hasStock = wmsFeignService.getSkusHasStock(collect);
        //形成一個List集合,獲取所有物品是否有貨的情況
        List<SkuStockVo> data = hasStock.getData(new TypeReference<List<SkuStockVo>>() {
        });
        if (data!=null){
            //收集起來,Map<Long,Boolean> stocks;
            Map<Long, Boolean> map = data.stream().collect(Collectors.toMap(SkuStockVo::getSkuId, SkuStockVo::getHasStock));
            confirmVo.setStocks(map);
        }
    },executor);
    //feign遠程調用在調用之前會調用很多攔截器,因此遠程調用會丟失很多請求頭

    //3、查詢用戶積分
    Integer integration = memberResVo.getIntegration();
    confirmVo.setIntegration(integration);
    //其他數(shù)據(jù)自動計算

    CompletableFuture.allOf(getAddressFuture,cartFuture).get();
    return confirmVo;
}

我們看的出來,他的業(yè)務代碼即使是開啟多線程,也是等最后線程里的任務都執(zhí)行完成后,業(yè)務方法才結束返回,而我的業(yè)務方法并不會等feignService3調用完成結束,抱著嘗試的心態(tài),我調整了下代碼添加了CountDownLatch,讓業(yè)務方法等待feign調用結束后在返回。

public void invokeFeign() throws Exception {
        feignService1.method();
        feignService2.method();
        CountDownLatch latch = new CountDownLatch(1);
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder
                .getRequestAttributes();
        executor.submit(()->{
            RequestContextHolder.setRequestAttributes(RequestContextHolder.getRequestAttributes(), true);
            feignService3.method();
            latch.countDown();
        });
        latch.await();
    }
}

不如所料,調用成功了。到這里看似是解決了問題,但是與我想象的異步差別太大了,最終業(yè)務線程還是需要等待feignService3.method()調用業(yè)務方法才能返回,而且異步場景如發(fā)送短信、消息推送,記錄日志可能調用耗時,業(yè)務方法可不想等待他們執(zhí)行結束,此時該怎么解決?只能翻源碼ServletRequestAttributes.java

首先看到了注釋,這給了我靈感

Servlet-based implementation of the {@link RequestAttributes} interface. <p>Accesses objects from servlet request and HTTP session scope,
with no distinction between "session" and "global session".

servlet請求和HTTP會話范圍訪問對象,"session"和"global session"作用域沒有區(qū)別。對呀會不會是因為header中的參數(shù)是request作用域的原因呢,因為請求結束,所以即使在子線程設置請求頭,也取不到原因?;氐秸埱髷r截器RequestInterceptor查看獲取token地方

ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
    //老的請求
    HttpServletRequest request = attributes.getRequest();
if (request != null) {
        //同步老的請求頭中的數(shù)據(jù),這里是獲取cookie
        String cookie = request.getHeader("token");
        template.header("token", cookie);
        }

果然如此,從attributes中獲取request,然后從request中獲取token。但是沒有考慮到request請求結束,request作用域的問題,此時肯定取不到header里的token了。

那么該怎么解決呢?思路不能變,肯定還是圍繞著ServletRequestAttributes展開,發(fā)現(xiàn)他有兩個方法getAttributessetAttribute,而且這倆方法都支持兩個作用域request、session。

@Override
public Object getAttribute(String name, int scope) {
    if (scope == SCOPE_REQUEST) {
        if (!isRequestActive()) {
            throw new IllegalStateException(
                    "Cannot ask for request attribute - request is not active anymore!");
        }
        return this.request.getAttribute(name);
    }
    else {
        HttpSession session = getSession(false);
        if (session != null) {
            try {
                Object value = session.getAttribute(name);
                if (value != null) {
                    this.sessionAttributesToUpdate.put(name, value);
                }
                return value;
            }
            catch (IllegalStateException ex) {
                // Session invalidated - shouldn't usually happen.
            }
        }
        return null;
    }
}

@Override
public void setAttribute(String name, Object value, int scope) {
    if (scope == SCOPE_REQUEST) {
        if (!isRequestActive()) {
            throw new IllegalStateException(
                    "Cannot set request attribute - request is not active anymore!");
        }
        this.request.setAttribute(name, value);
    }
    else {
        HttpSession session = obtainSession();
        this.sessionAttributesToUpdate.remove(name);
        session.setAttribute(name, value);
    }
}

既然我們的業(yè)務方法調用(HttpServletRequest)不會等待feignService3.method,我們可以通過ServletRequestAttributes.setAttributes指定作用域為session呀。此時invokeFeign代碼如下

public void invokeFeign() throws Exception {
        feignService1.method();
        feignService2.method();
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder
                .getRequestAttributes();
        //在ServeletRequestAttributes中設置token,作用域為session                 
        attributes.setAttribute("token",attributes.getRequest().getHeader("token"),1);
        executor.submit(()->{
            RequestContextHolder.setRequestAttributes(RequestContextHolder.getRequestAttributes(), true);
            feignService3.method();
        });
    }
}

然后RequestInterceptor.apply方法也做響應調整,如下

ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
    //老的請求
    HttpServletRequest request = attributes.getRequest();
    String token = (String) attributes.getAttribute("token",1);
template.header("token",token);
        if (request != null) {
        //同步老的請求頭中的數(shù)據(jù),這里是獲取cookie
        String cookie = request.getHeader("token");
        template.header("token", cookie);
        }

問題得以圓滿解決。

到此這篇關于SpringCloud Feign異步調用傳參問題的文章就介紹到這了,更多相關SpringCloud Feign傳參內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Java實例講解多態(tài)數(shù)組的使用

    Java實例講解多態(tài)數(shù)組的使用

    本文章向大家介紹Java多態(tài)數(shù)組,主要包括Java多態(tài)數(shù)組使用實例、基本知識點總結和需要注意事項,具有一定的參考價值,需要的朋友可以參考一下
    2022-05-05
  • Spring Bean的Scope作用域詳解

    Spring Bean的Scope作用域詳解

    本文介紹了Spring框架中的BeanScope(作用域),包括Singleton(單例)和Prototype(原型)兩種常見作用域的定義、生命周期和適用場景
    2025-01-01
  • SpringCloud Feign遠程調用實現(xiàn)詳解

    SpringCloud Feign遠程調用實現(xiàn)詳解

    Feign是Netflix公司開發(fā)的一個聲明式的REST調用客戶端; Ribbon負載均衡、 Hystrⅸ服務熔斷是我們Spring Cloud中進行微服務開發(fā)非?;A的組件,在使用的過程中我們也發(fā)現(xiàn)它們一般都是同時出現(xiàn)的,而且配置也都非常相似
    2022-11-11
  • java中使用xls格式化xml的實例

    java中使用xls格式化xml的實例

    這篇文章主要介紹了java中調用xls格式化xml的實例的相關資料,需要的朋友可以參考下
    2017-07-07
  • 使用Springboot注入帶參數(shù)的構造函數(shù)實例

    使用Springboot注入帶參數(shù)的構造函數(shù)實例

    這篇文章主要介紹了使用Springboot注入帶參數(shù)的構造函數(shù)實例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-04-04
  • 解決RestTemplate反序列化嵌套對象的問題

    解決RestTemplate反序列化嵌套對象的問題

    這篇文章主要介紹了解決RestTemplate反序列化嵌套對象的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-11-11
  • HashMap線程不安全問題解析

    HashMap線程不安全問題解析

    這篇文章主要介紹了HashMap線程不安全問題解析,HashMap的線程不安全體現(xiàn)在會造成死循環(huán)、數(shù)據(jù)丟失、數(shù)據(jù)覆蓋等問題,其中死循環(huán)和數(shù)據(jù)丟失是在JDK1.7中出現(xiàn)的問題,在JDK1.8中已經(jīng)得到解決,但是1.8中仍會有數(shù)據(jù)覆蓋這樣的問題,需要的朋友可以參考下
    2023-11-11
  • 詳解SpringBoot定時任務功能

    詳解SpringBoot定時任務功能

    這篇文章主要介紹了SpringBoot定時任務功能詳細解析,這次的功能開發(fā)過程中也算是對其內涵的進一步了解,以后遇到定時任務的處理也更清晰,更有效率了,對SpringBoot定時任務相關知識感興趣的朋友一起看看吧
    2022-05-05
  • Spring Boot 接口參數(shù)加密解密的實現(xiàn)方法

    Spring Boot 接口參數(shù)加密解密的實現(xiàn)方法

    這篇文章主要介紹了Spring Boot 接口參數(shù)加密解密的實現(xiàn)方法,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-03-03
  • 詳解java倒計時三種簡單實現(xiàn)方式

    詳解java倒計時三種簡單實現(xiàn)方式

    這篇文章主要介紹了詳解java倒計時三種簡單實現(xiàn)方式,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-09-09

最新評論

遵化市| 济源市| 青河县| 海伦市| 安陆市| 镇坪县| 宁城县| 宜丰县| 黄冈市| 永丰县| 大冶市| 鄂尔多斯市| 天镇县| 大理市| 海阳市| 淮滨县| 云浮市| 黄陵县| 宜兰市| 河西区| 观塘区| 濉溪县| 潜山县| 改则县| 柘城县| 葵青区| 长汀县| 宁城县| 平远县| 新晃| 江达县| 广汉市| 揭东县| 乌恰县| 靖西县| 霍邱县| 九寨沟县| 广东省| 双城市| 尉犁县| 襄汾县|