在使用MyBatis-Plus多租戶插件時遇到一個問題,同樣一個請求查詢,有時不會自動拼接租戶條件進行查詢,可能連續(xù)發(fā)送幾次有一次會是這樣,經(jīng)過對TenantLineInnerInterceptor類的調(diào)試跟蹤發(fā)現(xiàn)了問題所在。
一、問題跟蹤
問題就出現(xiàn)在這,在查詢之前會進入beforeQuery,在這里InterceptorIgnoreHelper.willIgnoreTenantLine(ms.getId())表示是否會忽略租戶條件,如果為true就表示不添加租戶條件。

最終進入這個willIgnore函數(shù),如果ignoreStrategy不為空就會進入斷點部分代碼,導(dǎo)致返回為true,從而忽略多租戶條件。

而ignoreStrategy是由當(dāng)前線程變量IGNORE_STRATEGY_LOCAL中獲取得到,而設(shè)置這個變量的只有handle方法,所以是調(diào)用了handle方法導(dǎo)致。

但實質(zhì)上IGNORE_STRATEGY_LOCAL是一個ThreadLocal對象,應(yīng)該只在當(dāng)前線程有效,而且我當(dāng)前查詢并沒有調(diào)用handle函數(shù),倒是有其它請求有調(diào)用這個方法,所以只有一種可能,線程污染導(dǎo)致。
至于兩個不同的請求為什么會線程污染,主要跟我項目依賴有關(guān),我使用的spring是采用tomcat作為web服務(wù)器,tomcat對請求的處理采用的是線程池的方式,而不是每個請求都建立一個線程,所以如果在A請求中調(diào)用了handle方法,但又沒清理,它就會一直留在線程當(dāng)中,當(dāng)下次某一個請求B進入,tomcat剛好將那個線程分配給這個請求就會出現(xiàn)這種情況。
二、解決方法
解決方法就是在調(diào)用handle函數(shù)后記得調(diào)用clearIgnoreStrategy進行清理。
InterceptorIgnoreHelper.handle(IgnoreStrategy.builder().tenantLine(true).build());
try {
// 業(yè)務(wù)處理
} catch (Exception e) {
// 清理線程
InterceptorIgnoreHelper.clearIgnoreStrategy();
}
三、問題聯(lián)想
我這里使用的是spring boot3,默認(rèn)引入的就是tomcat6,采用的請求線程池是nio形式,在平常的業(yè)務(wù)中難免會使用ThreadLocal進行業(yè)務(wù)處理,最好在全局進行清理以防萬一:
/**
* 全局ThreadLocal清理過濾器
* @author 劉靖
*/
public class ThreadLocalCleanupFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
try {
// 繼續(xù)執(zhí)行請求
chain.doFilter(request, response);
} finally {
// 清理 ThreadLocal 內(nèi)容
cleanupThreadLocal();
}
}
/**
* 清理 ThreadLocal 中的內(nèi)容
*/
private void cleanupThreadLocal() {
// 這里清除所有 ThreadLocal 中的內(nèi)容
TenantThreadLocal.clear();
PermissionThreadLocal.cleanPermission();
DataScopeThreadLocal.clean();
}
}
但由于tomcat6使用的請求線程池是nio,所以如果在一個請求處理過程中有文件之類的io阻塞操作,線程也可能會切換,所以如果一個請求中既有數(shù)據(jù)庫操作或者是依賴ThreadLocal的操作,盡量把io阻塞的操作放到最后。