zoukankan      html  css  js  c++  java
  • Atomikos实现多数据源的事物管理

      之前试过使用Spring动态切换数据库,通过继承AbstractRoutingDataSource重写determineCurrentLookupKey()方法,来决定使用那个数据库。在开启事务之前,通过改变lookupKey来达到切换数据源目的。但是这种方法多个数据库之前没办法做事务管理,或许对于主从(读写)数据库会好用一些,而对于需要一个操作中更新多个数据库的情况,使用Atomikos或许会更好一些。

      本文采用spring4+hibernate4+Atomikos进行事务管理。

    依赖dependency

      Atomikos要使用的库使用maven导入,spring和hibernate的依赖就不一一列出,需要额外添加的dependency如下:

        <dependency>
                <groupId>com.atomikos</groupId>
                <artifactId>atomikos-util</artifactId>
                <version>3.9.3</version>
            </dependency>
            <dependency>
                <groupId>com.atomikos</groupId>
                <artifactId>transactions-jdbc</artifactId>
                <version>3.9.3</version>
            </dependency>
            <dependency>
                <groupId>com.atomikos</groupId>
                <artifactId>transactions-jta</artifactId>
                <version>3.9.3</version>
            </dependency>
            <dependency>
                <groupId>com.atomikos</groupId>
                <artifactId>transactions</artifactId>
                <version>3.9.3</version>
            </dependency>
            <dependency>
                <groupId>com.atomikos</groupId>
                <artifactId>transactions-api</artifactId>
                <version>3.9.3</version>
            </dependency>
            <dependency>
                <groupId>javax.transaction</groupId>
                <artifactId>jta</artifactId>
                <version>1.1</version>
            </dependency>

    使用的数据库驱动依赖如下:

            <!-- 加入mysql驱动依赖包 -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.34</version>
            </dependency>
            <!-- sql server数据库驱动 -->
            <dependency>
                <groupId>net.sourceforge.jtds</groupId>
                <artifactId>jtds</artifactId>
                <version>1.3.1</version>
            </dependency>
            <!-- 加入druid数据源依赖包 -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>1.0.26</version>
            </dependency>            

    有点需要注意的是,druid的版本应该跟上mysql的版本,不然可能找不到MySQL的XADataSource。

    配置configuration

    配置数据源xml代码:

      <bean id="mysql" class="com.alibaba.druid.pool.xa.DruidXADataSource" init-method="init" destroy-method="close">
            <property name="url" value="${jdbc_url}" />
            <property name="username" value="${jdbc_username}" />
            <property name="password" value="${jdbc_password}" />
    
            <!-- 初始化连接大小 -->
            <property name="initialSize" value="0" />
            <!-- 连接池最大使用连接数量 -->
            <property name="maxActive" value="20" />
            <!-- 连接池最小空闲 -->
            <property name="minIdle" value="0" />
            <!-- 获取连接最大等待时间 -->
            <property name="maxWait" value="60000" />
            <!-- <property name="poolPreparedStatements" value="true" />
            <property name="maxPoolPreparedStatementPerConnectionSize" value="33" /> -->
            <property name="validationQuery" value="SELECT 1" />
            <property name="testOnBorrow" value="false" />
            <property name="testOnReturn" value="false" />
            <property name="testWhileIdle" value="true" />
            <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
            <property name="timeBetweenEvictionRunsMillis" value="60000" />
            <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
            <property name="minEvictableIdleTimeMillis" value="25200000" />
            <!-- 打开removeAbandoned功能 -->
            <property name="removeAbandoned" value="true" />
            <!-- 1800秒,也就是30分钟 -->
            <property name="removeAbandonedTimeout" value="1800" />
            <!-- 关闭abanded连接时输出错误日志 -->
            <property name="logAbandoned" value="true" />
    
            <!-- 监控数据库 -->
            <!-- <property name="filters" value="mergeStat" /> -->
            <!-- <property name="filters" value="stat" /> -->
            <!--<property name="filters" value="config" />
              <property name="connectionProperties" value="config.decrypt=true" />-->
        </bean>
    
        <bean id="mysqlDataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean"
              init-method="init" destroy-method="close">
            <!-- Set unique name for this DataSource -->
            <property name="uniqueResourceName">
                <value>mysql</value>
            </property>
            <!-- Set XADatasource class name-->
            <property name="xaDataSource" ref="mysql"/>
            <!-- set properties for datasource connection pool -->
            <property name="poolSize" value="3" />
            <!-- 管理 Connection 被占用的时间 -->
            <!-- 如果不设置这个值,Atomikos使用默认的300秒(即5分钟),那么在处理大批量数据读取的时候,一旦超过5分钟,就会抛出类似 Resultset is close 的错误 -->
            <property name="reapTimeout"><value>20000</value></property>
        </bean>

    配置SessionFactory的xml代码:

    <bean id="noticesSessionFactory"
              class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
            <property name="dataSource" ref="mysqlDataSource" />
            <property name="jtaTransactionManager" ref="springTransactionManager"></property>
            <property name="packagesToScan">
                <list>
                    <value>com.test.model</value>
                </list>
            </property>
            <property name="hibernateProperties">
                <props>
                    <!-- 慎重(推荐禁止) 固定为update -->
                    <prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
                    <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
                    <prop key="hibernate.autoReconnect">true</prop>
                    <prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
                    <prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
                    <prop key="hibernate.use_sql_comments">${hibernate.use_sql_comments}</prop>
                    <prop key="hibernate.jdbc.batch_size">${hibernate.batch_size}</prop>
                </props>
            </property>
        </bean>

    如果要使用getCurrentSession()的话,要注意加上:

    <property name="jtaTransactionManager" ref="springTransactionManager"></property>

    添加多个数据库的话,只需要把上面的xml再写一遍。

    下面配置atomikos的事务管理器xml代码:

    <!-- atomikos事务管理器 -->
        <bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager"
              init-method="init" destroy-method="close">
            <property name="forceShutdown">
                <value>true</value>
            </property>
        </bean>
        <bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp">
            <property name="transactionTimeout" value="300" />
        </bean>

    spring的事务管理器xml代码:

    <!-- spring 事务管理器 -->
        <bean id="springTransactionManager"
              class="org.springframework.transaction.jta.JtaTransactionManager">
            <property name="transactionManager" ref="atomikosTransactionManager"/>
            <property name="userTransaction" ref="atomikosUserTransaction" />
            <property name="allowCustomIsolationLevels" value="true"/>
        </bean>
    
        <!-- 注解方式配置事物-->
       <tx:annotation-driven transaction-manager="springTransactionManager" />

    添加transactions.properties文件:

    # SAMPLE PROPERTIES FILE FOR THE TRANSACTION SERVICE
    # THIS FILE ILLUSTRATES THE DIFFERENT SETTINGS FOR THE TRANSACTION MANAGER
    # UNCOMMENT THE ASSIGNMENTS TO OVERRIDE DEFAULT VALUES;
    
    # Required: factory implementation class of the transaction core.
    # NOTE: there is no default for this, so it MUST be specified! 
    # 
    com.atomikos.icatch.service=com.atomikos.icatch.standalone.UserTransactionServiceFactory
    
            
    # Set base name of file where messages are output 
    # (also known as the 'console file').
    #
    # com.atomikos.icatch.console_file_name = tm.out
    
    # Size limit (in bytes) for the console file;
    # negative means unlimited.
    #
    # com.atomikos.icatch.console_file_limit=-1
    
    # For size-limited console files, this option
    # specifies a number of rotating files to 
    # maintain.
    #
    # com.atomikos.icatch.console_file_count=1
    
    # Set the number of log writes between checkpoints
    #
    # com.atomikos.icatch.checkpoint_interval=500
    
    # Set output directory where console file and other files are to be put
    # make sure this directory exists!
    #
    # com.atomikos.icatch.output_dir = ./
    
    # Set directory of log files; make sure this directory exists!
    #
    # com.atomikos.icatch.log_base_dir = ./
    
    # Set base name of log file
    # this name will be  used as the first part of 
    # the system-generated log file name
    #
    # com.atomikos.icatch.log_base_name = tmlog
    
    # Set the max number of active local transactions 
    # or -1 for unlimited.
    #
    # com.atomikos.icatch.max_actives = 50
    
    # Set the default timeout (in milliseconds) for local transactions
    #
    # com.atomikos.icatch.default_jta_timeout = 10000
    
    # Set the max timeout (in milliseconds) for local transactions
    #
    # com.atomikos.icatch.max_timeout = 300000
    
    # The globally unique name of this transaction manager process
    # override this value with a globally unique name
    #
    # com.atomikos.icatch.tm_unique_name = tm
        
    # Do we want to use parallel subtransactions? JTA's default
    # is NO for J2EE compatibility
    #
    #com.atomikos.icatch.serial_jta_transactions=false
                        
    # If you want to do explicit resource registration then
    # you need to set this value to false.
    #
    # com.atomikos.icatch.automatic_resource_registration=true  
        
    # Set this to WARN, INFO or DEBUG to control the granularity
    # of output to the console file.
    #
    # com.atomikos.icatch.console_log_level=WARN
        
    # Do you want transaction logging to be enabled or not?
    # If set to false, then no logging overhead will be done
    # at the risk of losing data after restart or crash.
    #
    # com.atomikos.icatch.enable_logging=true
    
    # Should two-phase commit be done in (multi-)threaded mode or not?
    # Set this to false if you want commits to be ordered according
    # to the order in which resources are added to the transaction.
    #
    # NOTE: threads are reused on JDK 1.5 or higher. 
    # For JDK 1.4, thread reuse is enabled as soon as the 
    # concurrent backport is in the classpath - see 
    # http://mirrors.ibiblio.org/pub/mirrors/maven2/backport-util-concurrent/backport-util-concurrent/
    #
    # com.atomikos.icatch.threaded_2pc=false
    
    # Should shutdown of the VM trigger shutdown of the transaction core too?
    #
    # com.atomikos.icatch.force_shutdown_on_vm_exit=false

    好了,Atomikos算是配置完成,我们可以写代码测试一下。测试的代码这里就不写出了。

    性能优化

    尽管这个软件有着很大的优势,但是想要更好的发挥其作用,可以按以下的方法优化:
      l 更高的内存,意味着更高的吞吐量(每秒的事务数目)。
      l 使连接池尽可能的大。
      l 一旦你不需要的连接请马上关闭它们。不要把你的应用程序放在缓存里,让内部连接池为你做这些,这将促使更高效的连接使用。
      l 不要让活动的事务闲置:终止所有情况下的事务,尤其是在异常报错情况下的事务。这将减少数据库的锁定时间,并且最大效率的处理启用的使用。

    总结

      这么长的配置文件要小心点写,我的粗心结果就是datasource乱套了,导致花了不少时间排错。当然还有各种乱七八糟的糟心问题,这些问题会在后面的文章中列出来,大家可以注意避免。最后用上Atomikos的分布式事务,没有了各种的脏数据,舒心了不少。

  • 相关阅读:
    zoj3888 找第二大
    zoj3882 博弈
    字典树小总结
    hdu2222 字典树
    hdu1247 字典树
    开放融合 | “引擎级”深度对接!POLARDB与SuperMap联合构建首个云原生时空平台
    阿里HBase高可用8年“抗战”回忆录
    最佳实践 | RDS & POLARDB归档到X-Pack Spark计算
    今日头条在消息服务平台和容灾体系建设方面的实践与思考
    饿了么监控系统 EMonitor 与美团点评 CAT 的对比
  • 原文地址:https://www.cnblogs.com/ginponson/p/5947200.html
Copyright © 2011-2022 走看看