Java 对并发请求执行长时间I/o操作后,HikariPool连接不可用

Java 对并发请求执行长时间I/o操作后,HikariPool连接不可用,java,multithreading,spring-boot,hikaricp,Java,Multithreading,Spring Boot,Hikaricp,我最近开始在调用第三方api的项目中出错,通常需要40秒。看起来来自hikaripool的db连接被父线程劫持,无法用于其他并发请求 下面是实现的概述 数据库插入(业务逻辑) 重新模板调用(40秒) 数据库更新(业务逻辑) 关于高并发性的一些错误 java.sql.SQLTransientConnectionException:HikariPool-1-连接不可用,请求在30005ms后超时。 我试图在本地模拟这一点,发现这些错误与生产相同 Hikari配置 hikari: maxi

我最近开始在调用第三方api的项目中出错,通常需要40秒。看起来来自hikaripool的db连接被父线程劫持,无法用于其他并发请求

下面是实现的概述

  • 数据库插入(业务逻辑)
  • 重新模板调用(40秒)
  • 数据库更新(业务逻辑)
  • 关于高并发性的一些错误

    java.sql.SQLTransientConnectionException:HikariPool-1-连接不可用,请求在30005ms后超时。

    我试图在本地模拟这一点,发现这些错误与生产相同

    Hikari配置

    hikari:
          maximumPoolSize: 2
          idleTimeout: 60000
          minimumIdle: 2
          maxLifetime: 120000
          leak-detection-threshold: 1000 
    
    模拟生产逻辑的服务方法

    
    public void insertComment() throws InterruptedException {
            Load load =  new Load();
            load.setComment("new comment "+System.currentTimeMillis());
            repo.save(load);
            Thread.sleep(60000); // mocking restemplate long i/o
            load =  new Load();
            load.setComment("new comment "+System.currentTimeMillis());
            repo.save(load);
        }
    
    
    编辑1:

    现在向调用此服务方法的api(hikari cp+1中的2个连接)触发3个并发请求

    堆栈跟踪-1

    2021-01-01 22:50:01.309  WARN 6599 --- [l-1 housekeeper] com.zaxxer.hikari.pool.ProxyLeakTask     : Connection leak detection triggered for com.mysql.jdbc.JDBC4Connection@462abec3 on thread http-nio-8080-exec-1, stack trace follows
    
    java.lang.Exception: Apparent connection leak detected
        at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:128) ~[HikariCP-3.4.5.jar:na]
        at org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl.getConnection(DatasourceConnectionProviderImpl.java:122) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
        at org.hibernate.internal.NonContextualJdbcConnectionAccess.obtainConnection(NonContextualJdbcConnectionAccess.java:38) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
        at org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.acquireConnectionIfNeeded(LogicalConnectionManagedImpl.java:108) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
        at org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.getPhysicalConnection(LogicalConnectionManagedImpl.java:138) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
        at org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.getConnectionForTransactionManagement(LogicalConnectionManagedImpl.java:273) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
        at org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.begin(LogicalConnectionManagedImpl.java:281) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
        at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.begin(JdbcResourceLocalTransactionCoordinatorImpl.java:246) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
        at org.hibernate.engine.transaction.internal.TransactionImpl.begin(TransactionImpl.java:83) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
        at org.springframework.orm.jpa.vendor.HibernateJpaDialect.beginTransaction(HibernateJpaDialect.java:164) ~[spring-orm-5.3.2.jar:5.3.2]
        at org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:421) ~[spring-orm-5.3.2.jar:5.3.2]
        at org.springframework.transaction.support.AbstractPlatformTransactionManager.startTransaction(AbstractPlatformTransactionManager.java:400) ~[spring-tx-5.3.2.jar:5.3.2]
        at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:373) ~[spring-tx-5.3.2.jar:5.3.2]
        at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:595) ~[spring-tx-5.3.2.jar:5.3.2]
        at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:382) ~[spring-tx-5.3.2.jar:5.3.2]
        at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) ~[spring-tx-5.3.2.jar:5.3.2]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.2.jar:5.3.2]
        at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:137) ~[spring-tx-5.3.2.jar:5.3.2]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.2.jar:5.3.2]
        at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:174) ~[spring-data-jpa-2.4.2.jar:2.4.2]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.2.jar:5.3.2]
        at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97) ~[spring-aop-5.3.2.jar:5.3.2]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.2.jar:5.3.2]
        at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215) ~[spring-aop-5.3.2.jar:5.3.2]
        at com.sun.proxy.$Proxy77.save(Unknown Source) ~[na:na]
        at com.example.demo.Service.insertComment(Service.java:89) ~[classes/:na]
    
    堆栈跟踪-2

    
    java.sql.SQLTransientConnectionException: HikariPool-1 - Connection is not available, request timed out after 30002ms.
        at com.zaxxer.hikari.pool.HikariPool.createTimeoutException(HikariPool.java:695) ~[HikariCP-3.4.5.jar:na]
        at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:197) ~[HikariCP-3.4.5.jar:na]
        at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:162) ~[HikariCP-3.4.5.jar:na]
        at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:128) ~[HikariCP-3.4.5.jar:na]
        at org.hibernate.engine.jdbc.connections.internal.DatasourceConnectionProviderImpl.getConnection(DatasourceConnectionProviderImpl.java:122) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
        at org.hibernate.internal.NonContextualJdbcConnectionAccess.obtainConnection(NonContextualJdbcConnectionAccess.java:38) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
        at org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.acquireConnectionIfNeeded(LogicalConnectionManagedImpl.java:108) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
        at org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.getPhysicalConnection(LogicalConnectionManagedImpl.java:138) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
        at org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.getConnectionForTransactionManagement(LogicalConnectionManagedImpl.java:273) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
        at org.hibernate.resource.jdbc.internal.LogicalConnectionManagedImpl.begin(LogicalConnectionManagedImpl.java:281) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
        at org.hibernate.resource.transaction.backend.jdbc.internal.JdbcResourceLocalTransactionCoordinatorImpl$TransactionDriverControlImpl.begin(JdbcResourceLocalTransactionCoordinatorImpl.java:246) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
        at org.hibernate.engine.transaction.internal.TransactionImpl.begin(TransactionImpl.java:83) ~[hibernate-core-5.4.25.Final.jar:5.4.25.Final]
        at org.springframework.orm.jpa.vendor.HibernateJpaDialect.beginTransaction(HibernateJpaDialect.java:164) ~[spring-orm-5.3.2.jar:5.3.2]
        at org.springframework.orm.jpa.JpaTransactionManager.doBegin(JpaTransactionManager.java:421) ~[spring-orm-5.3.2.jar:5.3.2]
        at org.springframework.transaction.support.AbstractPlatformTransactionManager.startTransaction(AbstractPlatformTransactionManager.java:400) ~[spring-tx-5.3.2.jar:5.3.2]
        at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:373) ~[spring-tx-5.3.2.jar:5.3.2]
        at org.springframework.transaction.interceptor.TransactionAspectSupport.createTransactionIfNecessary(TransactionAspectSupport.java:595) ~[spring-tx-5.3.2.jar:5.3.2]
        at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:382) ~[spring-tx-5.3.2.jar:5.3.2]
        at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) ~[spring-tx-5.3.2.jar:5.3.2]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.2.jar:5.3.2]
        at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:137) ~[spring-tx-5.3.2.jar:5.3.2]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.2.jar:5.3.2]
        at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:174) ~[spring-data-jpa-2.4.2.jar:2.4.2]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.2.jar:5.3.2]
        at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97) ~[spring-aop-5.3.2.jar:5.3.2]
        at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.3.2.jar:5.3.2]
        at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215) ~[spring-aop-5.3.2.jar:5.3.2]
        at com.sun.proxy.$Proxy77.save(Unknown Source) ~[na:na]
        at com.example.demo.Service.insertComment(Service.java:89) ~[classes/:na]
    
    堆栈跟踪-3

    
    2021-01-01 22:50:51.411  INFO 6599 --- [nio-8080-exec-1] com.zaxxer.hikari.pool.ProxyLeakTask     : Previously reported leaked connection com.mysql.jdbc.JDBC4Connection@462abec3 on thread http-nio-8080-exec-1 was returned to the pool (unleaked)
    
    如何处理这个问题

    编辑2:


    我找到了这个帖子。设置此配置
    spring.jpa.open in view=false似乎可以解决这个问题。任何关于这个配置的线索。安全吗?

    您可以增加连接超时:


    但是,在这种情况下,最好不要在调用第三方API时保持连接,而是使用单独的连接进行DB调用。

    您说过:

    关于高并发性的一些错误

    你会得到:

    java.sql.SQLTransientConnectionException:HikariPool-1-连接为 不可用,请求在30005ms后超时

    这意味着DB连接池(Hikari)最多无法在30秒内提供连接。
    您已将池的最大连接数配置为2:

      maximumPoolSize: 2
    
    它很少,而且,如果您的查询处理时间很长,并且您的应用程序具有较高的并发性,那么该参数看起来确实低于估计值。
    将其增加到更高的数字,例如10或更多,然后观察其行为。 您也可以使用connectionTimeout参数,但结果是,如果客户端请求等待连接,它会减慢应用程序的速度

    可以为您提供有关调整配置方法的一些信息

    作为替代方案,您可以重新考虑您的数据源。 例如,定义两个数据源:一个用于长连接(用于长查询),另一个用于排序连接(用于短查询),并根据情况使用更适合的数据源

    另一方面,Spring Boot提供了一个专注于数据源连接的执行器:
    datasourcehealthdicator
    检查是否可以获得到数据源的连接。

    与监控系统工具(普罗米修斯或任何其他工具)结合,这些工具可能有助于诊断此类问题。

    Hi@davidxxx这只是一个模拟。。。。在prod上,我确实将其设置为10,但是对于10个并发请求,这是罚款,但是对于第11个请求,这是相同的故事。嗨,pranav,当我读到你的问题时,我有一些疑问。你可以增加到10以上。15岁或20岁可能会有所改善。我用另一个想法编辑了我的答案。当然。有没有其他的方法来处理这个问题,或者这是故意的。一个线程一个数据库连接。这是设计的。数据库连接是有状态的:它包含调用方和数据库发送的特定信息。如果不冒破坏其状态的风险,您就无法共享它。您好,David,在.yml中设置此值时,
    spring.jpa.open in view=false
    。。。负载测试了15个并发请求和2个hikari池连接的相同场景。未发现任何问题。你能在这里分享你的想法吗。。。。也未检测到连接泄漏…谢谢。
      maximumPoolSize: 2