Spring + Hibernate+ HikariCP:如何在执行长 运行 REST 调用时处理数据库连接?
Spring + Hibernate+ HikariCP: how to handle DB connection while doing long running REST call?
我在 Spring Boot 1.3.8、Hikari CP 2.6.1 和 Hibernate (Spring ORM 4.2.8) 上有一个项目 运行。服务层的代码如下所示:
public void doStuff() {
A a = dao.findByWhatever();
if (a.hasProperty()) {
B b = restService.doRemoteRequestWithRetries(); // May take long time
}
a.setProp(b.getSomethig());
dao.save(b);
}
Hikari 配置是这样的:spring.datasource.leakDetectionThreshold=2000
。
问题是外部 REST 服务很慢,通常需要 2 秒以上才能响应,结果我们看到了很多 java.lang.Exception: Apparent connection leak detected
这不过是漏报,尽管可以清楚地看到问题:我们持有我们执行休息请求时的数据库连接。
问题是:如何正确分离 DB 和 REST 的东西?或者如何告诉休眠释放两者之间的连接?这样我们在等待 REST 响应时 return 到池的数据库连接。
我已经尝试设置 hibernate.connection.release_mode=AFTER_TRANSACTION
并且它有点帮助,至少我们没有连接泄漏异常。唯一的问题是我们的测试开始显示:
2018-04-17 15:48:03.438 WARN 94029 --- [ main] o.s.orm.jpa.vendor.HibernateJpaDialect : JDBC Connection to reset not identical to originally prepared Connection - please make sure to use connection release mode ON_CLOSE (the default) and to run against Hibernate 4.2+ (or switch HibernateJpaDialect's prepareConnection flag to false`
测试使用注入的 DAO 在数据库中插入记录,然后通过应用程序检查它们 API。它们没有用 @Transactional
注释,听众列表如下所示:
@TestExecutionListeners({
DependencyInjectionTestExecutionListener.class,
TransactionalTestExecutionListener.class,
TransactionDbUnitTestExecutionListener.class
})
有什么想法可能是测试的问题吗?
代码中
public void doStuff() {
A a = dao.findByWhatever();
if (a.hasProperty()) {
B b = restService.doRemoteRequestWithRetries(); // May take long time
}
a.setProp(b.getSomethig());
dao.save(b);
}
我在这里看到三个任务 - 获取实体 A
、连接到远程服务和更新实体 A
。还有 all these are in same transaction, so the underlying connection will be held till the method is complete
.
所以我们的想法是将任务一和任务三拆分为单独的事务,在调用远程服务之前允许释放连接。
基本上,使用 spring 引导您需要添加 spring.jpa.open-in-view=false
。这不会注册 OpenEntityManagerInViewInterceptor,因此 entityManager(依次连接)未绑定到当前 thread/request.
随后,使用@Transactional 将这三个任务拆分为单独的方法。这有助于我们将 entityManager 绑定到事务范围并在事务方法结束时释放连接。
注意: 并确保在调用这些方法之前没有任何事务 started/in 进展(即,调用者 - 如 Controller 等)。否则目的就失败了,这些新的@Transactional 方法将 运行 在与以前相同的事务中。
因此高级方法可能如下所示:
- 在spring开机application.properties添加属性
spring.jpa.open-in-view=false
.
- 接下来需要在新服务class中将
doStuff
方法拆分为三个方法。目的是确保他们使用不同的交易。
- @Transactional 的第一个方法
will call
A a = dao.findByWhatever();`.
- 第二种方式远程调用
- @Transactional 的第三种方法
will call rest of the code with JPA merge or hibernate saveOrUpdate on object
a`。
- 现在在您当前的代码中自动装配这个新服务并调用 3 个方法。
我在 Spring Boot 1.3.8、Hikari CP 2.6.1 和 Hibernate (Spring ORM 4.2.8) 上有一个项目 运行。服务层的代码如下所示:
public void doStuff() {
A a = dao.findByWhatever();
if (a.hasProperty()) {
B b = restService.doRemoteRequestWithRetries(); // May take long time
}
a.setProp(b.getSomethig());
dao.save(b);
}
Hikari 配置是这样的:spring.datasource.leakDetectionThreshold=2000
。
问题是外部 REST 服务很慢,通常需要 2 秒以上才能响应,结果我们看到了很多 java.lang.Exception: Apparent connection leak detected
这不过是漏报,尽管可以清楚地看到问题:我们持有我们执行休息请求时的数据库连接。
问题是:如何正确分离 DB 和 REST 的东西?或者如何告诉休眠释放两者之间的连接?这样我们在等待 REST 响应时 return 到池的数据库连接。
我已经尝试设置 hibernate.connection.release_mode=AFTER_TRANSACTION
并且它有点帮助,至少我们没有连接泄漏异常。唯一的问题是我们的测试开始显示:
2018-04-17 15:48:03.438 WARN 94029 --- [ main] o.s.orm.jpa.vendor.HibernateJpaDialect : JDBC Connection to reset not identical to originally prepared Connection - please make sure to use connection release mode ON_CLOSE (the default) and to run against Hibernate 4.2+ (or switch HibernateJpaDialect's prepareConnection flag to false`
测试使用注入的 DAO 在数据库中插入记录,然后通过应用程序检查它们 API。它们没有用 @Transactional
注释,听众列表如下所示:
@TestExecutionListeners({
DependencyInjectionTestExecutionListener.class,
TransactionalTestExecutionListener.class,
TransactionDbUnitTestExecutionListener.class
})
有什么想法可能是测试的问题吗?
代码中
public void doStuff() {
A a = dao.findByWhatever();
if (a.hasProperty()) {
B b = restService.doRemoteRequestWithRetries(); // May take long time
}
a.setProp(b.getSomethig());
dao.save(b);
}
我在这里看到三个任务 - 获取实体 A
、连接到远程服务和更新实体 A
。还有 all these are in same transaction, so the underlying connection will be held till the method is complete
.
所以我们的想法是将任务一和任务三拆分为单独的事务,在调用远程服务之前允许释放连接。
基本上,使用 spring 引导您需要添加 spring.jpa.open-in-view=false
。这不会注册 OpenEntityManagerInViewInterceptor,因此 entityManager(依次连接)未绑定到当前 thread/request.
随后,使用@Transactional 将这三个任务拆分为单独的方法。这有助于我们将 entityManager 绑定到事务范围并在事务方法结束时释放连接。
注意: 并确保在调用这些方法之前没有任何事务 started/in 进展(即,调用者 - 如 Controller 等)。否则目的就失败了,这些新的@Transactional 方法将 运行 在与以前相同的事务中。
因此高级方法可能如下所示:
- 在spring开机application.properties添加属性
spring.jpa.open-in-view=false
. - 接下来需要在新服务class中将
doStuff
方法拆分为三个方法。目的是确保他们使用不同的交易。- @Transactional 的第一个方法
will call
A a = dao.findByWhatever();`.
- @Transactional 的第一个方法
- 第二种方式远程调用
- @Transactional 的第三种方法
will call rest of the code with JPA merge or hibernate saveOrUpdate on object
a`。 - 现在在您当前的代码中自动装配这个新服务并调用 3 个方法。