SpringCloud Alibaba微服务实战七 - 分布式事务

简介: SpringCloud Alibaba微服务实战七 - 分布式事务

导读:本篇作为SpringCloud Alibaba微服务实战系列的第七篇,主要内容是使用Seata解决分布式事务问题。系列文章,欢迎持续关注。


场景说明


订单服务order-service需要对外提供创建订单的接口,创建订单的业务逻辑如下:

先调用本地的orderService保存订单操作,然后通过feign调用远程的accout-service进行账户余额扣减,最后再通过feign调用远程的product-service进行库存扣减操作。

关键的逻辑代码如下:

  • OrderController对外提供创建订单的接口
@PostMapping("/order/create")
public ResultData<OrderDTO> create(@RequestBody OrderDTO orderDTO){
  log.info("create order:{}",orderDTO);
  orderDTO.setOrderNo(UUID.randomUUID().toString());
  orderDTO.setAmount(orderDTO.getPrice().multiply(new BigDecimal(orderDTO.getCount())));
  orderService.createOrder(orderDTO);
  return ResultData.success("create order success");
}
  • OrderServiceImpl负责处理创建订单的业务逻辑
@Transactional(rollbackFor = RuntimeException.class)
@Override
public void createOrder(OrderDTO orderDTO) {
  Order order = new Order();
  BeanUtils.copyProperties(orderDTO,order);
  //本地存储Order
  this.saveOrder(order);
  //库存扣减
  productFeign.deduct(orderDTO.getProductCode(),order.getCount());
  //账户余额扣减
  accountFeign.reduce(orderDTO.getAccountCode(), orderDTO.getAmount());
}
@Transactional(rollbackFor = RuntimeException.class)
void saveOrder(Order order) {
  orderMapper.insert(order);
}

本地先保存订单,然后调用两个远程服务进行扣减操作。

  • AccountServiceImpl扣减账户余额
@Transactional(rollbackFor = RuntimeException.class)
@Override
public void reduceAccount(String accountCode, BigDecimal amount) {
  Account account = accountMapper.selectByCode(accountCode);
  if(null == account){
    thrownew RuntimeException("can't reduce amount,account is null");
  }
  BigDecimal subAmount = account.getAmount().subtract(amount);
  if(subAmount.compareTo(BigDecimal.ZERO) < 0){
    thrownew RuntimeException("can't reduce amount,account'amount is less than reduce amount");
  }
  account.setAmount(subAmount);
  accountMapper.updateById(account);
}

做些简单的校验,当账户余额不足的时候不允许扣减操作。

  • ProductServiceImpl扣减产品库存
@Transactional(rollbackFor = RuntimeException.class)
@Override
public void deduct(String productCode, Integer deductCount) {
  Product product = productMapper.selectByCode(productCode);
  if(null == product){
    thrownew RuntimeException("can't deduct product,product is null");
  }
  int surplus = product.getCount() - deductCount;
  if(surplus < 0){
    thrownew RuntimeException("can't deduct product,product's count is less than deduct count");
  }
  product.setCount(surplus);
  productMapper.updateById(product);
}

做些简单的校验,当产品库存不足时不允许扣减操作。

order-serviceproduct-serviceaccount-service分属不同的服务,当其中一个服务抛出异常无法提交时就会导致分布式事务,如当使用feign调用account-service执行扣减账户余额时,account-service校验账户余额不足抛出异常,但是order-service的保存操作不会回滚;或者是前两步执行成功但是product-service校验不通过前面的操作也不会回滚,这就导致了数据不一致,也就是分布式事务问题!


Seata解决方案


在Springcloud Alibaba体系中使用Seata作为分布式事务解决方案,大家可以访问seata官网去了解详情。这次我们先使用Seata的file配置解决上面出现的问题,后面再来对其改造。


下载安装Seata Server。

  • 从 Release 页面下载Seata Server
  • 下载完成后直接启动Server端服务。
    在Linux/Mac下
    $ sh ./bin/seata-server.sh
    在Windows下
    bin\seata-server.bat

引入seata组件

<dependency>
  <groupId>com.alibaba.cloud</groupId>
  <artifactId>spring-cloud-alibaba-seata</artifactId>
</dependency>


在配置文件中增加seata配置

spring:
  cloud:
    alibaba:
      seata:
        tx-service-group:${spring.application.name}-seata


Seata Client 配置修改

  • 将Seata Server 配置目录下的registry.conffile.conf 2个文件拷贝到微服务中的resources文件夹下
  • 修改拷贝后的registry.conf
registry{
  type = "file"
  file {
    name = "file.conf"
  }
}
config{
  type = "file"
  file {
    name = "file.conf"
  }
}
  • 修改file.conf主要修改如下三处:
    service.vgroup_mapping.后面的值修改为配置文件spring.cloud.alibaba.seata.tx-service-group的属性
    service.default.grouplist=修改为Seata Server的ip:端口
    support.spring.datasource.autoproxy的值修改为true,开启datasource自动代理

生成undo_log表

在微服务的业务库下执行如下语句,生成undo_log表

-- 此脚本必须初始化在你当前的业务数据库中,用于AT 模式XID记录。与server端无关(注:业务数据库)
droptable`undo_log`;
CREATETABLE`undo_log` (
  `id`bigint(20) NOTNULL AUTO_INCREMENT,
  `branch_id`bigint(20) NOTNULL,
  `xid`varchar(100) NOTNULL,
  `context`varchar(128) NOTNULL,
  `rollback_info` longblob NOTNULL,
  `log_status`int(11) NOTNULL,
  `log_created` datetime NOTNULL,
  `log_modified` datetime NOTNULL,
  `ext`varchar(100) DEFAULTNULL,
  PRIMARY KEY (`id`),
  UNIQUEKEY`ux_undo_log` (`xid`,`branch_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1DEFAULTCHARSET=utf8;


开启全局事务

在分布式事务方法入口添加注解@GlobalTransactional,这里只需要在createOrder方法上添加此注解即可!

@GlobalTransactional(name = "TX_ORDER_CREATE")
@Override
public void createOrder(OrderDTO orderDTO) {
  Order order = new Order();
  BeanUtils.copyProperties(orderDTO,order);
  //本地存储Order
  this.saveOrder(order);
  log.info("ORDER XID is: {}", RootContext.getXID());
  //账户余额扣减
  accountFeign.reduce(orderDTO.getAccountCode(), orderDTO.getAmount());
  //库存扣减
  productFeign.deduct(orderDTO.getProductCode(),orderDTO.getCount());
}

在代码中可以使用RootContext.getXID()获取全局xid


启动服务

服务正常启动后在Seata Server控制台可以看到注册信息


接口测试

改造完成后对接口进行测试,如果其他服务抛出异常会看到如下错误日志,再结合数据库数据观察是否正常回滚

执行过程中我们通过debug可以发现undo_log表会不断插入数据,在执行后又会被删除。

通过上面几步我们使用Seata实现了分布式事务,保证了数据的一致性,最后说一句Seata真香,你们要不要感受一下。至此本期的“SpringCloud Alibaba微服务实战七 - 分布式事务”篇也就该结束啦,咱们下期有缘再见!


目录
相关文章
|
16天前
|
Java UED Sentinel
微服务守护神:Spring Cloud Sentinel,让你的系统在流量洪峰中稳如磐石!
【8月更文挑战第29天】Spring Cloud Sentinel结合了阿里巴巴Sentinel的流控、降级、熔断和热点规则等特性,为微服务架构下的应用提供了一套完整的流量控制解决方案。它能够有效应对突发流量,保护服务稳定性,避免雪崩效应,确保系统在高并发下健康运行。通过简单的配置和注解即可实现高效流量控制,适用于高并发场景、依赖服务不稳定及资源保护等多种情况,显著提升系统健壮性和用户体验。
44 1
|
16天前
|
Cloud Native Java Nacos
微服务时代的新宠儿!Spring Cloud Nacos实战指南,带你玩转服务发现与配置管理,拥抱云原生潮流!
【8月更文挑战第29天】Spring Cloud Nacos作为微服务架构中的新兴之星,凭借其轻量、高效的特点,迅速成为服务发现、配置管理和治理的首选方案。Nacos(命名和配置服务)由阿里巴巴开源,为云原生应用提供了动态服务发现及配置管理等功能,简化了服务间的调用与依赖管理。本文将指导你通过五个步骤在Spring Boot项目中集成Nacos,实现服务注册、发现及配置动态管理,从而轻松搭建出高效的微服务环境。
84 0
|
16天前
|
Dubbo Java 应用服务中间件
💥Spring Cloud Dubbo火爆来袭!微服务通信的终极利器,你知道它有多强大吗?🔥
【8月更文挑战第29天】随着信息技术的发展,微服务架构成为企业应用开发的主流模式,而高效的微服务通信至关重要。Spring Cloud Dubbo通过整合Dubbo与Spring Cloud的优势,提供高性能RPC通信及丰富的生态支持,包括服务注册与发现、负载均衡和容错机制等,简化了服务调用管理并支持多种通信协议,提升了系统的可伸缩性和稳定性,成为微服务通信领域的优选方案。开发者仅需关注业务逻辑,而无需过多关心底层通信细节,使得Spring Cloud Dubbo在未来微服务开发中将更加受到青睐。
36 0
|
1天前
|
存储 NoSQL Redis
SpringCloud基础7——Redis分布式缓存,RDB,AOF持久化+主从+哨兵+分片集群
Redis持久化、RDB和AOF方案、Redis主从集群、哨兵、分片集群、散列插槽、自动手动故障转移
SpringCloud基础7——Redis分布式缓存,RDB,AOF持久化+主从+哨兵+分片集群
|
1天前
|
SQL NoSQL 数据库
SpringCloud基础6——分布式事务,Seata
分布式事务、ACID原则、CAP定理、Seata、Seata的四种分布式方案:XA、AT、TCC、SAGA模式
SpringCloud基础6——分布式事务,Seata
|
1天前
|
监控 Java Nacos
SpringCloud基础5——微服务保护、Sentinel
sentinel、雪崩问题、流量控制、隔离和降级、授权规则、规则持久化
SpringCloud基础5——微服务保护、Sentinel
|
6天前
|
前端开发 Java UED
"揭秘!如何以戏剧性姿态,利用SpringCloud铸就无懈可击的异常处理铁壁,让你的微服务架构稳如泰山,震撼业界!"
【9月更文挑战第8天】随着微服务架构的普及,Spring Cloud作为一套完整的微服务解决方案被广泛应用。在微服务架构中,服务间调用频繁且复杂,异常处理成为保障系统稳定性和用户体验的关键。传统的异常处理方式导致代码冗余,降低系统可维护性和一致性。因此,基于Spring Cloud封装统一的异常处理机制至关重要。这样不仅可以减少代码冗余、提升一致性,还增强了系统的可维护性,并通过统一的错误响应格式优化了用户体验。具体实现包括定义全局异常处理器、自定义业务异常以及在服务中抛出这些异常。这种方式体现了微服务架构中的“服务治理”和“契约先行”原则,有助于构建健壮、可扩展的系统。
20 2
|
14天前
|
Java 微服务 Spring
驾驭复杂性:Spring Cloud在微服务构建中的决胜法则
【8月更文挑战第31天】Spring Cloud是在Spring Framework基础上打造的微服务解决方案,提供服务发现、配置管理、消息路由等功能,适用于构建复杂的微服务架构。本文介绍如何利用Spring Cloud搭建微服务,包括Eureka服务发现、Config Server配置管理和Zuul API网关等组件的配置与使用。通过Spring Cloud,可实现快速开发、自动化配置,并提升系统的伸缩性和容错性,尽管仍需面对分布式事务等挑战,但其强大的社区支持有助于解决问题。
27 0
|
16天前
|
消息中间件 Java RocketMQ
微服务架构师的福音:深度解析Spring Cloud RocketMQ,打造高可靠消息驱动系统的不二之选!
【8月更文挑战第29天】Spring Cloud RocketMQ结合了Spring Cloud生态与RocketMQ消息中间件的优势,简化了RocketMQ在微服务中的集成,使开发者能更专注业务逻辑。通过配置依赖和连接信息,可轻松搭建消息生产和消费流程,支持消息过滤、转换及分布式事务等功能,确保微服务间解耦的同时,提升了系统的稳定性和效率。掌握其应用,有助于构建复杂分布式系统。
33 0