从连接的视角来看类图
1.WrappedConnection直接实现了Connection接口,对外的包装类
2.BaseWrapperManagedConnection,包装了底层的物理连接,实现了连接获取,销毁等方法,psCache也是这里实现
3.BaseConnectionEventListener,和BaseWrapperManagedConnection互相引用,数据源内部实现并发控制的实体单元
4.InternalManagedConnectionPool,获取连接的并发控制地方,维护着connectionListener的列表和一个Semaphore信号量以实现最大连接数控制
连接获取过程
WrapperDataSource入口
public Connection getConnection() throws SQLException { try { WrappedConnection wc = (WrappedConnection) cm.allocateConnection(mcf, null); wc.setDataSource(this); wc.setZdatasource(zdatasource); return wc; } catch (ResourceException re) { ...... }
allocateConnection操作
public Object allocateConnection(ManagedConnectionFactory mcf, ConnectionRequestInfo cri) throws ResourceException { .... // Pick a managed connection from the pool Subject subject = getSubject(); //创建ConnectionListener ConnectionListener cl = getManagedConnection(subject, cri); // Tell each connection manager the managed connection is active reconnectManagedConnection(cl); // Ask the managed connection for a connection Object connection = null; //创建WrappedConnection try { connection = cl.getManagedConnection().getConnection(subject, cri); } catch (Throwable t) { managedConnectionDisconnected(cl); JBossResourceException.rethrowAsResourceException( "Unchecked throwable in ManagedConnection.getConnection() cl=" + cl, t); } // Associate managed connection with the connection //关联WrappedConnection和ConnectionListener registerAssociation(cl, connection); ..... return connection;
创建ConnectionListener过程
public ConnectionListener getConnection(Transaction trackByTransaction, Subject subject, ConnectionRequestInfo cri) throws ResourceException { // Determine the pool key for this request boolean separateNoTx = false; if (noTxSeparatePools) separateNoTx = clf.isTransactional(); Object key = getKey(subject, cri, separateNoTx); SubPoolContext subPool = getSubPool(key, subject, cri, poolName); //初始化连接池 InternalManagedConnectionPool mcp = subPool.getSubPool(); // Are we doing track by connection? TransactionLocal trackByTx = subPool.getTrackByTx(); // Simple case //非事务,直接拿连接 if (trackByTransaction == null || trackByTx == null) { ConnectionListener cl = mcp.getConnection(subject, cri); if (traceEnabled) dump("Got connection from pool " + cl); return cl; } .... }
拿连接过程
public ConnectionListener getConnection(Subject subject, ConnectionRequestInfo cri) throws ResourceException { subject = (subject == null) ? defaultSubject : subject; cri = (cri == null) ? defaultCri : cri; long startWait = System.currentTimeMillis(); try { //获取锁,blockingTimeout超时 if (permits.tryAcquire(poolParams.blockingTimeout, TimeUnit.MILLISECONDS)) { //We have a permit to get a connection. Is there one in the pool already? ConnectionListener cl = null; //从缓存的连接中拿连接 do { synchronized (connectionListeners) { if (shutdown.get()) { permits.release(); throw new ResourceException("The pool has been shutdown"); } //如果缓存中有,则取之,并添加到checkedOut记录中,更新maxUsedConnections数 if (connectionListeners.size() > 0) { cl = (ConnectionListener) connectionListeners .remove(connectionListeners.size() - 1); checkedOut.add(cl); int size = (maxSize - permits.availablePermits()); //Update the maxUsedConnections if (size > maxUsedConnections) maxUsedConnections = size; } } //缓存中拿到了连接,进行校验 if (cl != null) { //Yes, we retrieved a ManagedConnection from the pool. Does it match? try { Object matchedMC = mcf.matchManagedConnections(Collections.singleton(cl .getManagedConnection()), subject, cri); //校验通过,返回连接 if (matchedMC != null) { if (log.isDebugEnabled()) { log.debug("supplying ManagedConnection from pool: " + cl); } cl.grantPermit(true); return cl; } //校验不通过,销毁连接,继续找下一个连接 //Match did not succeed but no exception was thrown. //Either we have the matching strategy wrong or the //connection died while being checked. We need to //distinguish these cases, but for now we always //destroy the connection. log .warn("Destroying connection that could not be successfully matched: " + cl); synchronized (connectionListeners) { checkedOut.remove(cl); } doDestroy(cl, "getConnection"); cl = null; } catch (Throwable t) { ..... } } while (connectionListeners.size() > 0);//end of do loop //OK, we couldnt find a working connection from the pool. Make a new one. //从缓存中拿不到连接,则创建新的,并放入缓存 try { //No, the pool was empty, so we have to make a new one. cl = createConnectionEventListener(subject, cri); //添加到checkOut中,returnConnection的时候再添加到connectionListeners中 synchronized (connectionListeners) { checkedOut.add(cl); int size = (maxSize - permits.availablePermits()); if (size > maxUsedConnections) maxUsedConnections = size; } //lack of synch on "started" probably ok, if 2 reads occur we will just //run fillPool twice, no harm done. //第一次启动的时候,进行最小连接数的预热 if (started == false) { started = true; if (poolParams.minSize > 0) PoolFiller.fillPool(this); } if (log.isDebugEnabled()) { log.debug("supplying new ManagedConnection: " + cl); } cl.grantPermit(true); return cl; } catch (Throwable t) { log.warn("Throwable while attempting to get a new connection: " + cl, t); //return permit and rethrow synchronized (connectionListeners) { checkedOut.remove(cl); } permits.release(); JBossResourceException.rethrowAsResourceException( "Unexpected throwable while trying to create a connection: " + cl, t); throw new UnreachableStatementException(); } } else { //获取连接失败,抛出异常 if (this.maxSize == 0) {// 如果最大连接数为0,则说明db处于不可用状态 throw new ResourceException("当前数据库处于不可用状态,poolName = " + poolName, ZConstants.ERROR_CODE_DB_NOT_AVAILABLE); } else if (this.maxSize == this.maxUsedConnections) { throw new ResourceException("数据源最大连接数已满,并且在超时时间范围内没有新的连接释放,poolName = " + poolName + " blocking timeout=" + poolParams.blockingTimeout + "(ms),now-time = " + sdf.format(new Date()), ZConstants.ERROR_CODE_CONNECTION_NOT_AVAILABLE); } else {// 属于超时 throw new ResourceException( "No ManagedConnections available within configured blocking timeout ( " + poolParams.blockingTimeout + " [ms] ),the poolName = " + poolName, ZConstants.ERROR_CODE_CONNECTION_TIMEOUT); } } } catch (InterruptedException ie) { long end = System.currentTimeMillis() - startWait; throw new ResourceException("Interrupted while requesting permit! Waited " + end + " ms", ie); } }
获取物理连接过程
public ManagedConnection createManagedConnection(Subject subject, ConnectionRequestInfo cri) throws com.alipay.zdal.datasource.resource.ResourceException { //配置的链接参数 Properties props = getConnectionProperties(subject, cri); // Some friendly drivers (Oracle, you guessed right) modify the props you supply. // Since we use our copy to identify compatibility in matchManagedConnection, we need // a pristine copy for our own use. So give the friendly driver a copy. Properties copy = (Properties) props.clone(); if (log.isDebugEnabled()) { // Make yet another copy to mask the password Properties logCopy = copy; if (copy.getProperty("password") != null) { logCopy = (Properties) props.clone(); logCopy.setProperty("password", "--hidden--"); } log.debug("Using properties: " + logCopy); } try { //从driver获取真实连接 String url = getConnectionURL(); Driver d = getDriver(url); Connection con = d.connect(url, copy); if (con == null) { throw new JBossResourceException("Wrong driver class for this connection URL"); } String stz = copy.getProperty("sessionTimeZone");//支持oracle-driver中设置timestamp字段的属性. if (stz != null && stz.trim().length() > 0 && (con instanceof oracle.jdbc.OracleConnection)) { ((oracle.jdbc.OracleConnection) con).setSessionTimeZone(stz); } return new LocalManagedConnection(this, con, props, transactionIsolation, preparedStatementCacheSize); } catch (Exception e) { throw new JBossResourceException("Could not create connection,the url = " + getConnectionURL(), e); } }
以上就是获取连接的过程。接下来看以下,归还连接的过程
入口WrappedConnection.close方法
public void close() throws SQLException { closed = true; if (mc != null) { ..... //通过ManagedConnection来关闭 mc.closeHandle(this); } //释放引用,方便gc mc = null; dataSource = null; }
BaseWrapperManagedConnection释放连接
void closeHandle(WrappedConnection handle) { synchronized (stateLock) { if (destroyed) return; } //把WrappedConnection引用释放 synchronized (handles) { handles.remove(handle); } //发送CLOSED事件 ConnectionEvent ce = new ConnectionEvent(this, ConnectionEvent.CONNECTION_CLOSED); ce.setConnectionHandle(handle); Collection copy = null; synchronized (cels) { copy = new ArrayList(cels); } //通知连接池里的onnectionEventListener进行连接释放 for (Iterator i = copy.iterator(); i.hasNext();) { ConnectionEventListener cel = (ConnectionEventListener) i.next(); cel.connectionClosed(ce); } }
连接池内部释放连接,TxConnectionEventListener的connectionClosed方法
public void connectionClosed(ConnectionEvent ce) { ...... try { //释放对WrappedConnection的引用 unregisterAssociation(this, ce.getConnectionHandle()); boolean isFree = isManagedConnectionFree(); //no more handles //没有外部引用,则归还连接 if (isFree) { delist(); returnManagedConnection(this, false); } } catch (Throwable t) { log.error("Error while closing connection handle!", t); returnManagedConnection(this, true); } }
InternalManagedConnectionPool归还过程
public void connectionClosed(ConnectionEvent ce) { ...... try { //释放对WrappedConnection的引用 unregisterAssociation(this, ce.getConnectionHandle()); boolean isFree = isManagedConnectionFree(); //no more handles //没有外部引用,则归还连接 if (isFree) { delist(); returnManagedConnection(this, false); } } catch (Throwable t) { log.error("Error while closing connection handle!", t); returnManagedConnection(this, true); } } InternalManagedConnectionPool归还过程 public void returnConnection(ConnectionListener cl, boolean kill) { ...... //清理ManagedConnection里对外部WrappedConnection的引用 try { cl.getManagedConnection().cleanup(); } catch (ResourceException re) { log.warn("ResourceException cleaning up ManagedConnection: " + cl, re); kill = true; } // We need to destroy this one if (cl.getState() == ConnectionListener.DESTROY) kill = true; synchronized (connectionListeners) { //checkOut里删除 checkedOut.remove(cl); // This is really an error if (kill == false && connectionListeners.size() >= poolParams.maxSize) { log.warn("Destroying returned connection, maximum pool size exceeded " + cl); kill = true; } // If we are destroying, check the connection is not in the pool //如果是销毁连接,则删除之 if (kill) { // Adrian Brock: A resource adapter can asynchronously notify us that // a connection error occurred. // This could happen while the connection is not checked out. // e.g. JMS can do this via an ExceptionListener on the connection. // I have twice had to reinstate this line of code, PLEASE DO NOT REMOTE IT! connectionListeners.remove(cl); } // return to the pool //不销毁,重新添加到缓存中 else { cl.used(); connectionListeners.add(cl); } //重置状态为未使用,归还信号量 if (cl.hasPermit()) { // release semaphore cl.grantPermit(false); permits.release(); } } //销毁连接 if (kill) { if (trace) if (log.isDebugEnabled()) { log.debug("Destroying returned connection " + cl); } doDestroy(cl, "returnConnection"); } }
连接销毁
public void destroy() throws ResourceException { synchronized (stateLock) { destroyed = true; } cleanup(); try { con.close(); } catch (SQLException ignored) { getLog().error("Ignored error during close: ", ignored); } }