[Jboss数据源二]连接获取

从连接的视角来看类图

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过程

扫描二维码关注公众号,回复: 702762 查看本文章
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);
        }
    }

猜你喜欢

转载自iwinit.iteye.com/blog/1931662
今日推荐