win10+springboot+dubbo+seataAT
seata详细中文文档见: http://seata.io/zh-cn/docs/overview/what-is-seata.html
一.SeaTa在win10系统中安装调试
1. 下载seata客户端 : 下载地址: http://seata.io/zh-cn/blog/download.html,这里下载的是:binary格式的文件包,当前最新的版本1.4.2
2. 解压后修改配置文件. 这里注册中心是用的zk.所以只需要重点关注zk的配置项和seata所需的事务管理的数据库地址的配置.其他配置项.后续项目中可按需调整
2.1registry.conf文件[seata服务注册配置]如下:
registry { # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa type = "zk" zk { cluster = "default" serverAddr = "127.0.0.1:2181" sessionTimeout = 6000 connectTimeout = 2000 username = "" password = "" } file { name = "file.conf" } } config { # file、nacos 、apollo、zk、consul、etcd3 type = "file" zk { serverAddr = "127.0.0.1:2181" sessionTimeout = 6000 connectTimeout = 2000 username = "" password = "" nodePath = "/seata/seata.properties" } file { name = "file.conf" } }
2.2 file.conf文件配置信息如下
-
transport {
# tcp, unix-domain-socket type = "TCP" #NIO, NATIVE server = "NIO" #enable heartbeat heartbeat = true # the client batch send request enable enableClientBatchSendRequest = false #thread factory for netty threadFactory { bossThreadPrefix = "NettyBoss" workerThreadPrefix = "NettyServerNIOWorker" serverExecutorThreadPrefix = "NettyServerBizHandler" shareBossWorker = false clientSelectorThreadPrefix = "NettyClientSelector" clientSelectorThreadSize = 1 clientWorkerThreadPrefix = "NettyClientWorkerThread" # netty boss thread size bossThreadSize = 1 #auto default pin or 8 workerThreadSize = "default" } shutdown { # when destroy server, wait seconds wait = 3 } serialization = "seata" compressor = "none" } store { ## store mode: file、db、redis mode = "db" ## database store property db { ## DataSource的实现类型, 可选择DruidDataSource(druid)/BasicDataSource(dbcp)/HikariDataSource(hikari)等类型 datasource = "druid" ## mysql/oracle/postgresql/h2/oceanbase etc. dbType = "mysql" driverClassName = "com.mysql.jdbc.Driver" ## if using mysql to store the data, recommend add rewriteBatchedStatements=true in jdbc connection param url = "jdbc:mysql://127.0.0.1:3306/seata?rewriteBatchedStatements=true" user = "root" password = "root" minConn = 1 maxConn = 100 globalTable = "global_table" branchTable = "branch_table" lockTable = "lock_table" queryLimit = 100 maxWait = 5000 } } ## server configuration, only used in server side server { recovery { #schedule committing retry period in milliseconds committingRetryPeriod = 1000 #schedule asyn committing retry period in milliseconds asynCommittingRetryPeriod = 1000 #schedule rollbacking retry period in milliseconds rollbackingRetryPeriod = 1000 #schedule timeout retry period in milliseconds timeoutRetryPeriod = 1000 } undo { logSaveDays = 7 #schedule delete expired undo_log in milliseconds logDeletePeriod = 86400000 } #check auth enableCheckAuth = true #unit ms,s,m,h,d represents milliseconds, seconds, minutes, hours, days, default permanent maxCommitRetryTimeout = "-1" maxRollbackRetryTimeout = "-1" rollbackRetryTimeoutUnlockEnable = false retryDeadThreshold = 130000 } ## metrics configuration, only used in server side metrics { enabled = false registryType = "compact" # multi exporters use comma divided exporterList = "prometheus" exporterPrometheusPort = 9898 } service { # 事务服务组,sp-user-seata-group可按需定义,但是要求这个和项目中的配置信息对的上 vgroupMapping.sp-user-seata-group = "default" #only support when registry.type=file, please don't set multiple addresses default.grouplist = "127.0.0.1:8091" #degrade, current not support enableDegrade = false #disable seata disableGlobalTransaction = false }
2.3 事务管理相关表结构如下
-- 每个需要进行分布式事务管理的库中的undo_log[日志记录]表--AT模式只配置这个表即可 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, PRIMARY KEY (`id`), UNIQUE KEY `ux_undo_log` (`xid`,`branch_id`) ) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8; -- TCC模式下,可定义有个单独的库或者在现有库中配置事务相关表: CREATE TABLE `branch_table` ( `branch_id` bigint(20) NOT NULL, `xid` varchar(128) NOT NULL, `transaction_id` bigint(20) DEFAULT NULL, `resource_group_id` varchar(32) DEFAULT NULL, `resource_id` varchar(256) DEFAULT NULL, `branch_type` varchar(8) DEFAULT NULL, `status` tinyint(4) DEFAULT NULL, `client_id` varchar(64) DEFAULT NULL, `application_data` varchar(2000) DEFAULT NULL, `gmt_create` datetime(6) DEFAULT NULL, `gmt_modified` datetime(6) DEFAULT NULL, PRIMARY KEY (`branch_id`), KEY `idx_xid` (`xid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `global_table` ( `xid` varchar(128) NOT NULL, `transaction_id` bigint(20) DEFAULT NULL, `status` tinyint(4) NOT NULL, `application_id` varchar(32) DEFAULT NULL, `transaction_service_group` varchar(32) DEFAULT NULL, `transaction_name` varchar(128) DEFAULT NULL, `timeout` int(11) DEFAULT NULL, `begin_time` bigint(20) DEFAULT NULL, `application_data` varchar(2000) DEFAULT NULL, `gmt_create` datetime DEFAULT NULL, `gmt_modified` datetime DEFAULT NULL, PRIMARY KEY (`xid`), KEY `idx_gmt_modified_status` (`gmt_modified`,`status`), KEY `idx_transaction_id` (`transaction_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `lock_table` ( `row_key` varchar(128) NOT NULL, `xid` varchar(128) DEFAULT NULL, `transaction_id` bigint(20) DEFAULT NULL, `branch_id` bigint(20) NOT NULL, `resource_id` varchar(256) DEFAULT NULL, `table_name` varchar(32) DEFAULT NULL, `pk` varchar(36) DEFAULT NULL, `gmt_create` datetime DEFAULT NULL, `gmt_modified` datetime DEFAULT NULL, PRIMARY KEY (`row_key`), KEY `idx_branch_id` (`branch_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
3.服务启动
3.1.先启动本地zk服务.
3.2 可在本地seata文件夹中黑窗口执行命令 : .\bin\seata-server.bat .即可看到是否正常启动
4. 代码实现:
4.1.导pom包:这里用的2.6.1版本的springboot , 0.2.0的dubbo , 1.4.2的seata
<!-- https://mvnrepository.com/artifact/io.seata/seata-spring-boot-starter --> <dependency> <groupId>io.seata</groupId> <artifactId>seata-spring-boot-starter</artifactId> <version>${seata.version}</version> <exclusions> <exclusion> <artifactId>seata-all</artifactId> <groupId>io.seata</groupId> </exclusion> </exclusions> </dependency> <!-- https://mvnrepository.com/artifact/io.seata/seata-all --> <dependency> <groupId>io.seata</groupId> <artifactId>seata-all</artifactId> <version>${seata.version}</version> <exclusions> <exclusion> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> </exclusion> </exclusions> </dependency> <!-- # seata zk连接主要依赖zkclient--> <dependency> <groupId>com.101tec</groupId> <artifactId>zkclient</artifactId> <version>0.11</version> <exclusions> <exclusion> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> </exclusion> </exclusions> </dependency>
4.2 .资源文件配置
# ************************** dataSource config start **************************** spring.datasource.driverClassName=com.mysql.cj.jdbc.Driver spring.datasource.url=jdbc:mysql://127.0.0.1:3306/cloudtravel_consumer?useAffectedRows=true&serverTimezone=UTC&characterEncoding=utf-8 spring.datasource.username=root spring.datasource.password=root spring.datasource.type=com.alibaba.druid.pool.DruidDataSource spring.datasource.druid.initialSize=5 spring.datasource.druid.min-idle=5 spring.datasource.druid.max-active=20 spring.datasource.druid.max-wait=60000 spring.datasource.druid.time-between-eviction-runs-millis=60000 spring.datasource.druid.validation-query=SELECT 1 FROM DUAL spring.datasource.druid.test-while-idle=true spring.datasource.druid.test-on-borrow=true spring.datasource.druid.test-on-return=false spring.datasource.druid.pool-prepared-statements=true spring.datasource.druid.max-pool-prepared-statement-per-connection-size=20 spring.datasource.druid.filter=stat spring.datasource.druid.merge-sql=true spring.datasource.druid.slow-sql-millis=5000 # 多数据源的情况下可指定对应的目录 mybatis.mapperLocations=classpath:mapper/*.xml # ************************** dataSource config end **************************** # ************************** seata config start **************************** seata.enabled=true seata.application-id=cloudtravel-consumer seata.tx-service-group=sp-user-seata-group seata.service.default.grouplist=127.0.0.1:8091 seata.service.vgroup-mapping.sp-user-seata-group=default seata.enableAutoDataSourceProxy=false seata.registry.type=zk ##seata.registry.file.name=registry.conf # ************************** seata config end ****************************
4.3.代码配置.我这里的druid数据库连接池是采用的手动配置
package com.cloudtravel.consumer.db; import com.alibaba.druid.pool.DruidDataSource; import io.seata.rm.datasource.DataSourceProxy; import io.seata.spring.annotation.GlobalTransactionScanner; import org.apache.ibatis.session.SqlSessionFactory; import org.mybatis.spring.SqlSessionFactoryBean; import org.mybatis.spring.annotation.MapperScan; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import javax.sql.DataSource; @Configuration @MapperScan(basePackages = MainDataSourceConfig.BASE_PACKAGE , sqlSessionFactoryRef = "sqlSessionFactory") public class MainDataSourceConfig { @Value("${mybatis.mapperLocations}") public String MAPPER_LOCATIONS; /** dao层接口所在的包 */ public static final String BASE_PACKAGE = "com.cloudtravel.consumer.dao";
/** 这里的配置项要和seata服务中的file.config文件中相应属性对的上 */
@Value("${seata.application-id}")
public String SEATA_APPLICATION_ID;
/** 这里的配置项要和seata服务中的file.config文件中相应属性对的上 */ @Value("${seata.tx-service-group}") public String TX_SERVICE_GROUP; @Bean("dataSource") @ConfigurationProperties(prefix = "spring.datasource") public DruidDataSource druidDataSource() { return new DruidDataSource(); } //for seata @Bean("dataSourceProxy") public DataSourceProxy dataSourceProxy(@Qualifier("dataSource") DataSource dataSource) { return new DataSourceProxy(dataSource); } @Bean("transactionManager") public DataSourceTransactionManager transactionManager(@Qualifier("dataSource") DataSource dataSource) { return new DataSourceTransactionManager(dataSource); } @Bean("sqlSessionFactory") public SqlSessionFactory sqlSessionFactory(@Qualifier("dataSourceProxy") DataSourceProxy dataSourceProxy)throws Exception { final SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean(); sqlSessionFactoryBean.setDataSource(dataSourceProxy); sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(MAPPER_LOCATIONS)); return sqlSessionFactoryBean.getObject(); } @Bean("globalTransactionScanner") public GlobalTransactionScanner globalTransactionScanner() { return new GlobalTransactionScanner(SEATA_APPLICATION_ID , TX_SERVICE_GROUP); } }
4.4. 代码中引入事务管理-AT模式.其实很简单,就是加个注解即可
@GlobalTransactional(name = "sp-user-seata-group" , rollbackFor = Exception.class) public String testAddUserAndSp() { ......正常的业务代码调用 }
4.5. 代码调试 :
1 找到 DefaultGlobalTransaction类,在begin / commit / rollback 三个方法处分别打个断点,打开数据库中的undo_log表,对应的业务数据表
2. 进行业务调用 . 可通过断点一步步看到业务表中的数据生成 , undo_log表中快照数据生成 , ccommit时删除undo_log表中本次快照数据 . rollback时,根据xid[全局事务id]对业务数据进行补偿删除的过程
5.TCC
.......待办
代码实战: github : https://github.com/singleStudent/cloudtravel gitee : https://gitee.com/everydayup/cloudtravel