Druid Read XA Connection Initialization

Keywords: Java Druid Spring

Catalog

1 Initialize DruidXADataSource

2 Initialize Atomikos DataSourceBean

1 Initialize DruidXADataSource

Initializing DruidXADataSource is done by the application developer as follows:

/**
     * jdbc.ds1 Get configuration information, initialize druidDataSource1
     * @return
     */
    @Bean(name="druidDataSource1")
    @ConfigurationProperties(prefix = "jdbc.ds1")
    public DruidXADataSource dataSource0(){
        DruidXADataSource dataSource = new DruidXADataSource();
        return dataSource;
    }

  Click on the DruidXADataSource source code to find that the DruidDataSource inherits, so according to @ConfigurationProperties(prefix = "jdbc.ds1"), the configuration information at the beginning of jdbc.ds1 under the application.properties configuration file will be read and the connection configuration of DruidDataSource will be initialized, with the following inheritance relationships:

public class DruidXADataSource extends DruidDataSource implements XADataSource {

}

  The debug observations are as follows:

2 Initialize Atomikos DataSourceBean

This is explained here using the X/Open DTP model. The model mentioned above includes four parts: application (AP), transaction manager (TM), resource manager (RM), and communication resource manager (CRM).

  • Application (AP) is our client program
  • The Transaction Manager (TM) is the role of the Tomikos tool that we introduced to help us coordinate the submission and rollback of RM's distributed transactions to ensure the consistency of distributed database data.
  • Resource Manager (RM) is equivalent to the management of each local dataSource, and Druid is mainly supported by this block
  • Communications Resource Manager (CRM) I understand that this is not covered here and do not explain

Next, let's go through the initialization code, take a new AtomikosDataSourceBean instance, and initialize DruidXADataSource to manage as a member object.

    /**
     * Instantiate AtomikosDataSourceBean and set Druid initialized RuidXADataSource
     * @param druidDataSource1
     * @return
     */
    @Primary
    @Bean(name = "dataSource1")
    public AtomikosDataSourceBean dataSource(@Qualifier("druidDataSource1") DruidXADataSource druidDataSource1){
        AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();

        try {
            druidDataSource1.setFilters("stat");
            xaDataSource.setXaDataSource(druidDataSource1);
            xaDataSource.setMaxPoolSize(10);
            xaDataSource.setMinPoolSize(5);
            xaDataSource.setUniqueResourceName("dataSource1");
        } catch (SQLException e) {
            System.out.println("dataSource1 init error!"+e);
        }
        return xaDataSource;
    }

  Looking at the AtomikosDataSourceBean source code, AtomikosDataSourceBean implements the InitializingBean interface, which initiates a call to the afterPropertiesSet() method:

public class AtomikosDataSourceBean extends com.atomikos.jdbc.AtomikosDataSourceBean implements BeanNameAware, InitializingBean, DisposableBean {
    private String beanName;

    public AtomikosDataSourceBean() {
    }

    public void setBeanName(String name) {
        this.beanName = name;
    }
    // This method is called after the InitializingBean interface is implemented and the object is initialized
    public void afterPropertiesSet() throws Exception {
        if (!StringUtils.hasLength(this.getUniqueResourceName())) {
            this.setUniqueResourceName(this.beanName);
        }

        // Initialization method invoked
        this.init();
    }

    public void destroy() throws Exception {
        this.close();
    }
}

  Look at the core code in this.init() method as follows, and continue looking inside the new ConnectionPoolWithSynchronizedValidation(cf, this):

  ConnectionFactory cf = this.doInit();
                if (this.enableConcurrentConnectionValidation) {
                    this.connectionPool = new ConnectionPoolWithConcurrentValidation(cf, this);
                } else {
                    if (this.getTestQuery() != null) {
                        LOGGER.logWarning(this + ": testQuery set - pool may be slower / you might want to consider setting maxLifetime instead...");
                    }
                    // Initialize Connection
                    this.connectionPool = new ConnectionPoolWithSynchronizedValidation(cf, this);
                }

                this.getReference();

  Looking at the call stack, ConnectionPool calls an init() method again, and continues to look inside

public ConnectionPoolWithConcurrentValidation(ConnectionFactory connectionFactory, ConnectionPoolProperties properties) throws ConnectionPoolException {
        super(connectionFactory, properties);
    }
 public ConnectionPool(ConnectionFactory connectionFactory, ConnectionPoolProperties properties) throws ConnectionPoolException {
        this.connectionFactory = connectionFactory;
        this.properties = properties;
        this.destroyed = false;
        this.name = properties.getUniqueResourceName();
        this.init();
    }

  You can see from this method that the connection pool initialization starts with the following source code:

 private void init() throws ConnectionPoolException {
        if (LOGGER.isTraceEnabled()) {
            LOGGER.logTrace(this + ": initializing...");
        }
        // Start Initializing Connection Pool
        this.addConnectionsIfMinPoolSizeNotReached();
        this.launchMaintenanceTimer();
    }

  Source code analysis and review as follows:

 private synchronized void addConnectionsIfMinPoolSizeNotReached() {
        int connectionsToAdd = this.properties.getMinPoolSize() - this.totalSize();

        for(int i = 0; i < connectionsToAdd; ++i) {
            try {
                // Create XPooledConnection connection
                XPooledConnection xpc = this.createPooledConnection();
                // Join the connection pool
                this.connections.add(xpc);
                xpc.registerXPooledConnectionEventListener(this);
            } catch (Exception var4) {
                if (LOGGER.isTraceEnabled()) {
                    LOGGER.logTrace(this + ": could not establish initial connection", var4);
                }
            }
        }

    }

Continue tracking as follows:  

private XPooledConnection createPooledConnection() throws CreateConnectionException {
        XPooledConnection xpc = this.connectionFactory.createPooledConnection();
        EventPublisher.publish(new PooledConnectionCreatedEvent(this.properties.getUniqueResourceName(), xpc));
        return xpc;
    }

  Discover the New World, find the call to this.xaDataSource.getXAConnection(), is xaDataSource not the data source for our initialized Ruid?

  public XPooledConnection createPooledConnection() throws CreateConnectionException {
        try {
            // Start calling the getXAConnection method of xaDataSource
            XAConnection xaConnection = this.xaDataSource.getXAConnection();
            return new AtomikosXAPooledConnection(xaConnection, this.jdbcTransactionalResource, this.props);
        } catch (SQLException var3) {
            String msg = "XAConnectionFactory: failed to create pooled connection - DBMS down or unreachable?";
            LOGGER.logWarning(msg, var3);
            throw new CreateConnectionException(msg, var3);
        }
    }

Analyzing Druid's getXAConnection is as follows:

@Override
    public XAConnection getXAConnection() throws SQLException {
        // Get DruidPooledConnection, which is the connection object exposed to the client after analysis of the previous article
        DruidPooledConnection conn = this.getConnection();
        // Unencapsulating Get Real Database Connection Objects
        Connection physicalConn = conn.unwrap(Connection.class);
        // Get XAConnection from physicalConn 
        XAConnection rawXAConnection = createPhysicalXAConnection(physicalConn);
        // Encapsulate a DruidPooledXAConnection return
        return new DruidPooledXAConnection(conn, rawXAConnection);
    }

  Look at the createPhysicalXAConnection(physicalConn) to get the core code logic for the XAConnection as follows:

   if (utilClass == null && !utilClassError) {
                try {
                    utilClass = Class.forName("com.mysql.jdbc.Util");

                    Method method = utilClass.getMethod("isJdbc4");
                    utilClass_isJdbc4 = (Boolean) method.invoke(null);
                    // Get Connections
                    class_5_connection = Class.forName("com.mysql.jdbc.Connection");
                    method_5_getPinGlobalTxToPhysicalConnection = class_5_connection.getMethod("getPinGlobalTxToPhysicalConnection");
                    
                    class_5_suspendableXAConnection = Class.forName("com.mysql.jdbc.jdbc2.optional.SuspendableXAConnection");
                    constructor_5_suspendableXAConnection = class_5_suspendableXAConnection.getConstructor(class_5_connection);

                    class_5_JDBC4SuspendableXAConnection = Class.forName("com.mysql.jdbc.jdbc2.optional.JDBC4SuspendableXAConnection");
                    constructor_5_JDBC4SuspendableXAConnection = class_5_JDBC4SuspendableXAConnection.getConstructor(class_5_connection);
                    // Create XA Connection Object
                    class_5_MysqlXAConnection = Class.forName("com.mysql.jdbc.jdbc2.optional.MysqlXAConnection");
                    constructor_5_MysqlXAConnection = class_5_MysqlXAConnection.getConstructor(class_5_connection, boolean.class);
                } catch (Exception ex) {
                    ex.printStackTrace();
                    utilClassError = true;
                }
            }

  DruidPooledXAConnection is the encapsulation of DruidPooledConnection objects and XAConnection objects.

 
public DruidPooledXAConnection(DruidPooledConnection pooledConnection, XAConnection xaConnection){
        this.pooledConnection = pooledConnection;
        this.xaConnection = xaConnection;

    }

Continue tracking createPooledConnection analysis as follows:

 public XPooledConnection createPooledConnection() throws CreateConnectionException {
        try {
            // Get XAConnection
            XAConnection xaConnection = this.xaDataSource.getXAConnection();
            // Atomiikos self-encapsulates atomikos XAPooledConnection returns
            return new AtomikosXAPooledConnection(xaConnection, this.jdbcTransactionalResource, this.props);
        } catch (SQLException var3) {
            String msg = "XAConnectionFactory: failed to create pooled connection - DBMS down or unreachable?";
            LOGGER.logWarning(msg, var3);
            throw new CreateConnectionException(msg, var3);
        }
    }

  Depending on the call stack, you will find that the DruidPooledXAConnection.getXAResource() method that calls Druid is as follows:

 @Override
    public XAResource getXAResource() throws SQLException {
        return xaConnection.getXAResource();
    }

The debug screenshot below shows Druid calling MysqlXAConnection

  Looking at this class as an implementation of Mysql's support for the Xa protocol, the object relationships are as follows:

public class MysqlXAConnection extends MysqlPooledConnection implements XAConnection, XAResource {}

  Initialization of the entire transaction manager and XA objects is complete here, and will be iterated based on the number of connections configured

List <XPooledConnection> Connections connection pool out.

The next article will analyze how client s perform work after they have initialized such a connection pool for multiple databases.

Posted by fonecave on Wed, 17 Nov 2021 09:16:04 -0800