精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

SpringCloud微服務中Feign如何傳遞用戶Token,并保證多線程環境也可適用?

開發 架構
雖然可以在異步調用時設置 RequestContextHolder.setRequestAttributes(RequestContextHolder.getRequestAttributes(), true); 可以實現請求頭透傳,但是每次調用都需要加上這一句,實現上還略顯麻煩。

大家好,我是飄渺。

在上一篇文章中,我們解決了網關層認證后向后端服務傳遞用戶信息的問題。今天我們來解決另外一個問題:如何在 OpenFeign 中傳遞 Token,并且保證多線程情況下也能適用。

這是DDD&微服務系列文章的第34篇,歡迎持續關注!

為了方便演示,首先定義一個接口,在接口中通過 Feign 調用其他服務:

@Operation(summary = "用戶測試接口")  
@GetMapping("/api/pd/customer/info")  
public String info() {  
      
    String currentUser = UserContextHolder.getInstance().getCurrentUser();  
      
    log.info("feign調用方獲取當前登錄用戶:" + currentUser);  
    
 //通過feign調用遠程服務
    String info = experimentClient.info();  
  
    log.info("遠程獲取用戶:" + info);
    return currentUser;  
}

然后在遠程接口中通過上文定義的UserContextHolder對象獲取用戶信息:

@GetMapping("/api/pd/experiment/info")  
public String userInfo() {  
  
    String currentUser = UserContextHolder.getInstance().getCurrentUser();  
  
    log.info("feign被調用方獲取userToken : {} ",currentUser);  
  
    return currentUser == null ? "" : currentUser;  
}

圖片圖片

通過調用結果可知,當使用OpenFeign調用遠程服務時,接口是無法獲取到用戶 ID 的。

常規解決辦法

在使用OpenFeign請求其他服務接口時,默認不攜帶header信息,這樣就導致無法攜帶登錄用戶信息。常規情況下,我們只需要在使用 OpenFeign 調用時先從 Header 獲取 Token 信息,放入新請求即可,在項目中可以定義一個OpenFeign的攔截器來實現此功能,代碼如下所示:

public class FeignRequestConfiguration {  
  
    @Bean  
    public RequestInterceptor requestInterceptor(){  
        return new RequestInterceptor() {  
            @Override  
            public void apply(RequestTemplate template) {  
                RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();  
                ServletRequestAttributes attributes = (ServletRequestAttributes) requestAttributes;  
  
                // 當主線程的請求執行完畢后,Servlet容器會被銷毀當前的Servlet,因此在這里需要做判空  
                if (attributes != null) {  
                    HttpServletRequest request = attributes.getRequest();  
  
                    // 獲取userId 并傳遞 userId                    
                    String userId = request.getHeader(CommonConstant.X_CLIENT_TOKEN);  
                    if (StringUtils.hasText(userId)) {  
                        template.header(CommonConstant.X_CLIENT_TOKEN, userId);  
                    }  
                }  
            }  
        };  
    }  
}

經過上述配置以后再次調用即可在 Feign 接口中也獲取到用戶ID,如下圖所示:

圖片圖片

異步調用

上面是單線程的情況,假如我們在當前線程中又開啟了子線程去進行 Feign 調用,那么是無法從 RequestContextHolder 獲取到 Header 的。測試代碼如下:

public String info() {  
      
    String currentUser = UserContextHolder.getInstance().getCurrentUser();  
      
    log.info("feign調用方獲取當前登錄用戶:" + currentUser);  
  
    CompletableFuture<String> infoFuture = CompletableFuture.supplyAsync(experimentClient::info,executor);

    String info = "";  
    try{  
        info = infoFuture.get();  
    } catch (Exception e) {  
        e.printStackTrace();  
        throw new RuntimeException(e);  
    }  
  
    log.info("遠程獲取用戶:" + info);  
  
    return currentUser;  
}

在上述代碼中,通過 CompletableFuture 開啟異步線程去調用 experimentClient ,可以發現此時無法獲取到用戶信息,效果如下所示:

圖片圖片

出現上述問題的原因是,RequestContextHolder.getRequestAttributes() 方法里面使用的一個 ThreadLocal,默認不是線程共享的,源碼如下:

public static RequestAttributes getRequestAttributes() {  
    RequestAttributes attributes = requestAttributesHolder.get();  
    if (attributes == null) {  
       attributes = inheritableRequestAttributesHolder.get();  
    }  
    return attributes;  
}

所以主線程調用子線程時,無法獲取到主線程請求里面的 RequestAttributes。

解決辦法

原因已經清楚了,繼續觀察 RequestContextHolder.getRequestAttributes() 方法源碼,注意到如果當前線程拿不到 RequestAttributes ,它會從 inheritableRequestAttributesHolder 里面拿,再仔細觀察發現源碼設置 RequestAttributes 到 ThreadLocal 的時候有這樣一個重載方法。

/**
 * 給當前線程綁定屬性
 * @param inheritable 是否要將屬性暴露給子線程
 */
public static void setRequestAttributes(@Nullable RequestAttributes attributes, boolean inheritable) {  
    ......
}

這看起來符合我們的要求,只需要在主線程調用其他線程前將 RequestAttributes 對象設置為子線程共享,就能把 Header 等信息傳遞下去。

所以,在異步調用 Feign 接口時添加如下代碼即可:

RequestContextHolder.setRequestAttributes(RequestContextHolder.getRequestAttributes(),true);
CompletableFuture<String> infoFuture = CompletableFuture.supplyAsync(experimentClient::info,executor);
......

再次執行發現,是可以獲取到 userId 的。

這里使用CompletableFuture異步調用時需要使用自定義線程池,而不能使用默認線程池ForkJoinPool,這是為什么呢?

最佳解決方案

雖然可以在異步調用時設置 RequestContextHolder.setRequestAttributes(RequestContextHolder.getRequestAttributes(), true); 可以實現請求頭透傳,但是每次調用都需要加上這一句,實現上還略顯麻煩。

并且我們知道了獲取不到請求頭的原因是子線程無法獲取主線程的 header 屬性,那么我們只需要定義一個數據結構,使用 InheritableThreadLocal 在內存中保存一份 header 屬性即可。在上篇文章中通過網關進行 UserID 透傳時我們是使用 ThreadLocal 保存數據,現在只需要將其換成 InheritableThreadLocal,同時在 RequestInterceptor#apply() 方法中不再通過請求頭獲取而是直接從 InheritableThreadLocal 中獲取數據。

實現過程如下:

1、重命名并修改數據結構:

首先,將 UserContextHolder 重命名為 RequestHeaderHolder,同時使用 InheritableThreadLocal 替換 ThreadLocal,以便子線程也能獲取數據。

public class RequestHeaderHolder {
    private final ThreadLocal<Map<String,String>> REQUEST_HEADER_HOLDER;

    //使用InheritableThreadLocal,使得共享變量可被子線程繼承
    private RequestHeaderHolder() {
        this.REQUEST_HEADER_HOLDER = new InheritableThreadLocal<>() {
            @Override
            protected Map<String, String> initialValue() {
                return new HashMap<>();
            }
        };
    }
  
   public String getCurrentUser(){
        return this.REQUEST_HEADER_HOLDER.get().get(CommonConstant.X_CLIENT_TOKEN);
   }
  ......
}

2、修改請求攔截器:

將請求攔截器 UserTokenInterceptor 重命名為 RequestHeaderInterceptor,并將請求頭放入 RequestHeaderHolder 中。

@Slf4j
public class RequestHeaderInterceptor implements HandlerInterceptor {
    
    @Override
    public boolean preHandle(HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull Object handler) throws Exception {

        Enumeration<String> headerNames = request.getHeaderNames();
        RequestHeaderHolder requestHeaderHolder = RequestHeaderHolder.getInstance();

        //重新設置請求頭
        while (headerNames.hasMoreElements()){
            String key = headerNames.nextElement();
            requestHeaderHolder.set(key,request.getHeader(key));
        }
        return true;
    }
    
   ......
}

3、修改 Feign 配置類:在 FeignRequestConfiguration 中不再從 RequestContextHolder 獲取數據,而是從 RequestHeaderHolder 獲取數據。

@Slf4j
public class FeignRequestConfiguration {

    @Bean
    public RequestInterceptor requestInterceptor(){
        return template -> {
            Map<String, String> headerMap = RequestHeaderHolder.getInstance().get();
            if(headerMap != null){
                headerMap.forEach((key, value) -> {                   
                    template.header(key, value);
                });
            }
        };
    }
}

通過上面的改造,不管是同步調用還是子線程異步調用都可以直接通過RequestHeaderHolder.getInstance().getCurrentUser();獲取用戶信息,并且調用方無須做任何改動。

責任編輯:武曉燕 來源: JAVA日知錄
相關推薦

2024-03-06 08:36:36

2024-03-18 08:48:52

Spring多端認證微服務

2011-06-30 17:40:07

Linux 多線程 Android

2024-07-02 10:58:53

2011-06-30 17:31:32

Qt 多線程 信號

2022-09-06 10:29:27

無服務器Serverless

2010-02-01 17:18:23

Python多線程環境

2023-03-27 15:39:53

微服務架構REST

2023-12-14 08:01:47

環境復制微服務

2017-03-08 16:25:54

Linux多線程函數

2023-09-26 00:37:38

Spring微服務框架

2021-08-26 11:52:32

FeignWeb服務

2021-01-27 15:38:27

微服務架構IT

2025-10-09 01:22:00

2021-05-17 07:28:23

Spring可擴展性項目

2024-06-04 10:05:48

微服務網關日志

2023-01-26 02:07:51

HashSet線程安全

2024-06-17 00:02:00

線程安全HashMapJDK 1.7

2020-12-28 11:52:36

微服務數據中臺去中心化

2016-10-13 15:03:27

混合云多云環境微服務
點贊
收藏

51CTO技術棧公眾號

亚洲精品www久久久| 国产精品福利一区| 久久久人成影片一区二区三区| 一级做a爱视频| 日本动漫同人动漫在线观看| 成人性色生活片| 91国产高清在线| 高潮毛片无遮挡| 农村妇女一区二区| 亚洲一区视频在线| 欧美一区二视频在线免费观看| 亚洲日本视频在线观看| blacked蜜桃精品一区| 91精品国产综合久久精品性色 | gogo久久日韩裸体艺术| 亚洲777理论| 成人羞羞视频免费| 精品无码久久久久久久| 秋霞一区二区三区| 婷婷丁香久久五月婷婷| 三区精品视频观看| 成人乱码一区二区三区| 久久aⅴ乱码一区二区三区| 中文字幕亚洲综合久久| 亚洲欧美日韩色| 日韩精品影片| 一区二区三区国产| 日本一区视频在线播放| 亚洲第九十九页| 久热国产精品| 欧美高跟鞋交xxxxxhd| 成人手机在线免费视频| 亚洲天堂网站| 色婷婷亚洲综合| 日日噜噜噜夜夜爽爽| 免费观看成年人视频| 日本不卡免费在线视频| 韩剧1988免费观看全集| 亚洲女人毛茸茸高潮| 嫩草国产精品入口| 91精品国产入口| 九热视频在线观看| 18aaaa精品欧美大片h| 国产精品国产三级国产有无不卡| 久久精品丝袜高跟鞋| 国产男男gay体育生网站| 在线一区免费| 国产亚洲精品久久久久久牛牛 | 国产乱国产乱300精品| 欧美最猛黑人xxxx黑人猛叫黄| 最新一区二区三区| 精品九九在线| 亚洲开心激情网| av不卡中文字幕| 日本一道高清亚洲日美韩| 亚洲最新在线观看| 久久久成人精品一区二区三区 | 在线播放 亚洲| 91精彩视频在线播放| 国产亚洲欧美中文| 国产一区二区三区高清视频| 国产精品爽爽久久久久久| 秋霞午夜鲁丝一区二区老狼| 国产91免费看片| 国产无套丰满白嫩对白| 一本久道综合久久精品| 久久久久久九九九| 日本中文字幕免费在线观看| 午夜激情久久| www.欧美精品一二三区| 久久精品老司机| 美国一区二区| 欧美日韩另类一区| 天天综合网久久| 欧美在线一级| 欧美一区二区在线视频| 国产又黄又猛的视频| 欧美黑粗硬大| 欧美日韩国产免费| 男生操女生视频在线观看| 国产亚洲欧美日韩精品一区二区三区 | 亚洲伊人久久综合| 国产成人精品白浆久久69| 国产一区二区三区蝌蚪| 3d动漫精品啪啪一区二区三区免费| 国产成a人亚洲精v品无码| 国产成人精品免费| 国产精品视频福利| 国内爆初菊对白视频| jlzzjlzz国产精品久久| 麻豆成人av| 国产精品免费观看| 中文字幕精品一区二区精品绿巨人| 亚欧洲精品在线视频免费观看| shkd中文字幕久久在线观看| 国产精品福利一区二区三区| 亚洲春色在线视频| 337p日本欧洲亚洲大胆鲁鲁| 亚洲精品欧美专区| 缅甸午夜性猛交xxxx| 在线成人av观看| 天天综合色天天综合色h| 女人和拘做爰正片视频| 日韩一区二区三区在线免费观看| 欧美日韩国产bt| 日本黄色大片在线观看| 久久香蕉精品香蕉| 国产亚洲一区二区在线| 日日噜噜夜夜狠狠久久波多野| 欧美日韩爆操| 日本人成精品视频在线| 艳妇乳肉豪妇荡乳av| 国产一区二区不卡| 久久久av水蜜桃| 自拍视频在线网| 亚洲韩国一区二区三区| 爱情岛论坛成人| 在线播放一区二区精品视频| 日韩精品视频在线观看免费| 亚洲成人黄色av| 亚洲网址在线| 国产精品视频一区国模私拍| www.五月激情| 亚洲国产精品精华液2区45| 国产美女作爱全过程免费视频| 成人影院网站| 日韩精品一区二区三区蜜臀 | 黄在线观看免费网站ktv| 欧美伊人久久久久久久久影院| 性高潮久久久久久| 丝袜av一区| 麻豆国产va免费精品高清在线| 波多野结衣视频网站| 国产精品羞羞答答xxdd| 水蜜桃一区二区| bbw在线视频| 91精品午夜视频| 午夜在线观看一区| 尤物精品在线| 成人av在线亚洲| 少妇精品视频一区二区| 亚洲精品写真福利| 久久久久xxxx| 日韩大片在线| 97超级碰碰人国产在线观看| www.蜜臀av| 亚洲日本在线视频观看| 日日噜噜夜夜狠狠| 亚洲桃色综合影院| 91精品国产网站| 亚洲免费黄色片| 亚洲国产精品国自产拍av| 美女日批免费视频| 成人高潮视频| 久久久久久久久久久免费精品| 91 中文字幕| 国产亚洲欧洲一区高清在线观看| 欧美日韩亚洲一| 国内自拍欧美| 隔壁老王国产在线精品| 一起草av在线| 中国色在线观看另类| 国产精品igao| 成人网18免费网站| 国产精品一香蕉国产线看观看 | 天天舔天天操天天干| 麻豆久久精品| 日韩精品一区二区三区外面| 欧美性suv| 亚洲人午夜精品免费| 波多野结衣啪啪| www.亚洲在线| 久久久天堂国产精品| 动漫一区二区三区| 欧美日本在线视频中文字字幕| 国产偷拍一区二区| 一区二区成人在线| 日本一区二区在线观看视频| 禁久久精品乱码| 国产精品有限公司| 新版中文在线官网| 欧美不卡视频一区| 日产亚洲一区二区三区| k8久久久一区二区三区| 日韩在线xxx| 热久久天天拍国产| 91久久精品在线| 欧美人动性xxxxz0oz| 亚洲高清不卡av| 日韩 国产 欧美| 欧美国产禁国产网站cc| 天堂av在线网站| 亚洲乱码免费伦视频| 99国精产品一二二线| 菠萝蜜视频在线观看www入口| 日韩电影在线观看中文字幕| 成人公开免费视频| 中文字幕在线不卡| 91人妻一区二区| 免费在线欧美黄色| 欧美lavv| 亚洲精品国产嫩草在线观看| 久久久www成人免费精品| 成人免费公开视频| 日本电影亚洲天堂一区| 日韩激情小视频| 国产高清亚洲一区| 男人添女人下部高潮视频在观看| 中文字幕伦av一区二区邻居| 91久久久久久久久久久| 嗯~啊~轻一点视频日本在线观看| 亚洲精品一区中文| 国产精品毛片久久久久久久av| 亚洲五月六月丁香激情| 黄色国产在线播放| 成人aa视频在线观看| 亚洲成人av免费看| 狠狠干综合网| 亚洲国产欧美日韩| 欧美一级全黄| 91欧美激情另类亚洲| 免费高潮视频95在线观看网站| 亚洲欧美一区二区三区四区 | 男人的天堂官网| 国产精品白丝av| 少妇高清精品毛片在线视频| 欧美日韩一卡| 日本一区二区视频| 97久久综合区小说区图片区| 国产精品一区二区三区在线播放| 成人在线黄色电影| 久久国产精品久久国产精品| 免费观看成年在线视频网站| 欧美大肚乱孕交hd孕妇| 亚洲熟女乱色一区二区三区久久久| 亚洲成年人网站在线观看| 午夜精品一区二区三级视频| 国产亚洲一区二区三区在线观看| zjzjzjzjzj亚洲女人| 丝袜美腿亚洲色图| 咪咪色在线视频| 欧美三级三级| 狼狼综合久久久久综合网| 亚洲国产高清在线观看| 国产在线a不卡| 播放一区二区| 国产91免费看片| 韩国美女久久| 欧美一级bbbbb性bbbb喷潮片| 欧美黑人激情| 一色桃子一区二区| 亚洲日本中文字幕在线| 欧美精品一区二区三区一线天视频| 国产精品视频一二区| 欧美精品日日鲁夜夜添| 中文字幕777| 在线免费不卡视频| 国产午夜麻豆影院在线观看| 婷婷开心久久网| 欧美亚洲日本在线| 亚洲精品综合在线| a级片在线观看免费| 中文字幕在线视频一区| 男人的午夜天堂| 亚洲视频一区二区在线| 欧洲美女女同性互添| 国产精品久久777777| 粉嫩精品久久99综合一区| 99久久精品99国产精品| 岛国av免费观看| eeuss影院一区二区三区| 欧美日韩一区二区三区四区五区六区| 国产iv一区二区三区| 老司机午夜免费福利| 91玉足脚交白嫩脚丫在线播放| 欧美一区二区免费在线观看| 99v久久综合狠狠综合久久| 欧美深性狂猛ⅹxxx深喉| 91色婷婷久久久久合中文| v8888av| 国产精品美女视频| 午夜剧场免费在线观看| 一区二区三区美女| 久久久久久久久久影院| 在线一区二区三区做爰视频网站| 一级aaaa毛片| 欧美一区二区久久| 欧美一级视频免费| 亚洲精品自拍视频| 95在线视频| 欧美xxxx18国产| av电影在线地址| 热99在线视频| 日本一区二区三区中文字幕| 91在线免费看网站| 免费成人三级| 亚洲二区自拍| 欧美精品午夜| 亚洲激情自拍图| 五月天丁香综合久久国产| 国产精品无码久久久久一区二区| 韩国一区二区三区四区| 一级特黄aaa大片在线观看| 国产成人精品网址| 亚洲精品无码一区二区| 91丨porny丨在线| 国产7777777| 国产精品卡一卡二卡三| 欧美日韩国产精品一区二区三区| 黄色一区二区在线观看| 中文字幕免费在线看| 日韩一区二区免费在线电影| 午夜视频福利在线| 日韩最新在线视频| 91破解版在线观看| 国产成人精品一区二区三区| 视频一区国产| 日韩av电影免费在线| 欧美xxx在线观看| 国产精品人人妻人人爽人人牛| 久久精品国产成人一区二区三区| 小毛片在线观看| 久久久久久久久伊人| 国产一区二区三区视频播放| 亚洲成人免费视| 国产精品久久无码一三区| 日韩精品中文字幕久久臀| 免费在线观看av| 国产91色在线|免| 精品国产亚洲一区二区三区在线| 国产成人免费观看| 日韩在线精品| www黄色av| a级高清视频欧美日韩| 日韩av手机在线免费观看| 色爱区综合激月婷婷| 成人午夜免费在线观看| 日韩视频精品在线| 丝袜老师在线| 岛国视频一区| 一区二区中文字| 中文字幕第100页| 久久综合久久久久88| 成人免费看片98| 日韩手机在线导航| 成人在线播放免费观看| 91九色国产视频| 在线中文字幕亚洲| 台湾佬美性中文| 亚洲制服丝袜av| 黄色av网址在线| 国外成人在线视频| 激情视频极品美女日韩| 99久久久精品视频| 国产69精品久久久久777| 国产亚洲精品成人| 亚洲第一区第二区| 黄色18在线观看| 免费影院在线观看一区| 久久国产日本精品| 毛片aaaaaa| 欧美日本不卡视频| 国产精品一区二区三区视频网站| 91亚洲va在线va天堂va国 | 精品国产一区二区三区免费| 亚洲久久视频| 实拍女处破www免费看| 欧美午夜寂寞影院| 久草中文在线| 国产成人成网站在线播放青青| 国产情侣久久| 欧美丰满美乳xxⅹ高潮www| 777午夜精品视频在线播放| 欧美人与禽性xxxxx杂性| 精品久久蜜桃| 日本欧美韩国一区三区| 永久免费看mv网站入口| 亚洲精品一区在线观看| 日韩天堂在线| 色哺乳xxxxhd奶水米仓惠香| 不卡一区二区三区四区| 69xxxx国产| 欧美大尺度激情区在线播放| 国语一区二区三区| 中文字幕视频在线免费观看| 亚洲免费电影在线| 手机看片一区二区| 国产精品久久久久久久久久久新郎 | 国产桃色电影在线播放| 欧美一区少妇| 国产一区二区三区高清播放| 91午夜视频在线观看| 日韩资源在线观看| 国产精品香蕉| 男生操女生视频在线观看 | 99久久999| 欧美v在线观看| 亚洲色图清纯唯美| 手机看片福利在线观看| 国产日韩视频在线观看|