Spring Boot 关闭MyManagerFactory非常缓慢

esyap4oy  于 2024-01-06  发布在  Spring
关注(0)|答案(2)|浏览(142)

My Sping Boot v3.2.0应用使用JDK 21。当测试套件完成时,关闭已创建的EntityManagerFactory bean需要30秒以上,约占测试套件总执行时间的33%。日志显示其中3个bean需要11秒关闭,并且似乎大部分时间都花在等待相关的Hikari连接池关闭上。
当应用程序本身关闭时,不会发生这种延迟。这可能是因为应用程序只创建了此bean的1个示例,但测试套件创建了14个示例(因为测试套件构建的应用程序上下文不同)。
当测试运行时,是否有方法:

  • 减少创建的EntityManagerFactory bean的数量
  • 减少关闭EntityManagerFactory bean所需的时间

测试使用以下Hikari配置

  1. spring.datasource.hikari.maximumPoolSize=2

字符串
测试套件日志的相关部分如下所示

  1. 2023-12-07T15:53:41.555Z --- j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
  2. 2023-12-07T15:53:41.556Z --- com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown initiated...
  3. 2023-12-07T15:53:41.557Z --- com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Shutdown completed.
  4. 2023-12-07T15:53:52.831Z --- j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
  5. 2023-12-07T15:53:52.832Z --- com.zaxxer.hikari.HikariDataSource : HikariPool-2 - Shutdown initiated...
  6. 2023-12-07T15:53:52.834Z --- com.zaxxer.hikari.HikariDataSource : HikariPool-2 - Shutdown completed.
  7. 2023-12-07T15:54:03.861Z --- j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
  8. 2023-12-07T15:54:03.863Z --- com.zaxxer.hikari.HikariDataSource : HikariPool-3 - Shutdown initiated...
  9. 2023-12-07T15:54:03.865Z --- com.zaxxer.hikari.HikariDataSource : HikariPool-3 - Shutdown completed.
  10. 2023-12-07T15:54:03.886Z --- j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
  11. 2023-12-07T15:54:03.887Z --- com.zaxxer.hikari.HikariDataSource : HikariPool-4 - Shutdown initiated...
  12. 2023-12-07T15:54:03.888Z --- com.zaxxer.hikari.HikariDataSource : HikariPool-4 - Shutdown completed.
  13. 2023-12-07T15:54:03.899Z --- j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
  14. 2023-12-07T15:54:03.899Z --- com.zaxxer.hikari.HikariDataSource : HikariPool-5 - Shutdown initiated...
  15. 2023-12-07T15:54:03.900Z --- com.zaxxer.hikari.HikariDataSource : HikariPool-5 - Shutdown completed.
  16. 2023-12-07T15:54:14.916Z --- j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
  17. 2023-12-07T15:54:14.917Z --- com.zaxxer.hikari.HikariDataSource : HikariPool-6 - Shutdown initiated...
  18. 2023-12-07T15:54:14.919Z --- com.zaxxer.hikari.HikariDataSource : HikariPool-6 - Shutdown completed.
  19. 2023-12-07T15:54:14.938Z --- j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
  20. 2023-12-07T15:54:14.939Z --- com.zaxxer.hikari.HikariDataSource : HikariPool-7 - Shutdown initiated...
  21. 2023-12-07T15:54:14.940Z --- com.zaxxer.hikari.HikariDataSource : HikariPool-7 - Shutdown completed.
  22. 2023-12-07T15:54:14.953Z --- j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
  23. 2023-12-07T15:54:14.954Z --- com.zaxxer.hikari.HikariDataSource : HikariPool-8 - Shutdown initiated...
  24. 2023-12-07T15:54:14.954Z --- com.zaxxer.hikari.HikariDataSource : HikariPool-8 - Shutdown completed.
  25. 2023-12-07T15:54:14.955Z --- j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
  26. 2023-12-07T15:54:14.955Z --- com.zaxxer.hikari.HikariDataSource : HikariPool-9 - Shutdown initiated...
  27. 2023-12-07T15:54:14.956Z --- com.zaxxer.hikari.HikariDataSource : HikariPool-9 - Shutdown completed.
  28. 2023-12-07T15:54:14.960Z --- j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
  29. 2023-12-07T15:54:14.960Z --- com.zaxxer.hikari.HikariDataSource : HikariPool-10 - Shutdown initiated...
  30. 2023-12-07T15:54:14.960Z --- com.zaxxer.hikari.HikariDataSource : HikariPool-10 - Shutdown completed.
  31. 2023-12-07T15:54:14.964Z --- j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
  32. 2023-12-07T15:54:14.964Z --- com.zaxxer.hikari.HikariDataSource : HikariPool-11 - Shutdown initiated...
  33. 2023-12-07T15:54:14.965Z --- com.zaxxer.hikari.HikariDataSource : HikariPool-11 - Shutdown completed.
  34. 2023-12-07T15:54:14.968Z --- j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
  35. 2023-12-07T15:54:14.968Z --- com.zaxxer.hikari.HikariDataSource : HikariPool-12 - Shutdown initiated...
  36. 2023-12-07T15:54:14.969Z --- com.zaxxer.hikari.HikariDataSource : HikariPool-12 - Shutdown completed.
  37. 2023-12-07T15:54:14.972Z --- j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
  38. 2023-12-07T15:54:14.972Z --- com.zaxxer.hikari.HikariDataSource : HikariPool-13 - Shutdown initiated...
  39. 2023-12-07T15:54:14.972Z --- com.zaxxer.hikari.HikariDataSource : HikariPool-13 - Shutdown completed.
  40. 2023-12-07T15:54:14.975Z --- j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
  41. 2023-12-07T15:54:14.976Z --- com.zaxxer.hikari.HikariDataSource : HikariPool-14 - Shutdown initiated...
  42. 2023-12-07T15:54:14.992Z --- com.zaxxer.hikari.HikariDataSource : HikariPool-14 - Shutdown completed.
  43. > Task :test
  44. SUCCESS: Executed 706 tests in 1m 37s (2 skipped)

lsmd5eda

lsmd5eda1#

你的问题提供了很多先决条件,但是让我们试着弄清楚我们能做些什么。
首先
减少所创建的LogistyManagerFactory bean的数量
EntityManagerFactory是单例的,所以每个应用程序上下文只创建一个bean。如果你有多个BifactyManagerFactory,这表明你有相同数量的应用程序上下文。
所以首先让我们尝试通过缓存来减少它们的数量。我会提取一个抽象类并在其上放置公共注解,例如:

  1. @SpringBootTest
  2. public abstract class BaseTest {
  3. }

字符串
现在,每个涉及Sping Boot 的测试类都应该是TestBase的子类,这将允许缓存应用程序上下文并重用它:

  1. class MyRepositoryTest extends BaseTest {
  2. //...
  3. }
  4. class AnotherRepositoryTest extends BaseTest {
  5. //...
  6. }
  7. class OneMoreRepositoryTest extends BaseTest {
  8. //...
  9. }


如果需要,您可以创建多个抽象测试类,以便将具有相同应用程序上下文的测试分组,例如:

  1. @DataJpaTest
  2. @SpringBootTest
  3. public abstract class BaseJpaRepoTest {
  4. }
  5. @SpringBootTest
  6. @AutoConfigureMockMvc
  7. public abstract class BaseControllerTest {
  8. }


使用这种方法,您将为每组测试(用于存储库、端点等)缓存一个应用程序上下文。
然后,作为这个属性:

  1. spring.datasource.hikari.maximumPoolSize=2


我不认为你应该为测试配置池大小,所以让我们尝试删除这个属性并使用默认值。从另一个Angular 来看,这个属性也是特定于上下文的,因此,如果您有n应用程序上下文,则连接的总数为2*n。假设我们已将上下文的数量减少到1,并且所有测试都重用它,我认为2可能不够做测试
我想到的另一件事是,所有的连接都开始同时关闭,所以看起来你正在以并行模式运行测试。这可能会导致问题,例如,所有的连接都处理同一个表并且是事务性的,或者在@AfterEach中进行一些繁重的操作/清理。
你的步骤是:
1.高速缓存应用上下文
1.摆脱自定义池配置
1.注意事务性/拆卸方法
P.S.如果这没有帮助,请尝试在SessionFactoryImpl.close()中放置一些断点:

  1. // AbstractEntityManagerFactoryBean
  2. @Override
  3. public void destroy() {
  4. if (this.entityManagerFactory != null) {
  5. if (logger.isInfoEnabled()) {
  6. logger.info("Closing JPA EntityManagerFactory for persistence unit '" + getPersistenceUnitName() + "'");
  7. }
  8. this.entityManagerFactory.close();
  9. }
  10. }
  11. //SessionFactoryImpl
  12. public void close() throws HibernateException {
  13. synchronized (this) {
  14. if ( status != Status.OPEN ) {
  15. if ( getSessionFactoryOptions().getJpaCompliance().isJpaClosedComplianceEnabled() ) {
  16. throw new IllegalStateException( "EntityManagerFactory is already closed" );
  17. }
  18. LOG.trace( "Already closed" );
  19. return;
  20. }
  21. status = Status.CLOSING;
  22. }
  23. try {
  24. LOG.closing();
  25. observer.sessionFactoryClosing( this );
  26. // NOTE : the null checks below handle cases where close is called from
  27. // a failed attempt to create the SessionFactory
  28. if ( cacheAccess != null ) {
  29. cacheAccess.close();
  30. }
  31. if ( runtimeMetamodels != null && runtimeMetamodels.getMappingMetamodel() != null ) {
  32. final JdbcConnectionAccess jdbcConnectionAccess = jdbcServices.getBootstrapJdbcConnectionAccess();
  33. runtimeMetamodels.getMappingMetamodel().forEachEntityDescriptor(
  34. entityPersister -> {
  35. if ( entityPersister.getSqmMultiTableMutationStrategy() != null ) {
  36. entityPersister.getSqmMultiTableMutationStrategy().release(
  37. this,
  38. jdbcConnectionAccess
  39. );
  40. }
  41. if ( entityPersister.getSqmMultiTableInsertStrategy() != null ) {
  42. entityPersister.getSqmMultiTableInsertStrategy().release(
  43. this,
  44. jdbcConnectionAccess
  45. );
  46. }
  47. }
  48. );
  49. ( (MappingMetamodelImpl) runtimeMetamodels.getMappingMetamodel() ).close();
  50. }
  51. if ( queryEngine != null ) {
  52. queryEngine.close();
  53. }
  54. if ( eventEngine != null ) {
  55. eventEngine.stop();
  56. }
  57. }
  58. finally {
  59. status = Status.CLOSED;
  60. }
  61. observer.sessionFactoryClosed( this );
  62. serviceRegistry.destroy();
  63. }


这里有多个循环,AbstractServiceRegistryImpl中有一个:

  1. @Override
  2. public synchronized void destroy() {
  3. if ( active.compareAndSet( true, false ) ) {
  4. try {
  5. //First thing, make sure that the fast path read is disabled so that
  6. //threads not owning the synchronization lock can't get an invalid Service:
  7. initializedServiceByRole.clear();
  8. synchronized (serviceBindingList) {
  9. ListIterator<ServiceBinding<?>> serviceBindingsIterator = serviceBindingList.listIterator(
  10. serviceBindingList.size()
  11. );
  12. while ( serviceBindingsIterator.hasPrevious() ) {
  13. final ServiceBinding<?> serviceBinding = serviceBindingsIterator.previous();
  14. serviceBinding.getLifecycleOwner().stopService( serviceBinding );
  15. }
  16. serviceBindingList.clear();
  17. }
  18. serviceBindingMap.clear();
  19. }
  20. finally {
  21. if ( parent != null ) {
  22. parent.deRegisterChild( this );
  23. }
  24. }
  25. }
  26. }

展开查看全部
jmo0nnb3

jmo0nnb32#

升级hibernatejar,并开始使用BOM导入进行 Boot 。
我们需要更多的信息来了解您的Hikari Pool是如何工作的。您可以随时调用您正在创建的池的数量和大小。如果自动EMF的池很重而且很慢,请通过创建大的池大小来配置您自己的池。确保它们在池之间没有空闲。

  1. hikari:
  2. connection-timeout:
  3. minimum-idle:
  4. maximum-pool-size: to define pool size
  5. idle-timeout:
  6. max-lifetime:
  7. auto-commit: true
  8. driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
  9. leak-detection-threshold:

字符串

相关问题