本文主要研究在中途数据库挂的情况下,hikari与tomcat jdbc pool的fail fast情况。

实验代码

  @Testpublic void testDatabaseDownAndUp() throws SQLException, InterruptedException {LOGGER.info("begin to wait for database down and up");for(int c=0;c<10;c++){LOGGER.info("execute:"+(c+1));Connection conn = null;String sql = "select 1";PreparedStatement pstmt = null;try {long start = System.currentTimeMillis();conn = dataSource.getConnection();LOGGER.info("{} got connection,cost:{}",(c+1),System.currentTimeMillis() - start);pstmt = (PreparedStatement)conn.prepareStatement(sql);ResultSet rs = pstmt.executeQuery();int col = rs.getMetaData().getColumnCount();while (rs.next()) {for (int i = 1; i <= col; i++) {System.out.print(rs.getObject(i));}System.out.println("");}LOGGER.info("============================");} catch (Exception e) {e.printStackTrace();} finally {//close resourcesDbUtils.closeQuietly(pstmt);DbUtils.closeQuietly(conn);}TimeUnit.SECONDS.sleep(10);}}
复制代码

在第一次循环输出之后断开数据库连接,等个二三十秒再恢复。

hikari

2018-01-30 15:35:16.668  INFO 5852 --- [           main] c.e.demo.HikariDemoApplicationTests      : execute:1
2018-01-30 15:35:26.854  INFO 5852 --- [           main] c.e.demo.HikariDemoApplicationTests      : execute:2
2018-01-30 15:36:06.867  INFO 5852 --- [           main] c.e.demo.HikariDemoApplicationTests      : execute:3
2018-01-30 15:36:46.878  INFO 5852 --- [           main] c.e.demo.HikariDemoApplicationTests      : execute:4
复制代码

从这点来看,中途数据库断开的话,hikari会不断获取数据库链接

假设数据库一直没恢复,那么所以请求数据库操作的业务线程将都阻塞connectionTimeout的时间,这个会占用工作线程

hikari只有testOnBorrow功能,是直接一个while循环,在timeout时间内不断borrow连接,validate连接,validate成功才返回或者超时抛出SQLTransientConnectionException异常。borrow的超时时间为connectionTimeout,默认30秒。

validate不成功日志记录的异常

2018-01-31 16:45:00.653  WARN 6501 --- [           main] org.postgresql.jdbc.PgConnection         : Validating connection.org.postgresql.util.PSQLException: FATAL: terminating connection due to administrator commandat org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2422) ~[postgresql-42.2.1.jar:42.2.1]at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2167) ~[postgresql-42.2.1.jar:42.2.1]at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:306) ~[postgresql-42.2.1.jar:42.2.1]at org.postgresql.jdbc.PgStatement.executeInternal(PgStatement.java:441) ~[postgresql-42.2.1.jar:42.2.1]at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:365) ~[postgresql-42.2.1.jar:42.2.1]at org.postgresql.jdbc.PgPreparedStatement.executeWithFlags(PgPreparedStatement.java:155) ~[postgresql-42.2.1.jar:42.2.1]at org.postgresql.jdbc.PgPreparedStatement.executeUpdate(PgPreparedStatement.java:132) ~[postgresql-42.2.1.jar:42.2.1]at org.postgresql.jdbc.PgConnection.isValid(PgConnection.java:1364) ~[postgresql-42.2.1.jar:42.2.1]at com.zaxxer.hikari.pool.PoolBase.isConnectionAlive(PoolBase.java:160) [HikariCP-2.7.6.jar:na]at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:171) [HikariCP-2.7.6.jar:na]at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:147) [HikariCP-2.7.6.jar:na]at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:123) [HikariCP-2.7.6.jar:na]at com.example.demo.HikariDemoApplicationTests.testDatabaseDownAndUp(HikariDemoApplicationTests.java:109) [test-classes/:na]at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_71]at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_71]at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_71]at java.lang.reflect.Method.invoke(Method.java:497) ~[na:1.8.0_71]at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) [junit-4.12.jar:4.12]at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) [junit-4.12.jar:4.12]at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) [junit-4.12.jar:4.12]at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) [junit-4.12.jar:4.12]at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:73) [spring-test-5.0.3.RELEASE.jar:5.0.3.RELEASE]at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:83) [spring-test-5.0.3.RELEASE.jar:5.0.3.RELEASE]at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75) [spring-test-5.0.3.RELEASE.jar:5.0.3.RELEASE]at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86) [spring-test-5.0.3.RELEASE.jar:5.0.3.RELEASE]at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84) [spring-test-5.0.3.RELEASE.jar:5.0.3.RELEASE]at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) [junit-4.12.jar:4.12]at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251) [spring-test-5.0.3.RELEASE.jar:5.0.3.RELEASE]at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97) [spring-test-5.0.3.RELEASE.jar:5.0.3.RELEASE]at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) [junit-4.12.jar:4.12]at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) [junit-4.12.jar:4.12]at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) [junit-4.12.jar:4.12]at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) [junit-4.12.jar:4.12]at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) [junit-4.12.jar:4.12]at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) [spring-test-5.0.3.RELEASE.jar:5.0.3.RELEASE]at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70) [spring-test-5.0.3.RELEASE.jar:5.0.3.RELEASE]at org.junit.runners.ParentRunner.run(ParentRunner.java:363) [junit-4.12.jar:4.12]at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190) [spring-test-5.0.3.RELEASE.jar:5.0.3.RELEASE]at org.junit.runner.JUnitCore.run(JUnitCore.java:137) [junit-4.12.jar:4.12]at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69) [junit-rt.jar:na]at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234) [junit-rt.jar:na]at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74) [junit-rt.jar:na]at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_71]at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_71]at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_71]at java.lang.reflect.Method.invoke(Method.java:497) ~[na:1.8.0_71]at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144) [idea_rt.jar:na]
复制代码

假设在测试之前新建立好maxPoolSize的连接,那么执行之后中断数据库,则下一次的getConnection会挨个取出这些空闲连接,validate,validate失败会在log里头记录异常,同时getConnection继续循环获取连接

getConnection timeout抛出的异常

java.sql.SQLTransientConnectionException: demo1 - Connection is not available, request timed out after 60007ms.at com.zaxxer.hikari.pool.HikariPool.createTimeoutException(HikariPool.java:666)at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:182)at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:147)at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:123)at com.example.demo.HikariDemoApplicationTests.testDatabaseDownAndUp(HikariDemoApplicationTests.java:98)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.lang.reflect.Method.invoke(Method.java:497)at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:73)at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:83)at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)at org.junit.runners.ParentRunner.run(ParentRunner.java:363)at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)at org.junit.runner.JUnitCore.run(JUnitCore.java:137)at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234)at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.lang.reflect.Method.invoke(Method.java:497)at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Caused by: org.postgresql.util.PSQLException: Connection to 192.168.99.100:5432 refused. Check that the hostname and port are correct and that the postmaster is accepting TCP/IP connections.at org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl(ConnectionFactoryImpl.java:247)at org.postgresql.core.ConnectionFactory.openConnection(ConnectionFactory.java:49)at org.postgresql.jdbc.PgConnection.<init>(PgConnection.java:195)at org.postgresql.Driver.makeConnection(Driver.java:452)at org.postgresql.Driver.connect(Driver.java:254)at com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:117)at com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:123)at com.zaxxer.hikari.pool.PoolBase.newConnection(PoolBase.java:375)at com.zaxxer.hikari.pool.PoolBase.newPoolEntry(PoolBase.java:204)at com.zaxxer.hikari.pool.HikariPool.createPoolEntry(HikariPool.java:459)at com.zaxxer.hikari.pool.HikariPool.access$200(HikariPool.java:70)at com.zaxxer.hikari.pool.HikariPool$PoolEntryCreator.call(HikariPool.java:696)at com.zaxxer.hikari.pool.HikariPool$PoolEntryCreator.call(HikariPool.java:682)at java.util.concurrent.FutureTask.run(FutureTask.java:266)at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)at java.lang.Thread.run(Thread.java:745)
Caused by: java.net.ConnectException: Connection refusedat java.net.PlainSocketImpl.socketConnect(Native Method)at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)at java.net.Socket.connect(Socket.java:589)at org.postgresql.core.PGStream.<init>(PGStream.java:69)at org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl(ConnectionFactoryImpl.java:158)... 16 more
复制代码

循环获取连接超时,则抛出SQLTransientConnectionException

异步线程PoolEntryCreator补充新连接在日志记录的异常

2018-01-31 16:45:00.915  WARN 6501 --- [onnection adder] unknown.jul.logger                       : ConnectException occurred while connecting to 192.168.99.100:5432java.net.ConnectException: Connection refusedat java.net.PlainSocketImpl.socketConnect(Native Method) ~[na:1.8.0_71]at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350) ~[na:1.8.0_71]at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206) ~[na:1.8.0_71]at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188) ~[na:1.8.0_71]at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392) ~[na:1.8.0_71]at java.net.Socket.connect(Socket.java:589) ~[na:1.8.0_71]at org.postgresql.core.PGStream.<init>(PGStream.java:69) ~[postgresql-42.2.1.jar:42.2.1]at org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl(ConnectionFactoryImpl.java:158) ~[postgresql-42.2.1.jar:42.2.1]at org.postgresql.core.ConnectionFactory.openConnection(ConnectionFactory.java:49) [postgresql-42.2.1.jar:42.2.1]at org.postgresql.jdbc.PgConnection.<init>(PgConnection.java:195) [postgresql-42.2.1.jar:42.2.1]at org.postgresql.Driver.makeConnection(Driver.java:452) [postgresql-42.2.1.jar:42.2.1]at org.postgresql.Driver.connect(Driver.java:254) [postgresql-42.2.1.jar:42.2.1]at com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:117) [HikariCP-2.7.6.jar:na]at com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:123) [HikariCP-2.7.6.jar:na]at com.zaxxer.hikari.pool.PoolBase.newConnection(PoolBase.java:375) [HikariCP-2.7.6.jar:na]at com.zaxxer.hikari.pool.PoolBase.newPoolEntry(PoolBase.java:204) [HikariCP-2.7.6.jar:na]at com.zaxxer.hikari.pool.HikariPool.createPoolEntry(HikariPool.java:459) [HikariCP-2.7.6.jar:na]at com.zaxxer.hikari.pool.HikariPool.access$200(HikariPool.java:70) [HikariCP-2.7.6.jar:na]at com.zaxxer.hikari.pool.HikariPool$PoolEntryCreator.call(HikariPool.java:696) [HikariCP-2.7.6.jar:na]at com.zaxxer.hikari.pool.HikariPool$PoolEntryCreator.call(HikariPool.java:682) [HikariCP-2.7.6.jar:na]at java.util.concurrent.FutureTask.run(FutureTask.java:266) [na:1.8.0_71]at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_71]at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_71]at java.lang.Thread.run(Thread.java:745) [na:1.8.0_71]
复制代码

获取连接的源码

HikariCP-2.7.6-sources.jar!/com/zaxxer/hikari/pool/HikariPool.java

   /*** Get a connection from the pool, or timeout after the specified number of milliseconds.** @param hardTimeout the maximum time to wait for a connection from the pool* @return a java.sql.Connection instance* @throws SQLException thrown if a timeout occurs trying to obtain a connection*/public Connection getConnection(final long hardTimeout) throws SQLException{suspendResumeLock.acquire();final long startTime = currentTime();try {long timeout = hardTimeout;do {PoolEntry poolEntry = connectionBag.borrow(timeout, MILLISECONDS);if (poolEntry == null) {break; // We timed out... break and throw exception}final long now = currentTime();if (poolEntry.isMarkedEvicted() || (elapsedMillis(poolEntry.lastAccessed, now) > ALIVE_BYPASS_WINDOW_MS && !isConnectionAlive(poolEntry.connection))) {closeConnection(poolEntry, poolEntry.isMarkedEvicted() ? EVICTED_CONNECTION_MESSAGE : DEAD_CONNECTION_MESSAGE);timeout = hardTimeout - elapsedMillis(startTime);}else {metricsTracker.recordBorrowStats(poolEntry, startTime);return poolEntry.createProxyConnection(leakTaskFactory.schedule(poolEntry), now);}} while (timeout > 0L);metricsTracker.recordBorrowTimeoutStats(startTime);throw createTimeoutException(startTime);}catch (InterruptedException e) {Thread.currentThread().interrupt();throw new SQLException(poolName + " - Interrupted during connection acquisition", e);}finally {suspendResumeLock.release();}}
复制代码

tomcat

2018-01-30 15:39:23.913  INFO 5894 --- [           main] com.example.JpaDemoApplicationTests      : execute:1
2018-01-30 15:39:33.939  INFO 5894 --- [           main] com.example.JpaDemoApplicationTests      : execute:2
2018-01-30 15:39:43.949  INFO 5894 --- [           main] com.example.JpaDemoApplicationTests      : execute:3
2018-01-30 15:39:53.955  INFO 5894 --- [           main] com.example.JpaDemoApplicationTests      : execute:4
2018-01-30 15:40:03.962  INFO 5894 --- [           main] com.example.JpaDemoApplicationTests      : execute:5
2018-01-30 15:40:13.968  INFO 5894 --- [           main] com.example.JpaDemoApplicationTests      : execute:6
复制代码

tomcat jdbc pool有testOnBorrow,testOnReturn,testOnConnect,testWhileIdle属性来配置什么时候检测连接,如果是testOnBorrow的话,有空闲连接则进行borrow同时进行validate,如果上一次validate的时间在validation interval内,则默认validate成功,否则进行validate,validate失败会进行reconnect,再validate,如果再失败则抛出new SQLException("Failed to validate a newly established connection.")。如果没有空闲连接,且连接池没满就创建一个新的;如果没有空闲连接且连接池满了,则while轮询空闲队列,如果没取到连接,若没超过maxWait则继续,超过则抛出PoolExhaustedException。如果没有空闲连接,且连接池满了,则borrow超时时间为maxWait默认30秒。

这个示例程序,每次循环都会先归还连接再sleep,因此连接池始终有空闲连接,只是testOnBorrow校验不成功,进行了reconnect,在reconnect抛出了异常

reconnect抛出的异常

org.postgresql.util.PSQLException: Connection to 192.168.99.100:5432 refused. Check that the hostname and port are correct and that the postmaster is accepting TCP/IP connections.at org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl(ConnectionFactoryImpl.java:262)at org.postgresql.core.ConnectionFactory.openConnection(ConnectionFactory.java:51)at org.postgresql.jdbc.PgConnection.<init>(PgConnection.java:215)at org.postgresql.Driver.makeConnection(Driver.java:404)at org.postgresql.Driver.connect(Driver.java:272)at org.apache.tomcat.jdbc.pool.PooledConnection.connectUsingDriver(PooledConnection.java:310)at org.apache.tomcat.jdbc.pool.PooledConnection.connect(PooledConnection.java:203)at org.apache.tomcat.jdbc.pool.PooledConnection.reconnect(PooledConnection.java:361)at org.apache.tomcat.jdbc.pool.ConnectionPool.borrowConnection(ConnectionPool.java:821)at org.apache.tomcat.jdbc.pool.ConnectionPool.borrowConnection(ConnectionPool.java:651)at org.apache.tomcat.jdbc.pool.ConnectionPool.getConnection(ConnectionPool.java:198)at org.apache.tomcat.jdbc.pool.DataSourceProxy.getConnection(DataSourceProxy.java:132)at com.example.JpaDemoApplicationTests.testDatabaseDownAndUp(JpaDemoApplicationTests.java:296)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.lang.reflect.Method.invoke(Method.java:497)at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252)at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)at org.junit.runners.ParentRunner.run(ParentRunner.java:363)at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)at org.junit.runner.JUnitCore.run(JUnitCore.java:137)at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234)at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.lang.reflect.Method.invoke(Method.java:497)at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Caused by: java.net.ConnectException: Connection refusedat java.net.PlainSocketImpl.socketConnect(Native Method)at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)at java.net.Socket.connect(Socket.java:589)at org.postgresql.core.PGStream.<init>(PGStream.java:61)at org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl(ConnectionFactoryImpl.java:144)... 44 more
复制代码

获取连接的源码

tomcat-jdbc-8.5.23-sources.jar!/org/apache/tomcat/jdbc/pool/ConnectionPool.java

    /*** Thread safe way to retrieve a connection from the pool* @param wait - time to wait, overrides the maxWait from the properties,* set to -1 if you wish to use maxWait, 0 if you wish no wait time.* @param username The user name to use for the connection* @param password The password for the connection* @return a connection* @throws SQLException Failed to get a connection*/private PooledConnection borrowConnection(int wait, String username, String password) throws SQLException {if (isClosed()) {throw new SQLException("Connection pool closed.");} //end if//get the current time stamplong now = System.currentTimeMillis();//see if there is one available immediatelyPooledConnection con = idle.poll();while (true) {if (con!=null) {//configure the connection and return itPooledConnection result = borrowConnection(now, con, username, password);borrowedCount.incrementAndGet();if (result!=null) return result;}//if we get here, see if we need to create one//this is not 100% accurate since it doesn't use a shared//atomic variable - a connection can become idle while we are creating//a new connectionif (size.get() < getPoolProperties().getMaxActive()) {//atomic duplicate checkif (size.addAndGet(1) > getPoolProperties().getMaxActive()) {//if we got here, two threads passed through the first ifsize.decrementAndGet();} else {//create a connection, we're below the limitreturn createConnection(now, con, username, password);}} //end if//calculate wait time for this iterationlong maxWait = wait;//if the passed in wait time is -1, means we should use the pool property valueif (wait==-1) {maxWait = (getPoolProperties().getMaxWait()<=0)?Long.MAX_VALUE:getPoolProperties().getMaxWait();}long timetowait = Math.max(0, maxWait - (System.currentTimeMillis() - now));waitcount.incrementAndGet();try {//retrieve an existing connectioncon = idle.poll(timetowait, TimeUnit.MILLISECONDS);} catch (InterruptedException ex) {if (getPoolProperties().getPropagateInterruptState()) {Thread.currentThread().interrupt();}SQLException sx = new SQLException("Pool wait interrupted.");sx.initCause(ex);throw sx;} finally {waitcount.decrementAndGet();}if (maxWait==0 && con == null) { //no wait, return one if we have oneif (jmxPool!=null) {jmxPool.notify(org.apache.tomcat.jdbc.pool.jmx.ConnectionPool.POOL_EMPTY, "Pool empty - no wait.");}throw new PoolExhaustedException("[" + Thread.currentThread().getName()+"] " +"NoWait: Pool empty. Unable to fetch a connection, none available["+busy.size()+" in use].");}//we didn't get a connection, lets see if we timed outif (con == null) {if ((System.currentTimeMillis() - now) >= maxWait) {if (jmxPool!=null) {jmxPool.notify(org.apache.tomcat.jdbc.pool.jmx.ConnectionPool.POOL_EMPTY, "Pool empty - timeout.");}throw new PoolExhaustedException("[" + Thread.currentThread().getName()+"] " +"Timeout: Pool empty. Unable to fetch a connection in " + (maxWait / 1000) +" seconds, none available[size:"+size.get() +"; busy:"+busy.size()+"; idle:"+idle.size()+"; lastwait:"+timetowait+"].");} else {//no timeout, lets try againcontinue;}}} //while}
复制代码

小结

如果是没有空闲连接且连接池满不能新建连接的情况下

  • hikari则是阻塞connectionTimeout的时间,没有得到连接抛出SQLTransientConnectionException
  • tomcat jdbc pool会阻塞max-wait时间,若没有得到连接则抛出PoolExhaustedException

如果是有空闲连接的情况

  • hikari是在connectionTimeout时间内不断循环获取下一个空闲连接进行校验,校验失败继续获取下一个空闲连接,直到超时抛出SQLTransientConnectionException
  • tomcat jdbc pool会borrow连接然后validate连接,正常validate失败会reconnect,再validate,若此时reconnect不成功则抛出ConnectException: Connection refused,如果reconnect成功但是validate失败,则抛出SQLException("Failed to validate a newly established connection.")

假设数据库是挂的,但还有空闲连接,tomcat的testOnBorrow=true。对比可以发现

hikari在获取一个连接的时候,会在connectionTimeout时间内循环把空闲连接挨个validate一次,最后timeout抛出异常;之后的获取连接操作,则一直阻塞connectionTimeout时间再抛出异常

tomcat jdbc pool则是在获取一个连接的时候,先borrow出来连接,正常validate失败会reconnect,再validate,通常reconnect的时候抛出了ConnectException: Connection refused,然后业务程序会fail fast,之后的获取连接也都是fail fast的

由此可见,hikari如果connectionTimeout如果设置太大的话,在数据库挂的时候,很容易阻塞业务线程,而tomcat jdbc pool则会fail fast。

另外hikari是异步去建立minIdle大小的连接,而tomcat是同步的建立initial-size的连接。

聊聊hikari与tomcat jdbc pool的fail fast相关推荐

  1. 聊聊tomcat jdbc pool的默认参数及poolSweeper

    序 本文主要研究一下tomcat jdbc pool的默认参数及poolSweeper tomcat jdbc pool 参数默认值 initialSize = 10(默认值) maxActive=1 ...

  2. tomcat7 mysql 连接池_Tomcat7 新的数据库连接池Tomcat jdbc pool介绍和配置

    Tomcat 在 7.0 以前的版本都是使用commons-dbcp做为连接池的实现,但是 dbcp存在一些问题: (1)dbcp 是单线程的,为了保证线程安全会锁整个连接池 (2)dbcp 性能不佳 ...

  3. Tomcat JDBC Pool使用说明

    Maven依赖 <dependency><groupId>org.apache.tomcat</groupId><artifactId>tomcat-j ...

  4. php 连接池 idletime,聊聊hikari连接池的idleTimeout及minimumIdle属性

    序 本文主要研究一个hikari连接池的idleTimeout及minimumIdle属性 idleTimeout 默认是600000毫秒,即10分钟.如果idleTimeout+1秒>maxL ...

  5. tomcat jdbc连接池的suspect、abandon操作解析

    为什么80%的码农都做不了架构师?>>>    ##Connection has been marked suspect Connection has been marked sus ...

  6. Tomcat JDBC池–连接泄漏–捕获罪魁祸首

    数据库连接泄漏是可以隐藏的东西,除非特别注意,否则将在系统高峰期最关键的阶段暴露出来. 我们将手动检查所有打开的连接是否已正确关闭. 然后,我们将提供各种代码质量插件来进行扫描和检查. 当连接通过复杂 ...

  7. 在独立Java应用程序中使用Tomcat JDBC连接池

    这是从我们的客人文章W4G伙伴克拉伦斯豪的作者临春3从A按. 您可能会在文章结尾找到本书的折扣券代码,仅适用于Java Code Geeks的读者! 请享用! 在需要数据访问权限的独立Java应用程序 ...

  8. tomcat jdbc SlowQueryReport的实现解读

    为什么80%的码农都做不了架构师?>>>    ##序 tomcat提供了JdbcInterceptor可以用来监控jdbc的执行情况,默认提供了好几个现成的interceptor可 ...

  9. C3P0数据源和Tomcat jdbc数据源的基本配置

    在项目中我们经常需要使用数据源,数据源存储所有建立数据库连接的信息.就象通过指定文件名你可以在文件系统中找到文件一样,通过提供正确的数据源名称,你可以找到相应的数据库连接.下面分别对C3P0数据源和T ...

最新文章

  1. 剑指Offer 替换空格
  2. int **a[3][4] 和 sizeof(a) 和 int(**)a[3][4]
  3. LabviewRS232串口通信数据格式问题解析
  4. python程序多次运行_[Python]在一段Python程序中使用多次事件循环
  5. Flutter 找不到 android sdk(图文详解)
  6. 神策 2020 数据驱动用户大会:新愿景 + 新定位 + 新舰队正式亮相!
  7. XHTML行内描述性元素(持续更新中)
  8. python雪花算法生成id_理解分布式id生成算法SnowFlake
  9. 《Redis官方文档》Data types—数据类型
  10. Java8新特性教程 - 终极指南
  11. (转)基于Metronic的Bootstrap开发框架经验总结(7)--数据的导入、导出及附件的查看处理...
  12. linux的can通信busoff,socketCAN内核源码分析是否支持busoff自恢复--Apple的学习笔记
  13. php常见web安全问题,web安全面试常见问题(来自微博)
  14. jquery显示与隐藏效果
  15. 在GlassFish应用服务器上创建并运行你的第一个Restful Web Service【翻译】
  16. 一位全减器逻辑电路图_一种一位全减器电路的制作方法
  17. 中文图片验证码程序。
  18. KVM虚拟机,使用linux bridge配置vlan隔离
  19. js中如何访问对象和数组
  20. yii mysql gii_yii框架之gii的使用

热门文章

  1. QT每日一练day14:QFontDialog字体对话框
  2. node 后台重定向_node.js – 使用NodeJS重定向客户端并重新定义
  3. mysql循环建表_MySQL 开发准则(总结自阿里巴巴开发手册)
  4. 太阳直射点纬度计算公式_高中地理,常用计算公式大盘点,高中满分特辑!
  5. cnn 回归 坐标 特征图_论文笔记 | CNN 是怎么学到图片绝对位置信息的
  6. python ant_('python,-ANT--编程字典',)
  7. java jexl_一种表达式语言的解析引擎JEXL简单使用
  8. c 调用c语言头文件,混合使用C、C++和汇编语之:在C++中使用C头文件-嵌入式系统-与非网...
  9. python面部颜色分析_Python图像处理之颜色的定义与使用分析
  10. vue 插槽 有名插槽