赞
踩
Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事务模式,为用户打造一站式的分布式解决方案。
Seata 的 AT 模式(Automatic Transaction)是一种无侵入的分布式事务解决方案。下面结合具体业务场景来分析其执行的原理。
订单系统
当用户下订单时,执行以下三步流程:
(1)订单系统保存订单
(2)订单系统调用库存服务,减少商品库存
(3)订单系统调用账户服务,扣减用户金额
这三步要作为一个整体事务进行管理,要么整体成功,要么整体失败。
下面是事务执行失败的情况,需要回滚。
在Seata AT 事务中,有三个AT 事务的角色:TC(事务协调器)、TM(事务管理器)和RM(资源管理器),其中 TM 和 RM 是嵌入在业务应用中的,而 TC 则是一个独立服务。
Seata Server 就是 TC,直接从官方仓库下载启动即可,下载地址:https://github.com/seata/seata/releases
启动文件:seata-server.bat
用文本编辑器打开文件,找到文件中这一行:
%JAVACMD% %JAVA_OPTS% -server -Xmx2048m -Xms2048m -Xmn1024m -Xss512k -XX:Sur......
看到 Seata Server 默认使用 2G 内存,测试环境我们可以把内存调低:
%JAVACMD% %JAVA_OPTS% -server -Xmx256m -Xms256m -Xmn128m -Xss512k -XX:Sur......
Seata 需要存储全局事务信息、分支事务信息、全局锁信息,这些数据需要设定存储的位置。
可以放在本地文件、配置中心、db数据库、redis缓存中,这里我们选择db数据库,还需要配置数据库的相关参数信息。
Seata Server 要向注册中心进行注册,这样,其他服务就可以通过注册中心去发现 Seata Server,与 Seata Server 进行通信。
Seata 支持多款注册中心服务:nacos 、eureka、redis、zk、consul、etcd3、sofa。
项目中我们使用 eureka 注册中心,这还需要在 registry.conf 文件中配置:eureka服务的连接地址、注册的服务名。
双击 seata-server.bat
启动 Seata Server。
可查看 Eureka 注册中心 Seata Server 的注册信息:
在订单项目中要启动全局事务,还要执行订单保存的分支事务。
打开父工程的pom.xml文件,把其中注释的代码取消注释,其即为seata依赖。
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-seata</artifactId> <version>${spring-cloud-alibaba-seata.version}</version> <exclusions> <exclusion> <artifactId>seata-all</artifactId> <groupId>io.seata</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>io.seata</groupId> <artifactId>seata-all</artifactId> <version>${seata.version}</version> </dependency>
TC 事务协调器通过“事务组”的方式将多个服务组织成一个全局事务。每个服务启动时要向TC注册,加入同一个事务组。
spring: application: name: order datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost/seata_order?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8 jdbcUrl: ${spring.datasource.url} username: root password: root cloud: alibaba: seata: tx-service-group: order_tx_group server: port: 8083 eureka: client: service-url: defaultZone: http://localhost:8761/eureka mybatis-plus: mapper-locations: classpath:mapper/*.xml type-aliases-package: cn.tedu.entity configuration: map-underscore-to-camel-case: true logging: level: cn.tedu.mapper: debug
需要从注册中心获得 TC 的地址,这里配置注册中心的地址。
TC 在注册中心注册的服务ID在 file.conf
中指定。
registry { # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa type = "eureka" nacos { serverAddr = "localhost" namespace = "" cluster = "default" } eureka { serviceUrl = "http://localhost:8761/eureka" # application = "default" # weight = "1" } redis { serverAddr = "localhost:6379" db = "0" password = "" cluster = "default" timeout = "0" } zk { cluster = "default" serverAddr = "127.0.0.1:2181" session.timeout = 6000 connect.timeout = 2000 username = "" password = "" } consul { cluster = "default" serverAddr = "127.0.0.1:8500" } etcd3 { cluster = "default" serverAddr = "http://localhost:2379" } sofa { serverAddr = "127.0.0.1:9603" application = "default" region = "DEFAULT_ZONE" datacenter = "DefaultDataCenter" cluster = "default" group = "SEATA_GROUP" addressWaitTime = "3000" } file { name = "file.conf" } } config { # file、nacos 、apollo、zk、consul、etcd3、springCloudConfig type = "file" nacos { serverAddr = "localhost" namespace = "" group = "SEATA_GROUP" } consul { serverAddr = "127.0.0.1:8500" } apollo { app.id = "seata-server" apollo.meta = "http://192.168.1.204:8801" namespace = "application" } zk { serverAddr = "127.0.0.1:2181" session.timeout = 6000 connect.timeout = 2000 username = "" password = "" } etcd3 { serverAddr = "http://localhost:2379" } file { name = "file.conf" } }
在这里我们指定 TC 的服务ID seata-server
:
vgroupMapping.order_tx_group = "seata-server"
order_tx_group 对应 application.yml 中注册的事务组名。
transport { # tcp udt unix-domain-socket type = "TCP" #NIO NATIVE server = "NIO" #enable heartbeat heartbeat = true # the client batch send request enable enableClientBatchSendRequest = true #thread factory for netty threadFactory { bossThreadPrefix = "NettyBoss" workerThreadPrefix = "NettyServerNIOWorker" serverExecutorThread-prefix = "NettyServerBizHandler" shareBossWorker = false clientSelectorThreadPrefix = "NettyClientSelector" clientSelectorThreadSize = 1 clientWorkerThreadPrefix = "NettyClientWorkerThread" # netty boss thread size,will not be used for UDT bossThreadSize = 1 #auto default pin or 8 workerThreadSize = "default" } shutdown { # when destroy server, wait seconds wait = 3 } serialization = "seata" compressor = "none" } service { #transaction service group mapping # order_tx_group 与 yml 中的 “tx-service-group: order_tx_group” 配置一致 # “seata-server” 与 TC 服务器的注册名一致 # 从eureka获取seata-server的地址,再向seata-server注册自己,设置group # 事务组对应使用的协调器:seata-server vgroupMapping.order_tx_group = "seata-server" #only support when registry.type=file, please don't set multiple addresses order_tx_group.grouplist = "127.0.0.1:8091" #degrade, current not support enableDegrade = false #disable seata disableGlobalTransaction = false } client { rm { asyncCommitBufferLimit = 10000 lock { retryInterval = 10 retryTimes = 30 retryPolicyBranchRollbackOnConflict = true } reportRetryCount = 5 tableMetaCheckEnable = false reportSuccessEnable = false } tm { commitRetryCount = 5 rollbackRetryCount = 5 } undo { dataValidation = true logSerialization = "jackson" logTable = "undo_log" } log { exceptionRate = 100 } }
Seata AT 事务对业务代码无侵入,全自动化处理全局事务,其功能是靠 Seata 的数据源代理工具实现的。
这里我们创建 Seata 的数据源代理,并排除 Spring 默认的数据源。
注意:
需要在业务层的业务方法上添加注解:
@Transactional
//本地事务
@GlobalTransactional
//开启全局事务
需要在启动类上添加注解:排除默认数据源。
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class) //排除默认的数据源自动配置类,使用自定义的自动配置
package cn.tedu; import com.zaxxer.hikari.HikariDataSource; import io.seata.rm.datasource.DataSourceProxy; 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 javax.sql.DataSource; /** * 自定义的数据源自动配置类 * 用来创建AT事务的数据源代理对象 */ @Configuration public class DSAutoConfiguration { @ConfigurationProperties(prefix = "spring.datasource") //注入数据库参数 @Bean //自定义数据源 public DataSource dataSource(){ return new HikariDataSource(); } @Primary //首选对象 @Bean //数据源代理对象 public DataSource dataSourceProxy(DataSource ds){ return new DataSourceProxy(ds); } }
仿照order中的yml文件,把以下代码添加至相应的位置即可。
jdbcUrl: ${spring.datasource.url}
cloud:
alibaba:
seata:
tx-service-group: order_tx_group
这两个文件与order模块中的文件完全相同,可直接复制到对应的目录下。
直接复制order中的DSAutoConfiguration类,到相应的目录下即可。
注意:
还需在业务层的实现类的业务方法上添加注解:@Transactional
//本地事务控制
还需在启动类上,添加排除默认数据源的注解:@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
//排除默认数据源
按顺序启动项目:
Eureka
Seata Server
Easy Id Generator
Storage
Account
Order
访问:http://localhost:8083/order/create?userId=1&productId=1&count=10&money=100
订单会调用库存和账户,这三个服务会分别启动一个分支事务,三个分支事务一起组成一个全局事务。
观察三个项目的控制台都有Seata AT事务的日志。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。