使用 transmittable-thread-local 組件解決 ThreadLocal 父子線程數據傳遞問題

在某個項目中,需要使用mybatis-plus多租戶功能以便數據隔離,前端將租戶id傳到後端,後端通過攔截器將該租戶id設置到ThreadLocal以便後續使用,代碼大體上如下所示:

ThreadLocal<Integer> threadLocal = new InheritableThreadLocal<>();
threadLocal.set(1);

我在Controller層使用線程池取了租戶id,代碼大體上如下所示:

ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.execute(()->{
	//獲取租戶id
});

這時候出問題了,出現了有時候取得到有時候取不到租戶id的現象,但是經過若干次重試之後就能穩定獲取到租戶id;再次測試則發現如果前端傳了其它的租戶id,後端取得還是上一次獲取到的租戶id,這到底是爲啥呢?

問題分析:首先,這裏使用了InheritableThreadLocal爲的就是實現父子線程傳值,傳了值也能取到,但是也不總是能取到,若干次之後就總是能取到了。看到這種現象,我們正常人的第一反應就是懷疑這裏有緩存,每次使用的時候沒有,使用完了就緩存起來,由於線程池在執行任務的時候並非總是使用同一條線程,當線程池中的核心線程全都緩存完了,再請求就穩定不報錯了,然而有緩存的原因所以就算這時候外部請求換了一個租戶id,線程池中的線程仍然使用的是老的租戶id,這也是緩存最直接的體現。。。。。。這裏純屬基於現象的個人猜測,並沒有什麼實錘,看官們謹慎駕駛,小心翻車。

那怎麼解決該問題呢?

該問題產生的原因是InheritableThreadLocal的bug,至於什麼bug,我也不清楚(笑),但是有解決方案,解決方案就是使用阿里的transmittable-thread-local 組件,github地址如下:https://github.com/alibaba/transmittable-thread-local 使用起來也非常簡單

首先,引入maven依賴:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>transmittable-thread-local</artifactId>
    <version>2.12.0</version>
</dependency>

1. 改變ThreadLocal的創建方式

TransmittableThreadLocal<String> context = new TransmittableThreadLocal<>();

// =====================================================

// 在父線程中設置
context.set("value-set-in-parent");

// =====================================================

// 在子線程中可以讀取,值是"value-set-in-parent"
String value = context.get();

2.改變線程池創建方式

ExecutorService executorService = ...
// 額外的處理,生成修飾了的對象executorService
executorService = TtlExecutors.getTtlExecutorService(executorService);

也就是說除了正常創建線程池之外,還要對該線程池做一個代理。

就這麼簡單,搞完之後父子線程傳數據就一切正常了。

ps. 個人覺得這裏稱呼"父子線程"並不妥當,因爲線程池是系統啓動之後就已經創建好了的,算了,鑽牛角尖太沒勁了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章