分布式事务(SeataClient)

问题场景

元数据

  • 库存 100
  • 订单记录为空

下单操作

    @Autowired
    RestTemplate restTemplate;

    /**
     * 下单
     *
     * @return
     */
    @Transactional  // 开启事务 异常后触发数据库回滚操作
    @Override
    public Order create(Order order) {
        // 插入订单
        orderMapper.insert(order);

        // 扣减库存 
        MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<String, Object>();paramMap.add("productId", order.getProductId());
        String msg = restTemplate.postForObject("http://localhost:8071/stock/reduct", paramMap, String.class);

        // 制造异常
        int a = 1 / 0;

        return order;
    }

结果

  >> 由于制造异常, 数据库事务回滚,订单没有入库;但库存确减少了1. 100 -> 99

问题解决

  • 上述问题是 A服务 调用 B服务,因此A服务的事务无法回滚B服务的操作
  • 微服务架构下,类似场景频繁遇到,因此就需要引入分布式事务组件 Seata

SeataServer

    >> SeataServer的搭建,上一篇已详述,这里就不多介绍了。

引入依赖

<!--nacos-服务注册发现-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

<!--1. 添加openfeign依赖 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>

<!--seata的依赖-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-seata</artifactId>
</dependency>

增加配置

  • 订单、库存服务均增加 seata 、nacos 配置
spring:
  datasource:
    username: root
    password: xxxxx
    url: jdbc:mysql://localhost:3307/seata_stock?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&
    driver-class-name: com.mysql.cj.jdbc.Driver
    type: com.alibaba.druid.pool.DruidDataSource

    #初始化时运行sql脚本
    schema: classpath:sql/schema.sql
    initialization-mode: never
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8847
        username: nacos
        password: nacos
    alibaba:
      seata:
        tx-service-group: my_test_tx_group
  application:
    name: stock-seata

seata:
  registry:
    # 配置seata的注册中心, 告诉seata client 怎么去访问seata server(TC)    type: nacos
    nacos:
      server-addr: 127.0.0.1:8847  # seata server 所在的nacos服务地址      
      application: seata-server    # seata server 的服务名seata-server ,如果没有修改可以不配      
      username: nacos
      password: nacos
      group: SEATA_GROUP          # seata server 所在的组,默认就是SEATA_GROUP,没有改也可以不配  
  config:
    type: nacos
    nacos:
      server-addr: 127.0.0.1:8847
      username: nacos
      password: nacos
      group: SEATA_GROUP

编写OpenFeign接口

 >> 启动类增加 @EnableFeignClients 注解

@FeignClient(value = "stock-seata", path = "/stock")
public interface StockFeignService {

//    @RequestMapping("/reduct")
//    public String reduct(@RequestParam(value="productId") Integer productId){
//        stockService.reduct(productId);
//        return "扣减库存";
//    }

    @RequestMapping("/reduct")
    String reduct(@RequestParam(value = "productId") Integer productId);
}

修改调用类

>> @Transactional 变更为 @GlobalTransactional

    @Autowired
    StockFeignService stockFeignService;

    /**
     * 下单
     * @return
     */
//    @Transactional
//    @Override
//    public Order create(Order order) {
//        // 插入订单
//        orderMapper.insert(order);
//
//
//        // 扣减库存
//        MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<String, Object>();
//        paramMap.add("productId", order.getProductId());
//
//        String msg = restTemplate.postForObject("http://localhost:8071/stock/reduct", paramMap,String.class );
//
//        // 制造异常
//        int a=1/0;
//
//        return order;
//    }

    /**
     * 下单
     * @return
     */
    @GlobalTransactional
    @Override
    public Order create(Order order) {
        // 插入订单
        orderMapper.insert(order);


        // 扣减库存
        stockFeignService.reduct(order.getProductId());

        // 制造异常
        int a=1/0;

        return order;
    }

启动服务运行测试

>> 库存表数量为 99 ,订单表记录为空

  • 订单失败场景

  • 订单成功场景 (注释掉制造异常的代码, 重启订单服务)
    @GlobalTransactional
    @Override
    public Order create(Order order) {
        // 插入订单
        orderMapper.insert(order);


        // 扣减库存
        stockFeignService.reduct(order.getProductId());

        // 制造异常
//        int a=1/0;

        return order;
    }
  • 下单成功, 库存 - 1 

总结 

       微服务框架下,分布式事务的控制已多见不怪了,Seata组件就是其中的一种解决方案;本文介绍了Seata客户端使用,希望对你有所帮助!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

菜逼の世界

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值