Skip to main content

通过关闭OpenEntityManagerInViewInterceptor确保当下游服务响应缓慢时不会导致数据库连接被占用完

· 3 min read
orange
programmer on jvm platform

OpenEntityManagerInViewInterceptorspring中的一个拦截器,它的作用是在整个请求上下文复用同一个EntityManager ,从而避免在一个请求中多次创建一个EntityManager, 从而提高性能.
但是如果在一个请求的处理代码中请求了下游服务,而下游服务响应缓慢,那么在下游服务响应之前,EntityManager 会一直被占用,直到整个请求处理完成,这样会导致当服务并发请求量较大时,数据库连接被占用完.
为了避免这种情况,可以通过关闭OpenEntityManagerInViewInterceptor来确保当下游服务响应缓慢时不会导致数据库连接被占用完. 下面将介绍如何关闭OpenEntityManagerInViewInterceptor.

解决方案

spring-boot中,可以通过配置spring.jpa.open-in-viewfalse来关闭OpenEntityManagerInViewInterceptor,示例如下

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

参考资料