spring分散式事務學習筆記(1)
此文已由作者夏昀授權網易雲社群釋出。
歡迎訪問網易雲社群,瞭解更多網易技術產品運營經驗。
分散式事務對效能有一定的影響,所以不是最佳的解決方案,能通過設計避免最好儘量避免。
分散式事務(Distributed transactions),也稱作XA事務(XA是一個協議的名字),在spring中被稱作global transaction,是指一個事務會涉及到不同的事務資源,比如不同的資料庫,訊息佇列。事務資源都支援commit和rollback這樣的事務控制命令。
按是否需要實現完整javaEE功能的應用伺服器,可以將實現XA事務的方法分為兩種:
1.一般情況下,XA事務是通過javaEE應用伺服器實現的,即CMT(Container Managed Transaction)。應用伺服器實現了JTA(Java Transaction API),應用通過JNDI獲取應用伺服器的JTA UserTransaction。JTA的api比較複雜,開發者需要研究較多JTA的細節,使用spring事務管理可以簡化JTA的程式設計模型。但是這樣還是需要依賴實現了對應javaEE功能的應用伺服器。
2.不需要應用伺服器(standalone),或者只需要輕量級的應用伺服器,例如tomcat,tomcat沒有實現所有的javaEE功能。在應用中加上支援jta的第三方包,例如atomikos,JOTM等。
分散式事務的結構如圖所示:
圖中,1表示APP與資源管理器RM之間的介面,是資源管理器的本地介面或者XA介面。如果使用事務管理器TM來管理分散式事務, 則不需要app直接呼叫rm,即1介面不會使用。 2表示app與TM的介面,即UserTransaction,這個介面和普通的事務管理介面類似,是一個有提交和回滾等操作的介面。使用該介面,就像事務只處理一個數據源一樣。app通過該介面控制事務的提交或回滾。 3是TM與RM之間的介面,是一個兩階段提交(2 phase commit)的過程,兩階段提交簡單的說就是一個數據源的事務要提交兩次才算真正提交。該操作由TM控制,app不直接呼叫介面3。
下面通過一個demo介紹如何使用spring+mybatis+atomikos+tomcat構建在一個事務中涉及兩個資料來源的web應用。
demo功能:實現一個能成功提交和回滾的涉及兩個資料庫資料來源的XA事務。
demo將實現:
1.一次性在兩個資料庫的兩張表中各插入一條資料並提交。
2.一次性在兩個資料庫的兩張表中各插入一條資料並回滾。
測試方式:restful web api
使用工具:
spring 4.1.1.RELEASE
mybatis 3.2.7
atomikos 3.7.0
tomcat 7
在mysql中建立兩個schema,分別為dev和qa。並在裡面分別建立一張名字表。
schema:dev
table:namaDev
id | nameDev
scheme:qa
table:nameQa
id | nameQa
對應的sql為
CREATE SCHEMA `qa` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci ;CREATE SCHEMA `dev` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci ; CREATE TABLE `dev`.`nameDev` ( `id` BIGINT NOT NULL AUTO_INCREMENT , `nameDev` VARCHAR(45) NULL , PRIMARY KEY (`id`) , UNIQUE INDEX `id_UNIQUE` (`id` ASC) ); CREATE TABLE `qa`.`nameQa` ( `id` BIGINT NOT NULL AUTO_INCREMENT , `nameQa` VARCHAR(45) NULL , PRIMARY KEY (`id`) , UNIQUE INDEX `id_UNIQUE` (`id` ASC) );
程式碼分析:
本專案使用spring框架,因此首先配置相關bean
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" 4 xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jdbc="http://www.springframework.org/schema/jdbc" 5 xmlns:rabbit="http://www.springframework.org/schema/rabbit" 6 xmlns:cache="http://www.springframework.org/schema/cache" xmlns:task="http://www.springframework.org/schema/task" 7 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd http://www.springframework.org/schema/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit.xsd 8 http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd"> 9 <context:component-scan base-package="com.xy"> 10 <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> 11 </context:component-scan> 12 <context:property-placeholder location="classpath:context/database.properties"/> 13 <tx:annotation-driven/> 14 15 <bean id="abstractXADataSource" class="com.atomikos.jdbc.AtomikosDataSourceBean" init-method="init" 16 destroy-method="close" abstract="true"> 17 <property name="xaDataSourceClassName" value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource"/> 18 <property name="poolSize" value="10" /> 19 <property name="minPoolSize" value="10"/> 20 <property name="maxPoolSize" value="30"/> 21 <property name="borrowConnectionTimeout" value="60"/> 22 <property name="reapTimeout" value="20"/> 23 <!-- 最大空閒時間 --> 24 <property name="maxIdleTime" value="60"/> 25 <property name="maintenanceInterval" value="60"/> 26 <property name="loginTimeout" value="60"/> 27 <property name="testQuery"> 28 <value>select 1</value> 29 </property> 30 </bean> 31 32 <bean id="qadataSource" parent="abstractXADataSource"> 33 <!-- value只要兩個資料來源不同就行,隨便取名 --> 34 <property name="uniqueResourceName" value="mysql/sitestone1" /> 35 <property name="xaDataSourceClassName" 36 value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" /> 37 <property name="xaProperties"> 38 <props> 39 <prop key="URL">${qa.db.url}</prop> 40 <prop key="user">${qa.db.user}</prop> 41 <prop key="password">${qa.db.password}</prop> 42 <prop key="pinGlobalTxToPhysicalConnection">true</prop> 43 </props> 44 </property> 45 </bean> 46 47 <bean id="devdataSource" parent="abstractXADataSource"> 48 <!-- value只要兩個資料來源不同就行,隨便取名 --> 49 <property name="uniqueResourceName" value="mysql/sitestone" /> 50 <property name="xaDataSourceClassName" 51 value="com.mysql.jdbc.jdbc2.optional.MysqlXADataSource" /> 52 <property name="xaProperties"> 53 <props> 54 <prop key="URL">${dev.db.url}</prop> 55 <prop key="user">${dev.db.user}</prop> 56 <prop key="password">${dev.db.password}</prop> 57 <prop key="pinGlobalTxToPhysicalConnection">true</prop> 58 </props> 59 </property> 60 </bean> 61 62 63 64 <bean id="qasqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> 65 <property name="dataSource" ref="qadataSource" /> 66 <property name="mapperLocations" value="classpath*:com/xy/dao/*.xml" /> 67 </bean> 68 69 <bean id="devsqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> 70 <property name="dataSource" ref="devdataSource" /> 71 <property name="mapperLocations" value="classpath*:com/xy/daodev/*.xml" /> 72 </bean> 73 74 <bean id="atomikosTransactionManager" class="com.atomikos.icatch.jta.UserTransactionManager" 75 init-method="init" destroy-method="close"> 76 <property name="forceShutdown"> 77 <value>true</value> 78 </property> 79 </bean> 80 <bean id="atomikosUserTransaction" class="com.atomikos.icatch.jta.UserTransactionImp"> 81 <property name="transactionTimeout" value="300" /> 82 </bean> 83 84 <bean id="transactionManager" 85 class="org.springframework.transaction.jta.JtaTransactionManager"> 86 <property name="transactionManager"> 87 <ref bean="atomikosTransactionManager"/> 88 </property> 89 <property name="userTransaction"> 90 <ref bean="atomikosUserTransaction"/> 91 </property> 92 <!-- 必須設定,否則程式出現異常 JtaTransactionManager does not support custom isolation levels by default --> 93 <property name="allowCustomIsolationLevels" value="true"/> 94 95 </bean> 96 97 98 99 <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">100 <property name="basePackage" value="com.xy.dao"/>101 <property name="sqlSessionFactoryBeanName" value="qasqlSessionFactory" />102 </bean>103 104 <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">105 <property name="basePackage" value="com.xy.daodev"/>106 <property name="sqlSessionFactoryBeanName" value="devsqlSessionFactory" />107 </bean>108 </beans>
其中qadataSource和devdataSource是對應兩個資料庫的資料來源,qasqlSessionFactory和devsqlSessionFactory是mybatis的sessionfactory,兩個MapperScannerConfigurer自動將不同資料來源的sql語句檔案與interface自動裝配起來,atomikosTransactionManager會自動管理兩個atomikos的資料來源的事務,即resource manager,atomikosUserTransaction為最上層的事務管理器為transaction manager。
網易雲免費體驗館,0成本體驗20+款雲產品!
更多網易技術、產品、運營經驗分享請點選。