通过关闭OpenEntityManagerInViewInterceptor确保当下游服务响应缓慢时不会导致数据库连接被占用完
OpenEntityManagerInViewInterceptor是spring中的一个拦截器,它的作用是在整个请求上下文复用同一个EntityManager
,从而避免在一个请求中多次创建一个EntityManager, 从而提高性能.
但是如果在一个请求的处理代码中请求了下游服务,而下游服务响应缓慢,那么在下游服务响应之前,EntityManager
会一直被占用,直到整个请求处理完成,这样会导致当服务并发请求量较大时,数据库连接被占用完.
为了避免这种情况,可以通过关闭OpenEntityManagerInViewInterceptor来确保当下游服务响应缓慢时不会导致数据库连接被占用完.
下面将介绍如何关闭OpenEntityManagerInViewInterceptor.
解决方案
在spring-boot中,可以通过配置spring.jpa.open-in-view为false来关闭OpenEntityManagerInViewInterceptor,示例如下
spring:
jpa:
open-in-view: false
备注
-
org.springframework.orm.hibernate5.support.OpenSessionInViewInterceptor在请求开始时会创建一个EntityManager并调用org.springframework.transaction.support.TransactionSynchronizationManager.bindResource方法来绑定EntityManager到当前线程 -
后续框架内部的调用例如
Repository.findAll()的大致调用链路如下- Repository.findAll()
- org.springframework.orm.jpa.SharedEntityManagerCreator.SharedEntityManagerInvocationHandler.invoke
- org.springframework.orm.jpa.EntityManagerFactoryUtils.doGetTransactionalEntityManager
- org.springframework.transaction.support.TransactionSynchronizationManager.getResource
因为在请求开始时已经将EntityManager绑定到当前线程,所以这里会获取到之前创建的EntityManager
-
后续执行
query时会从DataSource中获取一个数据库连接,然后执行查询,查询完成后,数据库连接不会立即关闭, 而是在EntityManager被关闭时关闭,而EntityManager会在OpenEntityManagerInViewInterceptor被关闭时关闭.
综上所述,当OpenEntityManagerInViewInterceptor被关闭时,EntityManager会被关闭,而EntityManager
被关闭时其对应的数据库连接也会被关闭.
EntityManager被创建出来时不会立即创建数据库连接,而是在执行sql时才会创建数据库连接.
如果整个请求链路非常耗时,那么在整个请求链路中,EntityManager对应的数据库连接都会被占用,直到整个请求链路执行完成.
如果同时有多个请求正在执行, 那么会导致数据库连接被占用完. 从而导致后续请求无法获取数据库连接.
为了避免这种情况,可以通过关闭OpenEntityManagerInViewInterceptor来确保当下游服务响应缓慢时不会导致数据库连接被占用完.