Alibaba Seata 分布式事務管理
Seata 是什么?
Seata 是一款開源的分布式事務解決方案,致力於提供高性能和簡單易用的分布式事務服務。
Seata有3個基本組成部分:
事務協調器(TC): 維護全局事務和分支事務的狀態,驅動全局提交或回滾。
事務管理器TM: 定義全局事務的范圍:開始全局事務,提交或回滾全局事務。
資源管理器(RM): 管理分支事務處理的資源,與TC交談以注冊分支事務和報告分支事務的狀態,並驅動分支事務提交或回滾。
Seata管理的分布式事務的典型生命周期:
1、TM要求TC開始一項新的全局事務。TC生成代表全局事務的XID。
2、XID通過微服務的調用鏈傳播。
3、RM將本地事務注冊為XID到TC的相應全局事務的分支。
4、TM要求TC提交或回退相應的XID全局事務。
5、TC驅動XID的相應全局事務下的所有分支事務以完成分支提交或回滾。
Seata下載地址:
https://github.com/seata/seata/tags
解壓后:
1、如果是1.0之前的版本 打開 conf 文件夾,找到 db_store.sql mysql腳本文件在mysql中建表,1.1.0解壓后沒有,參考下面的建表語句:
-- the table to store GlobalSession data drop table if exists `global_table`; create table `global_table` ( `xid` varchar(128) not null, `transaction_id` bigint, `status` tinyint not null, `application_id` varchar(32), `transaction_service_group` varchar(32), `transaction_name` varchar(128), `timeout` int, `begin_time` bigint, `application_data` varchar(2000), `gmt_create` datetime, `gmt_modified` datetime, primary key (`xid`), key `idx_gmt_modified_status` (`gmt_modified`, `status`), key `idx_transaction_id` (`transaction_id`) ); -- the table to store BranchSession data drop table if exists `branch_table`; create table `branch_table` ( `branch_id` bigint not null, `xid` varchar(128) not null, `transaction_id` bigint , `resource_group_id` varchar(32), `resource_id` varchar(256) , `lock_key` varchar(128) , `branch_type` varchar(8) , `status` tinyint, `client_id` varchar(64), `application_data` varchar(2000), `gmt_create` datetime, `gmt_modified` datetime, primary key (`branch_id`), key `idx_xid` (`xid`) ); -- the table to store lock data drop table if exists `lock_table`; create table `lock_table` ( `row_key` varchar(128) not null, `xid` varchar(96), `transaction_id` long , `branch_id` long, `resource_id` varchar(256) , `table_name` varchar(32) , `pk` varchar(36) , `gmt_create` datetime , `gmt_modified` datetime, primary key(`row_key`) );
各個表對應功能:
- 全局事務---global_table
- 分支事務---branch_table
- 全局鎖-----lock_table
2、修改 file.conf 配置文件
因為我使用的是mysql8.0, 但是 /seata/lib 目錄下的mysql驅動是
所以如果你也是mysql8.0,需要刪除這個jar,重新添加:
3.修改conf文件夾下的 registry.conf, 根據自己的實際情況修改,nacos默認端口號是8848
使用案例:
以用戶購買商品的業務邏輯為例。整個業務邏輯由3個微服務提供支持:
- 倉儲服務:對給定的商品扣除倉儲數量。
- 訂單服務:根據采購需求創建訂單。
- 帳戶服務:從用戶帳戶中扣除余額。
如果該操作跨越3個不同的數據庫來操作,那么就存在分布式事務的問題。
測試開始:
1: 創建3個數據庫: seata_order(訂單)、 seata_storage(庫存)、seata_account(用戶的賬戶信息)
分別創建對應的表:order、storage、account表
2: 每個庫都創建 db_undo_log.sql 里面的表,同樣的Seata1.0之前的版本conf下面有,1.0之后的參考下面:
-- the table to store seata xid data -- 0.7.0+ add context -- you must to init this sql for you business databese. the seata server not need it. -- 此腳本必須初始化在你當前的業務數據庫中,用於AT 模式XID記錄。與server端無關(注:業務數據庫) -- 注意此處0.3.0+ 增加唯一索引 ux_undo_log drop table `undo_log`; CREATE TABLE `undo_log` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `branch_id` bigint(20) NOT NULL, `xid` varchar(100) NOT NULL, `context` varchar(128) NOT NULL, `rollback_info` longblob NOT NULL, `log_status` int(11) NOT NULL, `log_created` datetime NOT NULL, `log_modified` datetime NOT NULL, `ext` varchar(100) DEFAULT NULL, PRIMARY KEY (`id`), UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`) ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
建java服務項目:
1、maven依賴:
<dependencies> <!--cloud alibaba nacos- discovery--> <dependency> <groupId> com.alibaba.cloud </groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/com.alibaba.cloud/spring-cloud-starter-alibaba-seata --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-seata</artifactId> <exclusions> <exclusion> <artifactId>seata-all</artifactId> <groupId>io.seata</groupId> </exclusion> </exclusions> </dependency> <!--seata-all 與下載的server保持一致 --> <dependency> <groupId>io.seata</groupId> <artifactId>seata-all</artifactId> <version>1.1.0</version> </dependency> <!-- openFeign --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency>
<!-- Druid 數據庫連接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.5</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.2</version>
</dependency>
</dependencies>
2、yml:
server: port: 2001 spring: application: name: seata-order-service cloud: alibaba: seata: # 自定義的事務組名稱,需要與seata-server 的conf文件里面配置的一致 tx-service-group: default nacos: discovery: server-addr: localhost:8848 datasource: druid: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://192.168.2.241:3306/common_dev_scheme?useSSL=false&characterEncoding=utf8&autoReconnect=true username: root password: 123456 filters: stat # 設置最大數據庫連接數,設為0為無限制 maxActive: 20 # 配置初始化大小、最小、最大 initialSize: 1 # 最大等待時間 maxWait: 60000 # 始終保留在池中的最小連接數,如果連接驗證失敗將縮小至此值 minIdle: 1 timeBetweenEvictionRunsMillis: 6000 # 連接在池中保持空閑而不被回收的最小時間(毫秒) minEvictableIdleTimeMillis: 30000 validationQuery: select 'x' # 對池中空閑的連接是否進行驗證,驗證失敗則回收此連接(默認為false) testWhileIdle: true # 當從連接池中取出一個連接時是否進行驗證,若驗證失敗則從池中刪除該連接並嘗試取出另一個連接 testOnBorrow: true # 當一個連接使用完歸還到連接池時是否進行驗證 testOnReturn: false # 啟用游標緩存,這個對數據庫的性能提升很大 poolPreparedStatements: true # 要啟用PSCache,必須配置大於0,當大於0時,poolPreparedStatements自動觸發修改為true。在Druid中,不會存在Oracle下PSCache占用內存過多的問題,可以把這個數值配置大一些,比如說100 maxOpenPreparedStatements: 20 filter: stat: log-slow-sql: true slow-sql-millis: 2000 mybatis: mapper-locations: classpath:mapper/*.xml
3、拷貝seata-server包下面的 file.conf 和 registry.conf 到resource目錄下
我們需要排除掉SpringBoot默認自動注入的 DataSourceAutoConfiguration
Bean , 因為SEATA
是基於數據源攔截來實現的分布式事務,因此,我們需要自定義數據源配置信息:
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
package com.geekplus.broadway.ws.tally.application; import com.alibaba.druid.pool.DruidDataSource; import io.seata.rm.datasource.DataSourceProxy; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.transaction.SpringManagedTransactionFactory; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import javax.sql.DataSource; @Configuration public class DataSourceConfiguration { @Bean @ConfigurationProperties(prefix = "spring.datasource") public DataSource druidDataSource(){ DruidDataSource druidDataSource = new DruidDataSource(); return druidDataSource; } @Primary @Bean("dataSource") public DataSourceProxy dataSource(DataSource druidDataSource){ return new DataSourceProxy(druidDataSource); } @Bean public SqlSessionFactory sqlSessionFactory(DataSourceProxy dataSourceProxy)throws Exception{ SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(dataSourceProxy); sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver() .getResources("classpath*:/com.dw.study/*.xml")); sqlSessionFactoryBean.setTransactionFactory(new SpringManagedTransactionFactory()); return sqlSessionFactoryBean.getObject(); } }
該配置類,一般放在與啟動類相同的目錄即可!
使用全局事務: