本文主要研究一下hikari鏈接池的isAllowPoolSuspension屬性java
@Test public void testPoolSuspend() throws SQLException { Connection conn = dataSource.getConnection(); System.out.println("begin to suspend pool"); ((HikariDataSource)dataSource).suspendPool(); Connection conn2 = dataSource.getConnection(); System.out.println("finish"); }
begin to suspend pool java.lang.IllegalStateException: HikariPool-1 - is not suspendable at com.zaxxer.hikari.pool.HikariPool.suspendPool(HikariPool.java:372) at com.zaxxer.hikari.HikariDataSource.suspendPool(HikariDataSource.java:326) at com.example.demo.HikariDemoApplicationTests.testPoolSuspend(HikariDemoApplicationTests.java:37) 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)
當沒有設置的allow-pool-suspension=true時候,拋出java.lang.IllegalStateException: HikariPool-1 - is not suspendable
HikariCP-2.7.6-sources.jar!/com/zaxxer/hikari/pool/HikariPool.javagit
/** * Construct a HikariPool with the specified configuration. * * @param config a HikariConfig instance */ public HikariPool(final HikariConfig config) { super(config); this.connectionBag = new ConcurrentBag<>(this); this.suspendResumeLock = config.isAllowPoolSuspension() ? new SuspendResumeLock() : SuspendResumeLock.FAUX_LOCK; this.houseKeepingExecutorService = initializeHouseKeepingExecutorService(); checkFailFast(); if (config.getMetricsTrackerFactory() != null) { setMetricsTrackerFactory(config.getMetricsTrackerFactory()); } else { setMetricRegistry(config.getMetricRegistry()); } setHealthCheckRegistry(config.getHealthCheckRegistry()); registerMBeans(this); ThreadFactory threadFactory = config.getThreadFactory(); LinkedBlockingQueue<Runnable> addConnectionQueue = new LinkedBlockingQueue<>(config.getMaximumPoolSize()); this.addConnectionQueue = unmodifiableCollection(addConnectionQueue); this.addConnectionExecutor = createThreadPoolExecutor(addConnectionQueue, poolName + " connection adder", threadFactory, new ThreadPoolExecutor.DiscardPolicy()); this.closeConnectionExecutor = createThreadPoolExecutor(config.getMaximumPoolSize(), poolName + " connection closer", threadFactory, new ThreadPoolExecutor.CallerRunsPolicy()); this.leakTaskFactory = new ProxyLeakTaskFactory(config.getLeakDetectionThreshold(), houseKeepingExecutorService); this.houseKeeperTask = houseKeepingExecutorService.scheduleWithFixedDelay(new HouseKeeper(), 100L, HOUSEKEEPING_PERIOD_MS, MILLISECONDS); }
若是isAllowPoolSuspension爲ture,則suspendResumeLock是一個真實的SuspendResumeLock,不然是SuspendResumeLock.FAUX_LOCK
/** * This class implements a lock that can be used to suspend and resume the pool. It * also provides a faux implementation that is used when the feature is disabled that * hopefully gets fully "optimized away" by the JIT. * * @author Brett Wooldridge */ public class SuspendResumeLock { public static final SuspendResumeLock FAUX_LOCK = new SuspendResumeLock(false) { @Override public void acquire() {} @Override public void release() {} @Override public void suspend() {} @Override public void resume() {} }; private static final int MAX_PERMITS = 10000; private final Semaphore acquisitionSemaphore; /** * Default constructor */ public SuspendResumeLock() { this(true); } private SuspendResumeLock(final boolean createSemaphore) { acquisitionSemaphore = (createSemaphore ? new Semaphore(MAX_PERMITS, true) : null); } public void acquire() { acquisitionSemaphore.acquireUninterruptibly(); } public void release() { acquisitionSemaphore.release(); } public void suspend() { acquisitionSemaphore.acquireUninterruptibly(MAX_PERMITS); } public void resume() { acquisitionSemaphore.release(MAX_PERMITS); } }
FAUX_LOCK是一個空方法,false表示不建立信號量suspend方法一次性消耗了MAX_PERMITS信號量,這個方法被調用以後,以後getConnection方法都獲取不到鏈接,永遠阻塞在suspendResumeLock.acquire();github
resume方法一次性釋放MAX_PERMITS信號量spring
HikariCP-2.7.6-sources.jar!/com/zaxxer/hikari/pool/HikariPool.javasql
/** * Get a connection from the pool, or timeout after connectionTimeout milliseconds. * * @return a java.sql.Connection instance * @throws SQLException thrown if a timeout occurs trying to obtain a connection */ public Connection getConnection() throws SQLException { return getConnection(connectionTimeout); } /** * 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(); } }
能夠看到getConnection是先獲取信號量,最後無論獲取成功仍是超時,finally裏頭去釋放這個信號量
這裏的hardTimeout就是從鏈接池借用connection的超時時間
isAllowPoolSuspension用來標記釋放容許暫停鏈接池,一旦被暫停,全部的getConnection方法都會被阻塞。可能的用處就是用來實現chaosmonkey,模擬數據庫鏈接故障。做者在github上的解釋是能夠用來作些配置修改:暫停鏈接池,修改鏈接池配置或dns配置,soft-evit既有鏈接,而後恢復鏈接池。數據庫