天天看點

springboot+atomikos+多資料源管理事務(mysql 8.0)備注說明:可能存在的問題:

引用 https://www.cnblogs.com/coloz/p/10845058.html

jta:Java Transaction API,即是java中對事務處理的api 即 api即是接口的意思

atomikos:Atomikos TransactionsEssentials 是一個為Java平台提供增值服務的并且開源類事務管理器

目錄

1.結構

2.pom依賴

3.建立本地資料庫+表

4.application.yml 

5.實體類

6.mapper接口

7.service

8.Controller

9.配置資料源(*******重點總是在最後********)

10.測試

備注說明:

可能存在的問題:

1、using the Connector/J connection property 'autoReconnect=true' to avoid this problem.    https://blog.csdn.net/zljjava/article/details/7996091

日志:using the Connector/J connection property 'autoReconnect=true' to avoid this problem.  

MySQL資料庫連接配接逾時(wait_timeout)問題的處理

2、Atomikos資料連接配接池源碼,弄清testQuery

1.結構

springboot+atomikos+多資料源管理事務(mysql 8.0)備注說明:可能存在的問題:

2.pom依賴

我這裡使用本地資料庫是mysql8,   

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <!--<version>2.0.0.RELEASE</version>-->
        <version>2.1.0.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter -->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.0</version><!-- 1.3.0以上的版本沒有@MapperScan以及@Select注解 -->
        </dependency>
        <!-- automatic+jta的分布式事務管理 -->
        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-jta-atomikos -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jta-atomikos</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <!--boot 2.1預設 mysql8的版本; boot 2.0預設mysql5版本-->
            <version>8.0.13</version>
            <!--<version>5.1.46</version>-->
            <!--<scope>runtime</scope>-->
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
            <version>1.18.2</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

    </dependencies>
           

3.建立本地資料庫+表

springboot+atomikos+多資料源管理事務(mysql 8.0)備注說明:可能存在的問題:

4.application.yml 

server:
  port: 8080
  servlet:
  #    # 項目contextPath
    context-path: /manyDatasource

spring:
  application:
      name: manyDatasource
  datasource:
#  spring.datasource.test1
#    druid:
      test1:
#      jdbc-url,url,jdbcurl哪個合适用哪個
        jdbcurl: jdbc:mysql://localhost:3306/test1?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8
        username: root
        password: 123456
        initial-size: 1
        min-idle: 1
        max-active: 20
        test-on-borrow: true
#        driver-class-name: com.mysql.jdbc.Driver
#        下面是最新的mysql8版本推薦的驅動
        driver-class-name: com.mysql.cj.jdbc.Driver
        type: com.alibaba.druid.pool.DruidDataSource
#        下面是另外加的配置資料源的參數
        minPoolSize: 3
        maxPoolSize: 25
        maxLifetime: 20000
        borrowConnectionTimeout: 30
        loginTimeout: 30
        maintenanceInterval: 60
        maxIdleTime: 60

      test2:
        jdbcurl: jdbc:mysql://localhost:3306/test2?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf-8
        username: root
        password: 123456
        driver-class-name: com.mysql.cj.jdbc.Driver
        type: com.alibaba.druid.pool.DruidDataSource
        minPoolSize: 3
        maxPoolSize: 25
        maxLifetime: 20000
        borrowConnectionTimeout: 30
        loginTimeout: 30
        maintenanceInterval: 60
        maxIdleTime: 60

mybatis:
    mapper-locations: classpath:mapper/*.xml

#設定靜态資源路徑,多個以逗号分隔
spring.resources.static-locations: classpath:static/,file:static/

# 日志配置
logging:
  level:
    czs: debug
    org.springframework: WARN
    org.spring.springboot.dao: debug
           

5.實體類

 ps.使用lombok插件挺友善的~   id資料庫主鍵自增

springboot+atomikos+多資料源管理事務(mysql 8.0)備注說明:可能存在的問題:
@Data
public class User {
    private Integer id;
    private String name;
    private long age;
}
           
springboot+atomikos+多資料源管理事務(mysql 8.0)備注說明:可能存在的問題:

6.mapper接口

UserMapper1:
           
springboot+atomikos+多資料源管理事務(mysql 8.0)備注說明:可能存在的問題:
public interface UserMapper1 {
    // 查詢語句
    @Select("SELECT * FROM users WHERE NAME = #{name}")
    User findByName(@Param("name") String name);

    // 添加
    @Insert("INSERT INTO users(NAME, AGE) VALUES(#{name}, #{age})")
    int insert(@Param("name") String name, @Param("age") Integer age);
}
           
springboot+atomikos+多資料源管理事務(mysql 8.0)備注說明:可能存在的問題:
UserMapper2:

           
springboot+atomikos+多資料源管理事務(mysql 8.0)備注說明:可能存在的問題:
public interface UserMapper2 {
    // 查詢語句
    @Select("SELECT * FROM users WHERE NAME = #{name}")
    User findByName(@Param("name") String name);

    // 添加
    @Insert("INSERT INTO users(NAME, AGE) VALUES(#{name}, #{age})")
    int insert(@Param("name") String name, @Param("age") Integer age);
}
           
springboot+atomikos+多資料源管理事務(mysql 8.0)備注說明:可能存在的問題:

7.service

ManyService1:
           
springboot+atomikos+多資料源管理事務(mysql 8.0)備注說明:可能存在的問題:
@Service
public class ManyService1 {

    @Autowired
    private UserMapper1 userMapper1;
    @Autowired
    private UserMapper2 userMapper2;

 /*   @Transactional(transactionManager = "test1TransactionManager",rollbackFor = Exception.class)
    public int insert(String name, Integer age) {
        int i = userMapper1.insert(name, age);
        System.out.println("userMapper1.insert結束~ :" + i);
       // int a = 1 / 0;//手動異常
        return i;
    }*/

    // 開啟事務,由于使用jta+atomikos解決分布式事務,是以此處不必再指定事務
    @Transactional
    public int insert(String name, Integer age) {
        int insert = userMapper1.insert(name, age);
        //int i = 1 / age;// 指派age為0故意引發事務
        return insert;
    }

    //http://localhost:8080/manyDatasource/insertDb1AndDb2?name=tom3&age=2
    // 開啟事務,由于使用jta+atomikos解決分布式事務,是以此處不必再指定事務
    @Transactional
    public int insertDb1AndDb2(String name, Integer age) {
        int insert = userMapper1.insert(name, age);
        int insert2 = userMapper2.insert(name, age);
        int i = 1 / age;// 指派age為0故意引發事務
        return insert + insert2;
    }


}
           
springboot+atomikos+多資料源管理事務(mysql 8.0)備注說明:可能存在的問題:
ManyService2:
           
springboot+atomikos+多資料源管理事務(mysql 8.0)備注說明:可能存在的問題:
@Service
public class ManyService2 {

    @Autowired
    private UserMapper2 userMapper2;

    @Transactional(transactionManager = "test2TransactionManager",rollbackFor = Exception.class)
    public int insert(String name, Integer age) {
        int i = userMapper2.insert(name, age);
        System.out.println("userMapper2.insert結束~ :" + null);
        int a = 1 / 0;//手動異常
        return i;
    }

}
           
springboot+atomikos+多資料源管理事務(mysql 8.0)備注說明:可能存在的問題:

8.Controller

springboot+atomikos+多資料源管理事務(mysql 8.0)備注說明:可能存在的問題:
@RestController
public class ManyController {

    @Autowired
    private ManyService1 manyService1;

    @Resource
    private ManyService2 manyService2;

    @RequestMapping(value = "datasource1")
    public int datasource1(String name, Integer age) {
        return manyService1.insert(name, age);
    }

    @RequestMapping(value = "datasource2")
    public int datasource2(String name, Integer age) {
        return manyService2.insert(name, age);
    }

    /**
     * @Param:
     * @Description: 這裡測試兩個service兩個資料源的事務(不加上atomikos插件的情況下測試,
     *使用DataSource1Config和DataSource2Config 兩個配置類, 關閉DBConfig1, DBConfig2和MyBatisConfig1, MyBatisConfig1兩個類)
     * @Author: zyf    2019/5/10
     */
    //http://localhost:8080/manyDatasource/testManyTrans?name=tom4&age=2
    @RequestMapping(value = "testManyTrans")
    public int testManyTrans(String name, Integer age) {
        int i = 0;
        int i1 = manyService1.insert(name, age);
        System.out.println("manyService1.insert :" + i1);

        /*
        第二個事務中會手動造成一個異常~,
        但是第一個事務執行完畢了,儲存到了資料庫
        */
        int i2 = manyService2.insert(name, age);
        System.out.println("manyService2.insert :" + i2);
        return i;
    }


    /**
     * @Param:
     * @Description: 這裡測試使用atomikos插件測試多資料源事務
     * @Author: zyf    2019/5/10
     */
    //http://localhost:8080/manyDatasource/insertDb1AndDb2?name=tom5&age=2
    //http://localhost:8080/manyDatasource/insertDb1AndDb2?name=tom6&age=0  //測試除數為0後的事務管理
    @RequestMapping(value = "insertDb1AndDb2")
    public int insertDb1AndDb2(String name, Integer age) {
        return manyService1.insertDb1AndDb2(name, age);
    }


}
           
springboot+atomikos+多資料源管理事務(mysql 8.0)備注說明:可能存在的問題:

9.配置資料源(*******重點總是在最後********)

DBConfig1:
           
@Data
@ConfigurationProperties(prefix = "spring.datasource.test1") // 注意這個字首要和application.yml檔案的字首一樣
public class DBConfig1 {
   // @Value("${mysql.datasource.test1.jdbcurl}")
   //@Value("${jdbcurl}")
    private String jdbcurl;
    //private String url;
    // 比如這個url在properties中是這樣子的mysql.datasource.test1.username = root
    private String username;
    private String password;
    private int minPoolSize;
    private int maxPoolSize;
    private int maxLifetime;
    private int borrowConnectionTimeout;
    private int loginTimeout;
    private int maintenanceInterval;
    private int maxIdleTime;
    private String testQuery;
}
           
DBConfig2:

           
@Data
@ConfigurationProperties(prefix = "spring.datasource.test2")// 注意這個字首要和application.yml檔案的字首一樣
public class DBConfig2 {
    //@Value("${spring.datasource.test2.jdbcurl}")
    //@Value("${jdbcurl}")
    //private String url;
    private String jdbcurl;
    private String username;
    private String password;
    private int minPoolSize;
    private int maxPoolSize;
    private int maxLifetime;
    private int borrowConnectionTimeout;
    private int loginTimeout;
    private int maintenanceInterval;
    private int maxIdleTime;
    private String testQuery;

}
           

上面兩個配置類作用: 将application.yml配置檔案中配置自動封裝到實體類字段中,然後指派給atomikos類型的資料源.(下面兩個具體配置資料源)

MyBatisConfig1:
           
springboot+atomikos+多資料源管理事務(mysql 8.0)備注說明:可能存在的問題:
// 配置資料源
    //@Bean(name = "testDataSource")  //test1DataSource
    @Bean(name = "test1DataSource")  //test1DataSource
    public DataSource testDataSource(DBConfig1 testConfig) throws SQLException {
        MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
        //mysqlXaDataSource.setUrl(testConfig.getUrl());
        mysqlXaDataSource.setUrl(testConfig.getJdbcurl());
        mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
        mysqlXaDataSource.setPassword(testConfig.getPassword());
        mysqlXaDataSource.setUser(testConfig.getUsername());
        mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);

        // 将本地事務注冊到創 Atomikos全局事務
        AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
        xaDataSource.setXaDataSource(mysqlXaDataSource);
        xaDataSource.setUniqueResourceName("test1DataSource");

        xaDataSource.setMinPoolSize(testConfig.getMinPoolSize());
        xaDataSource.setMaxPoolSize(testConfig.getMaxPoolSize());
        xaDataSource.setMaxLifetime(testConfig.getMaxLifetime());
        xaDataSource.setBorrowConnectionTimeout(testConfig.getBorrowConnectionTimeout());
        xaDataSource.setLoginTimeout(testConfig.getLoginTimeout());
        xaDataSource.setMaintenanceInterval(testConfig.getMaintenanceInterval());
        xaDataSource.setMaxIdleTime(testConfig.getMaxIdleTime());
        xaDataSource.setTestQuery(testConfig.getTestQuery());
        return xaDataSource;
    }

    @Bean(name = "test1SqlSessionFactory")
    public SqlSessionFactory testSqlSessionFactory(@Qualifier("test1DataSource") DataSource dataSource)
            throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        return bean.getObject();
    }

    @Bean(name = "test1SqlSessionTemplate")
    public SqlSessionTemplate testSqlSessionTemplate(
            @Qualifier("test1SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
           
MyBatisConfig2 :
           
@Configuration
@MapperScan(basePackages = "czs.mapper2", sqlSessionTemplateRef = "test2SqlSessionTemplate")
public class MyBatisConfig2 {

    // 配置資料源
    @Bean(name = "test2DataSource")
    public DataSource testDataSource(DBConfig2 testConfig) throws SQLException {
        MysqlXADataSource mysqlXaDataSource = new MysqlXADataSource();
        //mysqlXaDataSource.setUrl(testConfig.getUrl());
        mysqlXaDataSource.setUrl(testConfig.getJdbcurl());
        mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);
        mysqlXaDataSource.setPassword(testConfig.getPassword());
        mysqlXaDataSource.setUser(testConfig.getUsername());
        mysqlXaDataSource.setPinGlobalTxToPhysicalConnection(true);

        AtomikosDataSourceBean xaDataSource = new AtomikosDataSourceBean();
        xaDataSource.setXaDataSource(mysqlXaDataSource);
        xaDataSource.setUniqueResourceName("test2DataSource");

        xaDataSource.setMinPoolSize(testConfig.getMinPoolSize());
        xaDataSource.setMaxPoolSize(testConfig.getMaxPoolSize());
        xaDataSource.setMaxLifetime(testConfig.getMaxLifetime());
        xaDataSource.setBorrowConnectionTimeout(testConfig.getBorrowConnectionTimeout());
        xaDataSource.setLoginTimeout(testConfig.getLoginTimeout());
        xaDataSource.setMaintenanceInterval(testConfig.getMaintenanceInterval());
        xaDataSource.setMaxIdleTime(testConfig.getMaxIdleTime());
        xaDataSource.setTestQuery(testConfig.getTestQuery());
        return xaDataSource;
    }

    @Bean(name = "test2SqlSessionFactory")
    public SqlSessionFactory testSqlSessionFactory(@Qualifier("test2DataSource") DataSource dataSource)
            throws Exception {
        SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
        bean.setDataSource(dataSource);
        return bean.getObject();
    }

    @Bean(name = "test2SqlSessionTemplate")
    public SqlSessionTemplate testSqlSessionTemplate(
            @Qualifier("test2SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
        return new SqlSessionTemplate(sqlSessionFactory);
    }
}
           

10.測試

http://localhost:8080/manyDatasource/insertDb1AndDb2?name=tom5&age=2   
結果: test1和test2資料庫都插入資料~
           
http://localhost:8080/manyDatasource/insertDb1AndDb2?name=tom6&age=0   (兩個insert操作後,手動異常)
           
結果: test1和test2資料庫都未插入資料~
           

 GitHub傳送門: https://github.com/ColoZhu/springbootmanyDatasource

備注說明:

  1.     private int minPoolSize;
  2.     private int maxPoolSize;
  3.     private int maxLifetime;
  4.     private int borrowConnectionTimeout;
  5.     private int loginTimeout;
  6.     private int maintenanceInterval;
  7.     private int maxIdleTime;
  8.     private String testQuery;

可能存在的問題:

1、using the Connector/J connection property 'autoReconnect=true' to avoid this problem.    https://blog.csdn.net/zljjava/article/details/7996091

日志:using the Connector/J connection property 'autoReconnect=true' to avoid this problem.  

\ 訂閱

com.mysql.jdbc.CommunicationsException: The last packet successfully received from the server was58129 seconds ago.The last packet sent successfully to the server was 58129 seconds ago, which is longer than the server configured value of 'wait_timeout'. You should consider either expiring and/or testing connection validity before use in your application, increasing the server configured values for client timeouts, or using the Connector/J connection property 'autoReconnect=true' to avoid this problem.

查了一下,原來是mysql逾時設定的問題

如 果連接配接閑置8小時 (8小時内沒有進行資料庫操作), mysql就會自動斷開連接配接, 要重新開機tomcat. 

解決辦法:

    一種. 如果不用hibernate的話, 則在 connection url中加參數: autoReconnect=true

jdbc.url=jdbc:mysql://ipaddress:3306/database?autoReconnect=true&amp;autoReconnectForPools=true

    二種。用hibernate的話, 加如下屬性: 

        <property name="connection.autoReconnect">true</property>

        <property name="connection.autoReconnectForPools">true</property>

        <property name="connection.is-connection-validation-required">true</property>

    三。要是還用c3p0連接配接池: 

        <property name="hibernate.c3p0.acquire_increment">1</property> 

        <property name="hibernate.c3p0.idle_test_period">0</property> 

        <property name="hibernate.c3p0.timeout">0</property>

        <property name="hibernate.c3p0.validate">true</property>

 四。最不好的解決方案

使用Connector/J連接配接MySQL資料庫,程式運作較長時間後就會報以下錯誤:

Communications link failure,The last packet successfully received from the server was *** millisecond ago.The last packet successfully sent to the server was ***  millisecond ago。

其中錯誤還會提示你修改wait_timeout或是使用Connector/J的autoReconnect屬性避免該錯誤。

後來查了一些資料,才發現遇到這個問題的人還真不少,大部分都是使用連接配接池方式時才會出現這個問題,短連接配接應該很難出現這個問題。這個問題的原因:

MySQL伺服器預設的“wait_timeout”是28800秒即8小時,意味着如果一個連接配接的空閑時間超過8個小時,MySQL将自動斷開該 連接配接,而連接配接池卻認為該連接配接還是有效的(因為并未校驗連接配接的有效性),當應用申請使用該連接配接時,就會導緻上面的報錯。

1.按照錯誤的提示,可以在JDBC URL中使用autoReconnect屬性,實際測試時使用了autoReconnect=true& failOverReadOnly=false,不過并未起作用,使用的是5.1版本,可能真像網上所說的隻對4之前的版本有效。

2.沒辦法,隻能修改MySQL的參數了,wait_timeout最大為31536000即1年,在my.cnf中加入:

[mysqld]

wait_timeout=31536000

interactive_timeout=31536000

重新開機生效,需要同時修改這兩個參數。

=============================================================================================================

MySQL資料庫連接配接逾時(wait_timeout)問題的處理

  • 部落格分類: 
  • 積少成多

MySQL Linux Windows JDBC 活動 

    想必大家在用MySQL時都會遇到連接配接逾時的問題,如下圖所示: 

springboot+atomikos+多資料源管理事務(mysql 8.0)備注說明:可能存在的問題:
    就是這個異常(com.mysql.jdbc.exceptions.jdbc4.CommunicationsException:Communications link failure Last packet sent to the server was X ms ago),是由于MySQL服務在長時間不連接配接之後斷開了,斷開之後的首次請求會抛出這個異常。那麼既然是連接配接逾時的問題,就要去MySQL中探究一下連接配接時間是怎麼控制的。打開MySQL的控制台,運作:show variables like ‘%timeout%’,檢視和連接配接時間有關的MySQL系統變量,得到如下結果: 
springboot+atomikos+多資料源管理事務(mysql 8.0)備注說明:可能存在的問題:

    其中wait_timeout就是負責逾時控制的變量,其時間為長度為28800s,就是8個小時,那麼就是說MySQL的服務會在操作間隔8小時後斷開,需要再次重連。也有使用者在URL中使用jdbc.url=jdbc:mysql://localhost:3306/nd?autoReconnect=true來使得連接配接自動恢複,當然了,這是可以的,不過是MySQL4及其以下版本适用。MySQL5中已經無效了,必須調整系統變量來控制了。MySQL5手冊中對兩個變量有如下的說明: 

    interactive_timeout:伺服器關閉互動式連接配接前等待活動的秒數。互動式用戶端定義為在mysql_real_connect()中使用CLIENT_INTERACTIVE選項的用戶端。又見wait_timeout 

    wait_timeout:伺服器關閉非互動連接配接之前等待活動的秒數。線上程啟動時,根據全局wait_timeout值或全局interactive_timeout值初始化會話wait_timeout值,取決于用戶端類型(由mysql_real_connect()的連接配接選項CLIENT_INTERACTIVE定義),又見interactive_timeout 

    如此看來,兩個變量是共同控制的,那麼都必須對他們進行修改了。繼續深入這兩個變量wait_timeout的取值範圍是1-2147483(Windows),1-31536000(linux),interactive_time取值随wait_timeout變動,它們的預設值都是28800。 

    MySQL的系統變量由配置檔案控制,當配置檔案中不配置時,系統使用預設值,這個28800就是預設值。要修改就隻能在配置檔案裡修改。Windows下在%MySQL HOME%/bin下有mysql.ini配置檔案,打開後在如下位置添加兩個變量,指派。(這裡修改為388000) 

    儲存退出,重新開機mysql服務,一定是重新開機系統服務。便可看到修改結果: 

springboot+atomikos+多資料源管理事務(mysql 8.0)備注說明:可能存在的問題:
    Linux系統下的配置檔案為/etc/my.cnf。需要多說一點的是:windows下的配置檔案具體是哪個需要從windows系統服務中找到mysql,打開屬性,看“可執行檔案路徑”裡面的參數值,因為它可能是my.cnf而不是my.ini,這是由于安裝時的設定,我們可能會忽略它。 
springboot+atomikos+多資料源管理事務(mysql 8.0)備注說明:可能存在的問題:

    至此,修改完成,那麼連接配接逾時的問題從資料庫上就解決了。當然在程式設計時也可以使用連接配接池設定逾時時間,配置相對簡單。但修改資料庫更為本質一些。 

    個人見解。希望對使用者有用。

2、Atomikos資料連接配接池源碼,弄清testQuery

作者:fbysss

msn:[email protected]

blog:blog.csdn.net/fbysss

聲明:本文由fbysss原創,轉載請注明出處

關鍵字:Atomikos資料連接配接池

前言

Atomikos資料連接配接池,國内有一些應用,但testQuery這個屬性,在網上均是簡單配置,并沒有做特殊說明。通過對Atomikos源碼的分析,發現這裡很有學問。

分析

我們使用的資料源是AtomikosDataSourceBean,在其doInit方法中,會調用AtomikosXAConnectionFactory的createPooledConnection方法,該方法會傳回一個AtomikosXAPooledConnection連接配接。

而AtomikosXAPooledConnection是AbstractXPooledConnection的一個子類,AbstractXPooledConnection中,在調用createConnectionProxy時,會調用testUnderlyingConnection方法,用于進行測試。

在AtomikosDataSourceBean的getConnection時,調用connectionPool的borrowConnection方法,進而調用AbstractXPooledConnection中的createConnectionProxy,進而調用testUnderlyingConnection方法。

可以看testUnderlyingConnection中的關鍵代碼,一旦設定了testQuery,每次getConnection的時候,就會連接配接查詢一次(通過jprofiler也可以檢測出來):

[java] view plaincopyprint?

如果失敗,抛出CreateConnectionException異常。

注意ConnectionPool的borrowConnection方法,其中有一段:

[java] view plaincopyprint?

可以看到,其做的事情,就是周遊連接配接池中的連接配接,一個一個的測試。注意ret = xpc.createConnectionProxy ( hmsg ),一旦該方法抛出CreateConnectionException,就執行it.remove();即将該連接配接從連接配接池中删除。

如果設定了testQuery屬性,每次擷取連接配接時testQuery,能夠保證應用伺服器啟動之後,與資料庫連接配接暫時中斷之後,能夠在下一次請求時,自動重建立立連接配接。

連接配接池是如何自動建立連接配接的呢?其實就是簡單的把無效的連接配接一個一個删掉,直到全部删光了,池裡面沒有有效(poolAvailableSize==0,是根據連接配接的isTerminated狀态來判斷的,而不是是否被重置過)的連接配接了,這樣根據連接配接池的機制,就會調用growPool方法去請求新的連接配接。

但是這樣有性能消耗,而且還不小。對于網站展示部分,不需要實時去檢測,可以考慮采用定時檢測的方法:

n 首先,保證testQuery為空,不配置。

n 建立一個DbPoolMonitorService類,實作ApplicationContextAware接口,這樣應用啟動時,會自動注入ApplicationContext對象。這樣,可以在service中,調用getBean方法,擷取AtomikosDataSourceBean的執行個體,

AtomikosDataSourceBean ads = (AtomikosDataSourceBean)ctx.getBean(“jtaDataSource”);

然後,ads中能夠獲得minPoolSize/maxPoolSize/availablePoolSize/totalPoolSize等屬性,可以做一個界面來監測資料連接配接池的使用和配置情況,甚至可以動态修改這些屬性。

n 做一個定時器,調用一個DbPoolMonitorService定期去手工test。使用SELECT 1 語句即可。

n 注意:try 部分ads.setTestQuery (“SELECT 1”);然後調用BaseDao的執行個體去執行一句話,依然可以使”SELECT 1” 。finally部分ads.setTestQuery(null),以保證不對其他部分造成後續影響。

n 可根據使用者通路量,來決定定時周期,一般10分鐘左右即可。這樣,避免了每次擷取連接配接去做一次檢測操作,又能夠将故障限制在一定時間範圍内。是一個較好的折衷做法。

補充:我們再看ret = xpc.createConnectionProxy ( hmsg )這句話,如果這時候DB和WebAppServer的網絡鍊路已經正常,DB正常運作,傳回将不是空,會退出循環。這樣,如果連接配接池中的連接配接是多個,則隻會生成1個新的連接配接,如果要保證連接配接池機制的效果,需要在寫監控程式的時候,去取得所有有效連接配接,循環test。

繼續閱讀