在使用MyBatis-Plus多租户插件时遇到一个问题,同样一个请求查询,有时不会自动拼接租户条件进行查询,可能连续发送几次有一次会是这样,经过对TenantLineInnerInterceptor类的调试跟踪发现了问题所在。
一、问题跟踪
问题就出现在这,在查询之前会进入beforeQuery
,在这里InterceptorIgnoreHelper.willIgnoreTenantLine(ms.getId())
表示是否会忽略租户条件,如果为true就表示不添加租户条件。
最终进入这个willIgnore
函数,如果ignoreStrategy
不为空就会进入断点部分代码,导致返回为true,从而忽略多租户条件。
而ignoreStrategy
是由当前线程变量IGNORE_STRATEGY_LOCAL
中获取得到,而设置这个变量的只有handle
方法,所以是调用了handle
方法导致。
但实质上IGNORE_STRATEGY_LOCAL
是一个ThreadLocal
对象,应该只在当前线程有效,而且我当前查询并没有调用handle
函数,倒是有其它请求有调用这个方法,所以只有一种可能,线程污染导致。
至于两个不同的请求为什么会线程污染,主要跟我项目依赖有关,我使用的spring是采用tomcat作为web服务器,tomcat对请求的处理采用的是线程池的方式,而不是每个请求都建立一个线程,所以如果在A请求中调用了handle
方法,但又没清理,它就会一直留在线程当中,当下次某一个请求B进入,tomcat刚好将那个线程分配给这个请求就会出现这种情况。
二、解决方法
解决方法就是在调用handle
函数后记得调用clearIgnoreStrategy
进行清理。
1 | InterceptorIgnoreHelper.handle(IgnoreStrategy.builder().tenantLine(true).build()); |
三、问题联想
我这里使用的是spring boot3,默认引入的就是tomcat6,采用的请求线程池是nio
形式,在平常的业务中难免会使用ThreadLocal
进行业务处理,最好在全局进行清理以防万一:
1 | /** |
但由于tomcat6使用的请求线程池是nio
,所以如果在一个请求处理过程中有文件之类的io阻塞操作,线程也可能会切换,所以如果一个请求中既有数据库操作或者是依赖ThreadLocal
的操作,尽量把io阻塞的操作放到最后。