Spring Boot中AbstractRoutingDataSource请求间数据源共享问题咨询
问题解答
为啥Spring Boot请求会共享ThreadLocal的值?
Spring Boot默认用Tomcat作为Web容器,而Tomcat是用线程池来处理请求的:
- 一个请求处理完后,线程不会被销毁,而是放回线程池等待下一个请求复用。
- ThreadLocal的变量是绑定在线程实例上的,不是绑定在请求上。如果上一个请求没清理ThreadLocal里的值,下一个请求复用这个线程时,就能直接读到之前遗留的数据源标识(比如你遇到的ABC)。
- 你碰到的1/5概率,本质是刚好命中了没清理ThreadLocal的复用线程的概率,这个概率和线程池大小、请求并发量直接相关。
必须清理ThreadLocal的原因
- 避免请求数据污染:后续请求会错误使用前一个请求的数据源,导致查询/操作错数据库,引发业务逻辑混乱。
- 防止内存泄漏:线程池里的线程是长期存活的,如果ThreadLocal绑定的对象一直没被清理,这些对象会一直被线程引用,无法被GC回收,积累多了会导致内存溢出。
- 符合线程复用的规范:线程池中的线程是共享资源,必须保证线程在处理新请求前是“干净状态”,不能遗留上一次请求的任何状态数据。
修复代码建议
一定要用try-finally包裹ThreadLocal的设置和清理逻辑,确保无论业务代码是否抛出异常,都会清理ThreadLocal:
public class ClientService { private ClientDao clientDao; @Autowired private ClientDatabaseContextHolder clientDatabaseContextHolder; public String getClientName(ClientDatabase clientDb) { try { clientDatabaseContextHolder.set(clientDb); // 设置当前请求的数据源标识 return this.clientDao.getClientName(); } finally { clientDatabaseContextHolder.clear(); // 强制清理,保证线程状态干净 } } }
内容的提问来源于stack exchange,提问作者tcg
相关产品推荐
相关产品推荐

