赞
踩
Seata: Simple Extensible Autonomous Transaction Architecture Seata
是一款开源的分布式事务解决方案,致力于提供高性能和简单易用的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA
事务模式,为用户打造一站式的分布式解决方案。 —— 引用自 SEATA 官方文档
开源时间:2019年1月
Seata AT模式是基于二阶段提交的事务模式,优点是对原有业务无入侵性,入门简单。
在 AT 模式下,用户只需关注自己的“业务 SQL”,用户的 “业务 SQL” 作为一阶段,Seata 框架会自动生成事务的二阶段提交和回滚操作。
下载seata-server-1.1.0服务端 :
下载地址:https://github.com/seata/seata/releases/download/v1.1.0/seata-server-1.1.0.zip
下载seata-server-0.0.9服务端 :(1.1.0缺少一些配置需要从0.9版本里找)
下载地址:https://github.com/seata/seata/releases/download/v0.9.0/seata-server-0.9.0.zip
seata-server中,/conf目录下,有两个配置文件,需要结合自己的情况来修改。
本次demo中使用了Nacos作为注册、配置中心,Seata全局事务选择数据库存储方式。配置如下
registry{}中是注册中心相关配置,config{}中是配置中心相关配置。seata中,注册中心和配置中心是分开实现的。
#注册中心配置选项 registry { # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa type = "nacos" #使用nacos nacos { serverAddr = "localhost" #设置配置中心地址 namespace = "" #namespace的命名空间为空的话默认是public cluster = "default" } ... } #配置中心配置选项 config { # file、nacos 、apollo、zk、consul、etcd3 type = "nacos" #使用nacos nacos { serverAddr = "localhost" #同上 namespace = "" } ... }
里面有事务组配置,锁配置,事务日志存储等相关配置信息。
## transaction log store, only used in seata-server store { ## store mode: file、db mode = "db" ## file store property file { ## store location dir dir = "sessionStore" # branch session size , if exceeded first try compress lockkey, still exceeded throws exceptions maxBranchSessionSize = 16384 # globe session size , if exceeded throws exceptions maxGlobalSessionSize = 512 # file buffer size , if exceeded allocate new buffer fileWriteBufferCacheSize = 16384 # when recover batch read size sessionReloadReadSize = 100 # async, sync flushDiskMode = async } ## database store property db { ## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/BasicDataSource(dbcp) etc. datasource = "druid" ## mysql/oracle/h2/oceanbase etc. dbType = "mysql" driverClassName = "com.mysql.jdbc.Driver" url = "jdbc:mysql://127.0.0.1:3306/seata" user = "root" password = "root" minConn = 1 maxConn = 10 globalTable = "global_table" branchTable = "branch_table" lockTable = "lock_table" queryLimit = 100 } }
Seata-server v1.x release版缺少初始化配置相关文件,因此需要从v0.9版本导入。
从Seata-server v0.9版本复制 nacos.config.*(3个文件)到Seata server v1.1.0。
需要修改的配置如下:
#存储模式设置为数据库存储。 store.mode=db #设置存储数据库相关信息。 store.db.driverClassName=com.mysql.jdbc.Driver store.db.url=jdbc:mysql://172.16.103.27:3306/seata?useUnicode=true store.db.user=root store.db.password=root store.db.branch.table=branch_table store.db.datasource=dbcp store.db.dbType=mysql store.db.global.table=global_table store.db.lockTable=lock_table ..... #设置业务系统相关配置(vrgroupMapping)。 #---事务组(驼峰命名为v1.0以上版本格式。) service.vgroupMapping.tx_tison_group=default #添加业务系统中子事务:(3个微服务) #---事务组中子事务storage-service service.vgroupMapping.storage-service-fescar-service-group=default #---事务组中子事务order-service service.vgroupMapping.order-service-fescar-service-group=default #---事务组中子事务account-service service.vgroupMapping.account-service-fescar-service-group=default ......
特别注意事项:
关于nacos-config 的坑,官方更新1.0之后对配置的key做了一些升级。统一格式命名为驼峰命名,否则在启动seata服务的时候会报错,报错也会打印找不到指定的key。
例如:store.db.driver-class-name 改成 store.db.driverClassName=com.mysql.jdbc.Driver
当Server端存储模式为db时,需要创建global_table、branch_table、lock_table。
当Seata设置Nacos为注册中心时,需要把Seata的配置初始化到Nacos配置中心。
方式1:脚本导入
在seata1.0的包里已经没有提供 nacos-config.txt 的配置文件及nacos-config.sh。只能用seata0.9版本,在seata\conf\nacos-config.txt、nacos-config.sh。该文件可通过nacos-config.sh脚本导入。
执行脚本的命令:
sh nacos-config.sh -h localhost -p 8848
方式2:http注册 (window系统不支持sh命令时)
可以自己写个post请求脚本或使用postMan工具,将nacos-config.txt里的所有参数注册到nacos。例如下面
请求方式:POST 请求地址:http://192.168.1.151:8848/nacos/v1/cs/configs? 封装的参数格式:
{
dataId: store.db.driverClassName
namespace:public
group:SEATA_CONFIG_GROUP
content:com.mysql.jdbc.Driver
}
启动路径在 seata\bin\seata-server.bat。对应的nacos注册中心的服务列表对应的命名空间里就会有该服务。
编写简单的项目模拟seata分布式事务 分别为:订单服务、库存服务、用户服务,核心模块。 springcloud seata+nacos+feign+mybatis-plus
core:通用依赖及Seata初始化配置工具
order:订单服务
storage:库存服务
user:用户服务
其中order-service通过FeignClient调用user-service进行扣款处理,调用storage-service进行库存减少处理。
事务组概念请见官网:事务分组
配置Seata管理分布式事务时,需要保证Server端(TC)的事务分组和子事务(RM)的事务分组保持一致。
每个微服务,都需要注册RM(资源管理器)到Seata。
#resource/registry.config registry { type = "nacos" nacos { serverAddr = "172.16.103.27:8848" namespace = "" cluster = "default" } } config { type = "nacos" nacos { #需要与Seata初始化配置保持一致 serverAddr = "172.16.103.27:8848" namespace = "" group = "SEATA_CONFIG_GROUP" cluster = "default" } }
注:v1.0版本之后,Seata支持yml/properties配置,本次demo中并未使用。
在事务链涉及的服务的数据库中新建 undo_log 表用来存储 UndoLog 信息,用于二阶段回滚操作,表中包含xid、branchId、rollback_info 等关键字段信息。
1.8 2.2.0.RELEASE 1.1.0 2.0.0.RELEASE … … com.alibaba.cloud spring-cloud-alibaba-seata ${spring-cloud-alibaba-seata.version} io.seata seata-all io.seata seata-all ${seata.version} … …
注意事项
seata-all 0.x版本和seata-all 1.x版本存在不兼容问题,spring-cloud-alibaba-seata中又包含seata-all低版本,注意排除。
seata-spring-boot-starter
1.0.0可用于替换seata-all,GlobalTransactionScanner自动初始化(依赖SpringUtils) 若其他途径实现GlobalTransactionScanner初始化,请保证io.seata.spring.boot.autoconfigure.util.SpringUtils先初始化; starter使用file配置中心时默认开启数据源自动代理
spring-cloud-alibaba-seata
2.1.0内嵌seata-all 0.7.1,2.1.1内嵌seata-all 0.9.0,2.2.0内嵌seata-spring-boot-starter 1.0.0
2.1.0和2.1.1兼容starter解决方案:
@SpringBootApplication注解内exclude掉spring-cloud-alibaba-seata内的com.alibaba.cloud.seata.GlobalTransactionAutoConfiguration
3.5 添加全局事务
/** * GlobalTransactional 分布式事务注解 */ @Override @GlobalTransactional public void generateOrder(String userId, String commodityCode, Integer count){ //假设商品默认价格为:100元 int unitPrice = 100; int money = count * 100; //扣除用户的余额 DefaultResult defaultResult = userServiceClient.deduction(userId,money); //如果状态不为200直接返回 if (defaultResult.getStatus() != 200) { //回滚事务 throw new RuntimeException(defaultResult.getMsg()); } //如果用户扣除成功,生成订单 OrderInfo orderInfo = new OrderInfo(); orderInfo.setCommodityCode(commodityCode); orderInfo.setMoney(money); orderInfo.setCount(count); orderInfo.setUserId(userId); save(orderInfo); String xid = RootContext.unbind(); //扣除库存 defaultResult = storageServiceClient.deduction(commodityCode,count); if (defaultResult.getStatus() != 200) { //抛出异常,回滚事务 throw new RuntimeException(defaultResult.getMsg()); } }
全局事务发生异常时,不回滚部分事务
String xid = RootContext.unbind(); //解绑Xid
... //不需要回顾的处理。
RootContext.bind(xid); //重新绑定xid
仅支持ACIM模式的关系型数据库事务,自动回滚(基于SQL自动补偿)
需要数据库带有InnoDB引擎
官方网站支持数据库列表:MySQL、Oracle、PostgreSQL
不支持文件相关操作的回滚。
不支持非关系型数据库的回滚。
只能在JDK 8以上环境中使用(Seata限制)
附录
1.官方链接
官方:https://seata.io/zh-cn/docs/overview/terminology.html
Git:https://github.com/seata
资源目录:https://seata.io/zh-cn/docs/ops/deploy-guide-beginner.html
2.常见问题
1.registry.conf修改问题
registry.conf修改为application.yml后异常:
Issue: 将配置参数改为application.yml后的问题
I have searched the issues of this repository and believe that this is not a duplicate.
Ⅰ. Issue Description
尝试将registry.conf和file.conf配置到application.yml中,目前发现一下两个问题,
undo相关配置不生效(估计seata.client.rm.lock相关属性也有类似问题)
service.disable-global-transaction不生效
2.注册类型为null异常
经排查,必须在@SpringApplication注解内手动exclude掉spring-cloud-alibaba-seata内的com.alibaba.cloud.seata.GlobalTransactionAutoConfiguration自动装配类,使用seata-spring-boot-starter内的io.seata.spring.boot.autoconfigure.SeataAutoConfiguration对GlobalTransactionScanner进行装配。否则项目运行时会优先使用GlobalTransactionAutoConfiguration进行装配,导致在启动加载阶段报错(”io.seata.common.exception.NotSupportYetException: not support register type: null“),报错位置为io.seata.spring.boot.autoconfigure.provider.SpringBootConfigurationProvider#get(String dataId),该方法在使用seata的spring工具类SpringUtils.getBean(propertyClass)从ApplicationContext中获取ConfigProperties.class的Bean时,因无法从容器中到符合要求的Bean而抛出NullPoinException异常.
Issue: 同时引入spring-cloud-alibaba-seata和seata-spring-boot-starter依赖,出现启动阶段无法读取ConfigProperties.class(SpringUtils.getBean抛出空指针异常)
Ⅰ. Issue Description
经排查,必须在@SpringApplication注解内手动exclude掉spring-cloud-alibaba-seata内的com.alibaba.cloud.seata.GlobalTransactionAutoConfiguration自动装配类,使用seata-spring-boot-starter内的io.seata.spring.boot.autoconfigure.SeataAutoConfiguration对GlobalTransactionScanner进行装配。否则项目运行时会优先使用GlobalTransactionAutoConfiguration进行装配,导致在启动加载阶段报错(”io.seata.common.exception.NotSupportYetException: not support register type: null“),报错位置为io.seata.spring.boot.autoconfigure.provider.SpringBootConfigurationProvider#get(String dataId),该方法在使用seata的spring工具类SpringUtils.getBean(propertyClass)从ApplicationContext中获取ConfigProperties.class的Bean时,因无法从容器中到符合要求的Bean而抛出NullPoinException异常.
依赖列表:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-seata</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-all</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
...
3.无法找到服务
no available service ‘null’ found, please make sure registry config correct
Seata 初始化配置到Nacos的配置文件有问题。
查看事务组名称是否相同。
Seata服务端中配置了子事务(客户端)vgroupMapping,参考见附录-参考配置-Seata配置导入Nacos。
查看Nacos保存的配置中,命名空间和组(Group)是否一致。
DEMO源码
demo参考:https://github.com/a970066364/spring-cloud-alibaba-seata
官方Demo: https://github.com/seata/seata-samples
参考配置
Seata配置导入Nacos
store.mode=db #存储模式设置为数据库存储。 store.db.driverClassName=com.mysql.jdbc.Driver store.db.url=jdbc:mysql://172.16.103.27:3306/seata?useUnicode=true store.db.user=root store.db.password=root store.db.branch.table=branch_table store.db.datasource=dbcp store.db.dbType=mysql store.db.global.table=global_table store.db.lockTable=lock_table store.db.maxConn=3 store.db.minConn=1 store.db.queryLimit=100 store.file.dir=file_store/data store.file.fileWriteBufferCacheSize=16384 store.file.flushDiskMode=async store.file.maxBranchSessionSize=16384 store.file.maxGlobalSessionSize=512 client.async.commit.buffer.limit=10000 client.lock.retry.internal=10 client.lock.retry.times=30 client.report.retry.count=5 client.support.spring.datasource.autoproxy=true metrics.enabled=false metrics.exporterList=prometheus metrics.exporterPrometheusPort=9898 metrics.registryType=compact recovery.asynCommittingRetryPeriod=1000 recovery.committingRetryPeriod=1000 recovery.rollbackingRetryPeriod=1000 recovery.timeoutRetryPeriod=1000 service.disable=false service.disableGlobalTransaction=false service.enableDegrade=false service.max.commit.retry.timeout=-1 service.max.rollback.retry.timeout=-1 #---事务组(驼峰命名为v1.0以上版本格式,下划线命名为v0.9以下版本格式,根据服务器版本选择。) service.vgroupMapping.tx_tison_group=default service.vgroup_mapping.tx_tison_group=default #---事务组中子事务storage-service service.vgroup_mapping.storage-service-fescar-service-group=default service.vgroupMapping.storage-service-fescar-service-group=default #---事务组中子事务order-service service.vgroup_mapping.order-service-fescar-service-group=default service.vgroupMapping.order-service-fescar-service-group=default #---事务组中子事务account-service service.vgroup_mapping.account-service-fescar-service-group=default service.vgroupMapping.account-service-fescar-service-group=default store.file.session.reload.read_size=100 transaction.undo.data.validation=true transaction.undo.log.delete.period=86400000 transaction.undo.log.save.days=7 transaction.undo.log.serialization=jackson transaction.undo.log.table=undo_log transport.compressor=none transport.heartbeat=true transport.serialization=seata transport.server=NIO transport.shutdown.wait=3 transport.threadFactory.bossThreadPrefix=NettyBoss transport.threadFactory.bossThreadSize=1 transport.threadFactory.clientSelectorThreadPrefix=NettyClientSelector transport.threadFactory.clientSelectorThreadSize=1 transport.threadFactory.clientWorkerThreadPrefix=NettyClientWorkerThread transport.threadFactory.serverExecutorThreadPrefix=NettyServerBizHandler transport.threadFactory.shareBossWorker=false transport.threadFactory.workerThreadPrefix=NettyServerNIOWorker transport.threadFactory.workerThreadSize=8 transport.type=TCP
官方资源链接:https://seata.io/zh-cn/docs/ops/deploy-guide-beginner.html
Seata相关yaml配置
以下配置为v1.0新增,用于替代项目中(client端)registry.conf和file.conf配置文件。
社区issue中有Bug记录——将配置参数改为application.yml后的问题,建议谨慎尝试。
seata: enabled: true application-id: applicationName tx-service-group: my_test_tx_group enable-auto-data-source-proxy: true use-jdk-proxy: false client: rm: async-commit-buffer-limit: 1000 report-retry-count: 5 table-meta-check-enable: false report-success-enable: false lock: retry-interval: 10 retry-times: 30 retry-policy-branch-rollback-on-conflict: true tm: commit-retry-count: 5 rollback-retry-count: 5 undo: data-validation: true log-serialization: jackson log-table: undo_log log: exceptionRate: 100 service: vgroup-mapping: my_test_tx_group: default grouplist: default: 127.0.0.1:8091 enable-degrade: false disable-global-transaction: false transport: shutdown: wait: 3 thread-factory: boss-thread-prefix: NettyBoss worker-thread-prefix: NettyServerNIOWorker server-executor-thread-prefix: NettyServerBizHandler share-boss-worker: false client-selector-thread-prefix: NettyClientSelector client-selector-thread-size: 1 client-worker-thread-prefix: NettyClientWorkerThread worker-thread-size: default boss-thread-size: 1 type: TCP server: NIO heartbeat: true serialization: seata compressor: none enable-client-batch-send-request: true config: type: file consul: server-addr: 127.0.0.1:8500 apollo: apollo-meta: http://192.168.1.204:8801 app-id: seata-server namespace: application etcd3: server-addr: http://localhost:2379 nacos: namespace: serverAddr: localhost group: SEATA_GROUP zk: server-addr: 127.0.0.1:2181 session-timeout: 6000 connect-timeout: 2000 username: "" password: "" registry: type: file consul: cluster: default server-addr: 127.0.0.1:8500 etcd3: cluster: default serverAddr: http://localhost:2379 eureka: application: default weight: 1 service-url: http://localhost:8761/eureka nacos: cluster: default server-addr: localhost namespace: redis: server-addr: localhost:6379 db: 0 password: cluster: default timeout: 0 sofa: server-addr: 127.0.0.1:9603 application: default region: DEFAULT_ZONE datacenter: DefaultDataCenter cluster: default group: SEATA_GROUP addressWaitTime: 3000 zk: cluster: default server-addr: 127.0.0.1:2181 session-timeout: 6000 connect-timeout: 2000 username: "" password: ""
SQL(MySQL)
服务端
CREATE TABLE IF NOT EXISTS `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`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8; -- the table to store BranchSession data CREATE TABLE IF NOT EXISTS `branch_table` ( `branch_id` BIGINT NOT NULL, `xid` VARCHAR(128) NOT NULL, `transaction_id` BIGINT, `resource_group_id` VARCHAR(32), `resource_id` VARCHAR(256), `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`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8; -- the table to store lock data CREATE TABLE IF NOT EXISTS `lock_table` ( `row_key` VARCHAR(128) NOT NULL, `xid` VARCHAR(96), `transaction_id` BIGINT, `branch_id` BIGINT NOT NULL, `resource_id` VARCHAR(256), `table_name` VARCHAR(32), `pk` VARCHAR(36), `gmt_create` DATETIME, `gmt_modified` DATETIME, PRIMARY KEY (`row_key`), KEY `idx_branch_id` (`branch_id`) ) ENGINE = InnoDB DEFAULT CHARSET = utf8;
客户端
-- for AT mode you must to init this sql for you business database. the seata server not need it. CREATE TABLE IF NOT EXISTS `undo_log` ( `id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT 'increment id', `branch_id` BIGINT(20) NOT NULL COMMENT 'branch transaction id', `xid` VARCHAR(100) NOT NULL COMMENT 'global transaction id', `context` VARCHAR(128) NOT NULL COMMENT 'undo_log context,such as serialization', `rollback_info` LONGBLOB NOT NULL COMMENT 'rollback info', `log_status` INT(11) NOT NULL COMMENT '0:normal status,1:defense status', `log_created` DATETIME NOT NULL COMMENT 'create datetime', `log_modified` DATETIME NOT NULL COMMENT 'modify datetime', PRIMARY KEY (`id`), UNIQUE KEY `ux_undo_log` (`xid`, `branch_id`) ) ENGINE = InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET = utf8 COMMENT ='AT transaction mode undo table';
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。