当前位置:   article > 正文

微服务分布式事务解决方案Seata_微服务事务解决方案

微服务事务解决方案

一、什么是Seata? 

Seata 是一款开源的分布式事务解决方案,致力于提供高性能和简单易用 的分布式事务服务。Seata 将为用户提供了 AT、TCC、SAGA 和 XA 事 务模式,为用户打造一站式的分布式解决方案(AT模式是阿里首推的模式, 阿里云上有商用版本的GTS[Global Transaction service 全局事务服务] ) 。

提示: 业务场景:

角色划分:

RM(ResourceManager 资源管理者)理解为我们的一个一个的微服务,也叫做事务的参与者. TM(TranactionManager 事务管理者) 也是我们的一个微服务,但是该微服务是 一个带头大哥,充当全局事务的发起者(决定了全局事务的开启,回滚,提交等) ***凡是我们的微服务中标注了@GlobalTransactional ,那么该微服务就会被看出 一个TM。我们业务场景中订单微服务就是一个事务发起者,同时也是一个RM

TC(全局事务的协调者):这里就是我们的Seata-server,用来保存全局事务,分支 事务,全局锁等记录,然后会通知各个RM进行回滚或者提交. 二:整体机制(两阶段提交协议的演变) 

工作原理

执行业务SQL update product set name = 'GTS' where name = 'TXC';

第一阶段:

1:解析 SQL:得到 SQL 的类型(UPDATE),表(product),条件(where id= '1')等相关的信息。

2:查询前镜像:根据解析得到的条件信息,生成查询语句,定位数据。

select id, name, since from product where name = 'TXC'; 

3:执行业务 SQL:更新这条记录的 name 为 'GTS'。

update product set name = 'GTS' where name = 'TXC';

4:查询后镜像:根据前镜像的结果,通过 主键 定位数据

select id, name, since from product where id = 1`; 

5:插入回滚日志:把前后镜像数据以及业务 SQL 相关的信息组成一条回滚日志记 录,插入到 UNDO_LOG 表中。

 {
"branchId": 641789253,
"undoItems": [{
"afterImage": {
"rows": [{
"fields": [{
"name": "id",
"type": 4,
"value": 1
 }, {
 "name": "name",
 "type": 12,
 "value": "GTS"
 }, {
 "name": "since",
 "type": 12,
 "value": "2014"
 }]
 }],
 "tableName": "product"
 },
 "beforeImage": {
 "rows": [{
 "fields": [{
 "name": "id",
 "type": 4,
 "value": 1
 }, {
 "name": "name",
 "type": 12,
 "value": "TXC"
 }, {
 "name": "since",
 "type": 12,
 "value": "2014"
 }]
 }],
 "tableName": "product"
 },
 "sqlType": "UPDATE"
 }],
 "xid": "xid:xxx"
 }

6:提交前,向 TC 注册分支:申请 product 表中,主键值等于 1 的记录的 全局 锁 。

7:本地事务提交:业务数据的更新和前面步骤中生成的 UNDO LOG 一并提交。

8:将本地事务提交的结果上报给 TC。

二阶段-回滚

1:收到 TC 的分支回滚请求,开启一个本地事务,执行如下操作

2:通过 XID 和 Branch ID 查找到相应的 UNDO LOG 记录。

3:数据校验:拿 UNDO LOG 中的后镜与当前数据进行比较,如果有不同,说明数 据被当前全局事务之外的动作做了修改

4:根据 UNDO LOG 中的前镜像和业务 SQL 的相关信息生成并执行回滚的语句:

5:提交本地事务。并把本地事务的执行结果(即分支事务回滚的结果)上报给 TC。

二阶段-提交

1:收到 TC 的分支提交请求,把请求放入一个异步任务的队列中,马上返回提交成 功的结果给 TC。

2:异步任务阶段的分支提交请求将异步和批量地删除相应 UNDO LOG 记录。

三:快速开始搭建Seata环境 3.1)Seata-server环境搭建 

二、快速开始搭建Seata环境

1.Seata-server环境搭建

第一步:https://github.com/seata/seata/releases 下载seata-server包

第二步: 解压我们的下载包seata-server包,解压的路径结构

第三步: 进入conf目录下 拿到db_store.sql脚本 ,然后再本地数据库创建一个 seata的数据库,执行脚本db_store.sql。

第四步: 修改conf目录下的file.conf文件

修改的节点: service节点

  1. service {
  2. #vgroup‐>rgroup
  3. //修改全局事务分组
  4. vgroup_mapping.prex_tx_group = "default"
  5. #only support single node
  6. #seata‐server的连接地址
  7. default.grouplist = "127.0.0.1:8091"
  8. #degrade current not support
  9. enableDegrade = false
  10. #disable
  11. disable = false
  12. #unit ms,s,m,h,d represents milliseconds, seconds, minutes, hours, days, default permanent
  13. max.commit.retry.timeout = "‐1"
  14. max.rollback.retry.timeout = "‐1"
  15. }

 修改store节点:

store {
## store mode: file、db
//存储模式 使用db
mode = "db"
file{//file的不要改

}
db {
## the implement of javax.sql.DataSource, such as DruidDataSource(druid)/Bas
icDataSource(dbcp) etc.
//数据源的类型
datasource = "druid"
## mysql/oracle/h2/oceanbase etc.
db‐type = "mysql"
driver‐class‐name = "com.mysql.jdbc.Driver"
//你seata库的地址
url = "jdbc:mysql://lcalhost:3306/seata"
user = "root"
password = "Zw726515"
min‐conn = 1
max‐conn = 3
global.table = "global_table"
branch.table = "branch_table"
lock‐table = "lock_table"
query‐limit = 100
}

 第五步:修改conf目录下的register.conf文件

修改registry节点的type类型为nacos 

  1. registry {
  2. # file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
  3. type = "nacos"
  4. nacos {
  5. serverAddr = "localhost:8848"
  6. namespace = ""
  7. cluster = "default"
  8. }
  9.  。。。。
  10.  。。。。
  11.  。。。。
  12.  }

修改config节点的type改为nacos

config {
# file、nacos 、apollo、zk、consul、etcd3
type = "nacos"

nacos {
serverAddr = "localhost:8848"
namespace = ""
cluster = "default"
}
 。。。。。。
 。。。。。。
 }

 第六步:修改conf文件下的nacos-config.txt文件

 第六步: 使用git的控制台 执行sh脚本 sh nacos-config.sh localhost 把seata的配置导入到nacos的配置中心上去

 

 第七步:启动 seata-server服务 进入seata的bin目录点击执行seata-server.bat

微服务搭建步骤

第一步:添加pom依赖

  1. <!‐‐seata‐‐>
  2. <dependency>
  3. <groupId>com.alibaba.cloud</groupId>
  4. <artifactId>spring‐cloud‐starter‐alibaba‐seata</artifactId>
  5. <exclusions>
  6. <exclusion>
  7. <artifactId>seata‐all</artifactId>
  8. <groupId>io.seata</groupId>
  9. </exclusion>
  10. </exclusions>
  11. </dependency>
  12. <dependency>
  13. <groupId>io.seata</groupId>
  14. <artifactId>seata‐all</artifactId>
  15. <version>${seata.version}</version>
  16. </dependency>

第二步:写注解 @SpringBootApplication(exclude = DataSourceAutoConfiguration.class)

  1. @EnableFeignClients
  2. @EnableDiscoveryClient
  3. @SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
  4. public class Tulingvip06MsAlibabaOrderApplication {
  5. public static void main(String[] args) {
  6. SpringApplication.run(Tulingvip06MsAlibabaOrderApplication.class, args);
  7. }
  8. }

微服务发起者需要写全局事务注解(这里是order服务为发起者)

@GlobalTransactional(name = "prex-create-order",rollbackFor = Exception.class)

  1. @GlobalTransactional(name = "prex‐create‐order",rollbackFor =
  2. Exception.class)
  3. @Override
  4. public void createOrder(Order order) {
  5. log.info("当前 XID: {}", RootContext.getXID());
  6. log.info("下单开始,用户:{},商品:{},数量:{},金额:{}", order.getUserId(),
  7. order.getProductId(), order.getCount(), order.getPayMoney());
  8. //创建订单
  9. order.setStatus(0);
  10. orderMapper.saveOrder(order);
  11. log.info("保存订单{}", order);
  12. //远程调用库存服务扣减库存
  13. log.info("扣减库存开始");
  14. remoteStorageService.reduceCount(order.getProductId(), order.getCount());
  15. log.info("扣减库存结束");
  16. //远程调用账户服务扣减余额
  17. log.info("扣减余额开始");
  18. remoteAccountService.reduceBalance(order.getUserId(), order.getPayMoney());
  19. log.info("扣减余额结束");
  20. //修改订单状态为已完成
  21. log.info("修改订单状态开始");
  22. orderMapper.updateOrderStatusById(order.getId(),1);
  23. log.info("修改订单状态结束");
  24. log.info("下单结束");
  25. }

 第三步:写配置添加代理数据源配置

  1. @Configuration
  2. @MapperScan(basePackages = {"com.demo.seata.mapper"})
  3. public class MyBatisConfig {
  4. /**
  5. * 从配置文件获取属性构造datasource,注意前缀,这里用的是hikari,根据自己情况配置,
  6. * 原生datasource前缀取"spring.datasource"
  7. *
  8. * @return
  9. */
  10. @Bean
  11. @ConfigurationProperties(prefix = "spring.datasource.hikari")
  12. public DataSource hikariDataSource() {
  13. return new HikariDataSource();
  14. }
  15. /**
  16. * 构造datasource代理对象,替换原来的datasource
  17. *
  18. * @param hikariDataSource
  19. * @return
  20. */
  21. @Primary
  22. @Bean("dataSource")
  23. public DataSourceProxy dataSourceProxy(DataSource hikariDataSource) {
  24. return new DataSourceProxy(hikariDataSource);
  25. }
  26. @Bean
  27. public SqlSessionFactoryBean sqlSessionFactory(DataSourceProxy dataSourcePr
  28. oxy) throws Exception {
  29. SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
  30. sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternRes
  31. olver()
  32. .getResources("classpath:/mybatis/mapper/**/*.xml"));
  33. sqlSessionFactoryBean.setConfigLocation(new PathMatchingResourcePatternReso
  34. lver().getResource("classpath:/mybatis/mybatis‐config.xml"));
  35. sqlSessionFactoryBean.setTypeAliasesPackage("com.tuling.seata.domin");
  36. sqlSessionFactoryBean.setDataSource(dataSourceProxy);
  37. return sqlSessionFactoryBean;
  38. }
  39. }

第四步:修改配置文件 yml中添加配置文件

  1. #seata配置(配置事务组 需要和seata‐server的配置一样)
  2. spring.cloud.alibaba.seata.tx‐service‐group=prex_tx_group

 修改file.conf 和register.conf文件(跟seata-server的改动一样)

 4:微服务测试

http://localhost:8081/order/create?userId=1&productId=1&count=1&payMoney=50

4.1)正常情况 

 

库存库 

 

4.2)异常情况,我们把支付服务人工模拟抛出异常。 

 库存库:

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/秋刀鱼在做梦/article/detail/813782
推荐阅读
相关标签
  

闽ICP备14008679号