通过关闭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
来确保当下游服务响应缓慢时不会导致数据库连接被占用完.