从连接的视角来看类图
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);
}
}