聊聊hikari与tomcat jdbc pool的fail fast
序
本文主要研究在中途数据库挂的情况下,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相关推荐
- 聊聊tomcat jdbc pool的默认参数及poolSweeper
序 本文主要研究一下tomcat jdbc pool的默认参数及poolSweeper tomcat jdbc pool 参数默认值 initialSize = 10(默认值) maxActive=1 ...
- tomcat7 mysql 连接池_Tomcat7 新的数据库连接池Tomcat jdbc pool介绍和配置
Tomcat 在 7.0 以前的版本都是使用commons-dbcp做为连接池的实现,但是 dbcp存在一些问题: (1)dbcp 是单线程的,为了保证线程安全会锁整个连接池 (2)dbcp 性能不佳 ...
- Tomcat JDBC Pool使用说明
Maven依赖 <dependency><groupId>org.apache.tomcat</groupId><artifactId>tomcat-j ...
- php 连接池 idletime,聊聊hikari连接池的idleTimeout及minimumIdle属性
序 本文主要研究一个hikari连接池的idleTimeout及minimumIdle属性 idleTimeout 默认是600000毫秒,即10分钟.如果idleTimeout+1秒>maxL ...
- tomcat jdbc连接池的suspect、abandon操作解析
为什么80%的码农都做不了架构师?>>> ##Connection has been marked suspect Connection has been marked sus ...
- Tomcat JDBC池–连接泄漏–捕获罪魁祸首
数据库连接泄漏是可以隐藏的东西,除非特别注意,否则将在系统高峰期最关键的阶段暴露出来. 我们将手动检查所有打开的连接是否已正确关闭. 然后,我们将提供各种代码质量插件来进行扫描和检查. 当连接通过复杂 ...
- 在独立Java应用程序中使用Tomcat JDBC连接池
这是从我们的客人文章W4G伙伴克拉伦斯豪的作者临春3从A按. 您可能会在文章结尾找到本书的折扣券代码,仅适用于Java Code Geeks的读者! 请享用! 在需要数据访问权限的独立Java应用程序 ...
- tomcat jdbc SlowQueryReport的实现解读
为什么80%的码农都做不了架构师?>>> ##序 tomcat提供了JdbcInterceptor可以用来监控jdbc的执行情况,默认提供了好几个现成的interceptor可 ...
- C3P0数据源和Tomcat jdbc数据源的基本配置
在项目中我们经常需要使用数据源,数据源存储所有建立数据库连接的信息.就象通过指定文件名你可以在文件系统中找到文件一样,通过提供正确的数据源名称,你可以找到相应的数据库连接.下面分别对C3P0数据源和T ...
最新文章
- 剑指Offer 替换空格
- int **a[3][4] 和 sizeof(a) 和 int(**)a[3][4]
- LabviewRS232串口通信数据格式问题解析
- python程序多次运行_[Python]在一段Python程序中使用多次事件循环
- Flutter 找不到 android sdk(图文详解)
- 神策 2020 数据驱动用户大会:新愿景 + 新定位 + 新舰队正式亮相!
- XHTML行内描述性元素(持续更新中)
- python雪花算法生成id_理解分布式id生成算法SnowFlake
- 《Redis官方文档》Data types—数据类型
- Java8新特性教程 - 终极指南
- (转)基于Metronic的Bootstrap开发框架经验总结(7)--数据的导入、导出及附件的查看处理...
- linux的can通信busoff,socketCAN内核源码分析是否支持busoff自恢复--Apple的学习笔记
- php常见web安全问题,web安全面试常见问题(来自微博)
- jquery显示与隐藏效果
- 在GlassFish应用服务器上创建并运行你的第一个Restful Web Service【翻译】
- 一位全减器逻辑电路图_一种一位全减器电路的制作方法
- 中文图片验证码程序。
- KVM虚拟机,使用linux bridge配置vlan隔离
- js中如何访问对象和数组
- yii mysql gii_yii框架之gii的使用
热门文章
- QT每日一练day14:QFontDialog字体对话框
- node 后台重定向_node.js – 使用NodeJS重定向客户端并重新定义
- mysql循环建表_MySQL 开发准则(总结自阿里巴巴开发手册)
- 太阳直射点纬度计算公式_高中地理,常用计算公式大盘点,高中满分特辑!
- cnn 回归 坐标 特征图_论文笔记 | CNN 是怎么学到图片绝对位置信息的
- python ant_('python,-ANT--编程字典',)
- java jexl_一种表达式语言的解析引擎JEXL简单使用
- c 调用c语言头文件,混合使用C、C++和汇编语之:在C++中使用C头文件-嵌入式系统-与非网...
- python面部颜色分析_Python图像处理之颜色的定义与使用分析
- vue 插槽 有名插槽