0
点赞
收藏
分享

微信扫一扫

Spring Cloud Alibaba(6.4 Seata——Spring Cloud AT 模式 + Feign调用)

90哦吼 2021-09-21 阅读 101

将使用 Seata 的 AT 模式,解决多个 Spring Cloud 服务下的分布式事务的问题,即服务提供者通过 SpringMVC 提供 Restful HTTP API 接口,服务消费者通过 Feign 进行 HTTP 调用。

Seata 提供了 spring-cloud-starter-alibaba-seata 项目,对 Spring Cloud 进行集成。实现原理是:

  • 服务消费者,使用 Seata 封装的 SeataFeignClient 过滤器,在使用 Feign 发起 HTTP 调用时,将 Seata 全局事务 XID 通过 Header 传递。
  • 服务提供者,使用 Seata 提供的 SpringMVC SeataHandlerInterceptor 拦截器,将 Header 中的 Seata 全局事务 XID 解析出来,设置到 Seata 上下文 中。

如此,我们便实现了多个 Spring Cloud 应用的 Seata 全局事务的传播

我们以用户购买商品的业务逻辑,来作为具体示例,一共会有三个 Spring Cloud 服务,分别对应自己的数据库。整体如下图所示:

本文的示例代码及SQL可从Gitee下载

主要代码说明

OrderServiceImpl

创建 OrderServiceImpl 类,实现创建订单的方法。代码如下:

@Service
public class OrderServiceImpl implements OrderService {

 private Logger logger = LoggerFactory.getLogger(getClass());

 @Autowired
 private OrderDao orderDao;

 @Autowired
 private AccountServiceFeignClient accountService;
 @Autowired
 private ProductServiceFeignClient productService;

 @Override
 @GlobalTransactional // <1>
 public Integer createOrder(Long userId, Long productId, Integer price) {
 Integer amount = 1; // 购买数量,暂时设置为 1。

 logger.info("[createOrder] 当前 XID: {}", RootContext.getXID());

 // <2> 扣减库存
 productService.reduceStock(new ProductReduceStockDTO().setProductId(productId).setAmount(amount));

 // <3> 扣减余额
 accountService.reduceBalance(new AccountReduceBalanceDTO().setUserId(userId).setPrice(price));

 // <4> 保存订单
 OrderDO order = new OrderDO().setUserId(userId).setProductId(productId).setPayAmount(amount * price);
 orderDao.saveOrder(order);
 logger.info("[createOrder] 保存订单: {}", order.getId());

 // 返回订单编号
 return order.getId();
 }

}

<1> 处,在类上,添加 Seata @GlobalTransactional 注解,声明全局事务

<2> 处,调用 ProductServiceFeignClient#reduceStock(productId, amount) 方法,通过 Feign 远程 HTTP 调用商品服务,进行扣除库存。代码如下:

// ProductServiceFeignClient.java
/**
 * `product-service` 服务的 Feign 客户端
 */
@FeignClient(name = "product-service")
public interface ProductServiceFeignClient {

 @PostMapping("/product/reduce-stock")
 void reduceStock(@RequestBody ProductReduceStockDTO productReduceStockDTO);

}

// ProductReduceStockDTO.java
/**
 * 商品减少库存 DTO
 */
public class ProductReduceStockDTO {

 /** 商品编号 */
 private Long productId;
 /** 数量 */
 private Integer amount;

 // ... 省略 setter/getter 方法
}
  • 远程调用失败,又或是扣除库存失败,都会抛出 Exception 异常,从而回滚 Seata 全局事务。

<3> 处,调用 AccountServiceFeignClient#reduceBalance(userId, price) 方法,通过 Feign 远程 HTTP 调用账户服务,进行扣除余额。代码如下:

// AccountServiceFeignClient.java
/**
 * `account-service` 服务的 Feign 客户端
 */
@FeignClient(name = "account-service")
public interface AccountServiceFeignClient {

 @PostMapping("/account/reduce-balance")
 void reduceBalance(@RequestBody AccountReduceBalanceDTO accountReduceBalanceDTO);

}
// AccountReduceBalanceDTO.java
/**
 * 账户减少余额 DTO
 */
public class AccountReduceBalanceDTO {

 /** 用户编号 */
 private Long userId;

 /** 扣减金额 */
 private Integer price;

 // ... 省略 setter/getter 方法
}
  • 远程调用失败,又或是扣除余额失败,都会抛出 Exception 异常,从而回滚 Seata 全局事务。

<4> 处,在全部调用成功后,调用 OrderDao 保存订单。

测试

使用 Postman 模拟调用 http://127.0.0.1:8081/order/create 创建订单的接口。

举报

相关推荐

0 条评论