After the Connection in Druid is used, it needs to be recycled, and the method of recycling the Connection is the recover method. The main purpose of recycling is to clear / reset the connection status, place it at the end of the connections array of the connection pool, and then send the notEmpty condition variable notification message of the connection pool lock to let the waiting consumer thread obtain the connection.
1. Recovery process
The first thing to do in the recycling method is to judge whether the recycling thread is the same thread. If not, print the log output.
if (logDifferentThread // && (!isAsyncCloseConnectionEnable()) // && pooledConnection.ownerThread != Thread.currentThread()// ) { LOG.warn("get/close not same thread"); }
This log level is warn, which is what we should pay special attention to when using the connection pool. Generally speaking, only when the connection pool has a connection leak and the thread using the connection holds the connection for a long time without performing specific operations, the thread monitoring the connection leak will close the connection. This situation requires special attention. In addition, the removeAbandoned mechanism connected to the leak monitoring mechanism will also call the recycle method for recycling.
pooledConnection.traceEnable and activeConnections are parameters related to the removeabanded mechanism, which will be analyzed in detail in the subsequent removeabanded mechanism.
if (pooledConnection.traceEnable) { Object oldInfo = null; activeConnectionLock.lock(); try { if (pooledConnection.traceEnable) { //Remove the connection from active connections. Considering the multithreading scenario, lock it oldInfo = activeConnections.remove(pooledConnection); pooledConnection.traceEnable = false; } } finally { activeConnectionLock.unlock(); } if (oldInfo == null) { if (LOG.isWarnEnabled()) { LOG.warn("remove abandonded failed. activeConnections.size " + activeConnections.size()); } } }
Rollback processing: if the connection is not automatically commit ted or read-only, the rollback operation shall be performed first when recycling.
// check need to rollback? if ((!isAutoCommit) && (!isReadOnly)) { pooledConnection.rollback(); }
reset processing: you need to judge whether it is a multi-threaded scenario. If it is not the same thread, because the thread connecting itself may call recycle when it is released after calling, so multi threading needs to be considered in this place.
// reset holder, restore default settings, clear warnings boolean isSameThread = pooledConnection.ownerThread == Thread.currentThread(); if (!isSameThread) { final ReentrantLock lock = pooledConnection.lock; lock.lock(); try { holder.reset(); } finally { lock.unlock(); } } else { holder.reset(); }
The most critical part is the reset method:
//Empty Listeners connectionEventListeners.clear(); statementEventListeners.clear(); lock.lock(); try { for (Object item : statementTrace.toArray()) { Statement stmt = (Statement) item; //Close the statement JdbcUtils.close(stmt); } //Empty statementTrace statementTrace.clear(); } finally { lock.unlock(); } //Clear warning information conn.clearWarnings();
The reset method closes all the statement s in the connection and clears the related alarm light contents.
Close connection processing:
//If the status is discard, exit directly if (holder.discard) { return; } //If the maximum number of uses of the connection is exceeded, the connection will also be closed if (phyMaxUseCount > 0 && holder.useCount >= phyMaxUseCount) { discardConnection(holder); return; }
If the status here is udicard, you can exit the recycle method directly. It will be discard ed in subsequent methods. If the maximum number of calls is reached, the connection will also be closed.
Close Cleanup: If the connection is closed, lock it and subtract the counter.
if (physicalConnection.isClosed()) { lock.lock(); try { if (holder.active) { activeCount--; holder.active = false; } closeCount++; } finally { lock.unlock(); } return; }
testOnReturn processing: If testOnReturn is enabled, test data will be sent. If the test fails, the connection will be closed.
if (testOnReturn) { boolean validate = testConnectionInternal(holder, physicalConnection); if (!validate) { JdbcUtils.close(physicalConnection); destroyCountUpdater.incrementAndGet(this); lock.lock(); try { if (holder.active) { activeCount--; holder.active = false; } closeCount++; } finally { lock.unlock(); } return; } }
If druid.phyTimeoutMillis is configured, the connection timeout detection is required during recycling:
if (phyTimeoutMillis > 0) { long phyConnectTimeMillis = currentTimeMillis - holder.connectTimeMillis; if (phyConnectTimeMillis > phyTimeoutMillis) { discardConnection(holder); return; } }
The most critical code:
lock.lock(); try { //Modify active status and activeCount counters if (holder.active) { activeCount--; holder.active = false; } //Increase closeCount counter closeCount++; //Place the join at the end of the array result = putLast(holder, currentTimeMillis); recycleCount++; } finally { lock.unlock(); }
This is the core code of connection recycling, which is to place the connection at the end of the array. notEmpty.signal() will be called in the putLast method; In this way, the consumer thread will generate a call.
2. Call timing of recycle
The recycle method will be called in the close method of the connection. When the connection needs to be closed, call recycle to add the qualified connection to the end of the connection pool. Method to call recycle:
public void close() throws SQLException ; public void syncClose() throws SQLException ;
Both the close and syncClose methods call recycle. Before closing the connection, the filter processing of the responsibility chain mode is the same as obtaining the connection.
if (filters.size() > 0) { FilterChainImpl filterChain = new FilterChainImpl(dataSource); filterChain.dataSource_recycle(this); } else { recycle(); }
This process is similar to that of the getConnection method executing the filter.