Spring Cloud集成分布式事务框架Seata 1.5.2(三)

简介: Spring Cloud集成分布式事务框架Seata 1.5.2
package com.example.awesomebusiness.api;
import com.example.accountapi.api.WalletApi;
import org.springframework.cloud.openfeign.FeignClient;
/**
 * account:注册的服务名称;
 * contextId:注册到spring中的Bean名称,保证唯一性
 * path:对应服务端的requestMapping里面的value值,没有的话,可不写
 *
 * @author zouwei
 * @className AccountApiClient
 * @date: 2022/9/18 14:42
 * @description:
 */
@FeignClient(name = "account", contextId = "walletApi", path = "/wallet")
public interface WalletApiClient extends WalletApi {
}
复制代码
package com.example.awesomebusiness.api;
import com.example.orderapi.api.OrderApi;
import org.springframework.cloud.openfeign.FeignClient;
/**
 * @author zouwei
 * @className OrderApiClient
 * @date: 2022/9/27 23:24
 * @description:
 */
@FeignClient(name = "order", contextId = "orderApi", path = "/order")
public interface OrderApiClient extends OrderApi {
}
复制代码
  • 接下来看看awesome-account的业务逻辑:
package com.example.awesomeaccount.service.impl;
import com.example.awesomeaccount.dao.mapper.WalletEnhanceMapper;
import com.example.awesomeaccount.service.IWalletService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
/**
 * @author zouwei
 * @className WalletServiceImpl
 * @date: 2022/9/27 21:20
 * @description:
 */
@Service
public class WalletServiceImpl implements IWalletService {
  @Resource
  private WalletEnhanceMapper walletEnhanceMapper;
  @Transactional
  @Override
  public Boolean deductMoney(String userId, long amount) {
    // 判断修改后的影响行数是否大于0
    // 对应sql:update wallet_tbl set money = money - #{amount,jdbcType=INTEGER} where user_id = #{userId,jdbcType=VARCHAR} and money <![CDATA[ >= ]]>#{amount,jdbcType=INTEGER}
    // WalletEnhanceMapper属于自定义的sql mapper,没有写在WalletMapper中,是因为避免重新生成Mapper的时候,自定义sql被覆盖
    return walletEnhanceMapper.deductMoney(userId, amount) > 0;
  }
}
复制代码

对应的controller如下:

package com.example.awesomeaccount.controller;
import com.example.accountapi.api.WalletApi;
import com.example.accountapi.model.AmountInfo;
import com.example.awesomeaccount.service.IWalletService;
import io.seata.core.context.RootContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * WalletApi接口在account-api模块中
 *
 * @author zouwei
 * @className WalletController
 * @date: 2022/9/18 14:58
 * @description:
 */
@Slf4j
@RestController
@RequestMapping("/wallet")
public class WalletController implements WalletApi {
  @Autowired
  private IWalletService walletService;
  @Override
  public Boolean deductMoney(AmountInfo amountInfo) {
    String userId = amountInfo.getUserId();
    long amount = amountInfo.getAmount();
    // 打印分布式事务XID
    log.warn("XID:{}", RootContext.getXID());
    // 扣款
    return walletService.deductMoney(userId, amount);
  }
}
复制代码
package com.example.accountapi.api;
import com.example.accountapi.model.AmountInfo;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
/**
 * 供生产者和消费者共同使用
 *
 * @author zouwei
 * @className AccountApi
 * @date: 2022/9/18 14:31
 * @description:
 */
public interface WalletApi {
  /**
   * 扣钱
   *
   * @param amountInfo
   * @return
   */
  @PostMapping("/deductMoney")
  Boolean deductMoney(@RequestBody AmountInfo amountInfo);
}
复制代码
  • awesome-orderawesome-storage服务也是类似的代码结构:
package com.example.awesomeorder.service.impl;
import com.example.awesomeorder.api.StockApiClient;
import com.example.awesomeorder.dao.entity.Order;
import com.example.awesomeorder.dao.mapper.OrderMapper;
import com.example.awesomeorder.service.IOrderService;
import com.example.storageapi.model.OrderInfo;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
import java.time.LocalDateTime;
/**
 * @author zouwei
 * @className OrderServiceImpl
 * @date: 2022/9/27 22:47
 * @description:
 */
@Service
public class OrderServiceImpl implements IOrderService {
  @Resource
  private OrderMapper orderMapper;
  @Resource
  private StockApiClient stockApiClient;
  /**
   * 创建订单
   *
   * @param userId
   * @param commodityCode
   * @param count
   * @param unitPrice
   * @return
   */
  @Transactional
  @Override
  public Boolean createOrder(String userId, String commodityCode, int count, long unitPrice) {
    // 构建待扣减的库存信息
    OrderInfo orderInfo = new OrderInfo();
    // 设置商品编码
    orderInfo.setCommodityCode(commodityCode);
    // 设置需要扣减的数量
    orderInfo.setCount(count);
    // 先构建库存
    if (stockApiClient.deductStock(orderInfo)) {
      // 扣减库存成功后,准备创建订单
      Order order = new Order();
      // 创建时间
      order.setCreateTime(LocalDateTime.now());
      // 用户ID
      order.setUserId(userId);
      // 数量
      order.setCount(count);
      // 商品编码
      order.setCommodityCode(commodityCode);
      // 单价
      order.setUnitPrice(unitPrice);
      // 创建订单
      return orderMapper.insert(order) > 0;
    }
    // 扣减库存失败,订单创建失败
    return Boolean.FALSE;
  }
}
复制代码
package com.example.awesomeorder.api;
import com.example.storageapi.api.StockApi;
import org.springframework.cloud.openfeign.FeignClient;
/**
 * @author zouwei
 * @className StockApiClient
 * @date: 2022/9/28 00:34
 * @description:
 */
@FeignClient(name = "storage", contextId = "stockApi", path = "/stock")
public interface StockApiClient extends StockApi {
}
复制代码
package com.example.awesomeorder.controller;
import com.example.awesomeorder.service.IOrderService;
import com.example.orderapi.api.OrderApi;
import com.example.orderapi.model.OrderInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * @author zouwei
 * @className OrderController
 * @date: 2022/9/27 22:55
 * @description:
 */
@RestController
@RequestMapping("/order")
public class OrderController implements OrderApi {
  @Autowired
  private IOrderService orderService;
  @Override
  public Boolean createOrder(OrderInfo orderInfo) {
    String commodityCode = orderInfo.getCommodityCode();
    int count = orderInfo.getCount();
    long unitPrice = orderInfo.getUnitPrice();
    String userId = orderInfo.getUserId();
    return orderService.createOrder(userId, commodityCode, count, unitPrice);
  }
}
package com.example.orderapi.api;
import com.example.orderapi.model.OrderInfo;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
/**
 * @author zouwei
 * @className OrderApi
 * @date: 2022/9/27 22:57
 * @description:
 */
public interface OrderApi {
  @PutMapping("/create")
  Boolean createOrder(@RequestBody OrderInfo orderInfo);
}
复制代码
  • 接下来,就剩下awesome-storage服务了:
package com.example.awesomestorage.service.impl;
import com.example.awesomestorage.dao.mapper.StockEnhanceMapper;
import com.example.awesomestorage.service.IStockService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource;
/**
 * @author zouwei
 * @className StockServiceImpl
 * @date: 2022/9/28 00:06
 * @description:
 */
@Service
public class StockServiceImpl implements IStockService {
  @Resource
  private StockEnhanceMapper stockEnhanceMapper;
  @Transactional
  @Override
  public Boolean deductStock(String commodityCode, int count) {
    // 扣减库存,判断影响行数是否大于0
    // sql如下: update stock_tbl set count = count - #{count,jdbcType=INTEGER} where commodity_code = #{commodityCode,jdbcType=VARCHAR} and count <![CDATA[ >= ]]> #{count,jdbcType=INTEGER}
    return stockEnhanceMapper.deductStock(commodityCode, count) > 0;
  }
}
复制代码
package com.example.awesomestorage.controller;
import com.example.awesomestorage.service.IStockService;
import com.example.storageapi.api.StockApi;
import com.example.storageapi.model.OrderInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * @author zouwei
 * @className StockController
 * @date: 2022/9/28 00:23
 * @description:
 */
@RestController
@RequestMapping("/stock")
public class StockController implements StockApi {
  @Autowired
  private IStockService stockService;
  @Override
  public Boolean deductStock(OrderInfo orderInfo) {
    String commodityCode = orderInfo.getCommodityCode();
    int count = orderInfo.getCount();
    return stockService.deductStock(commodityCode, count);
  }
}
package com.example.storageapi.api;
import com.example.storageapi.model.OrderInfo;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
/**
 * @author zouwei
 * @className StockApi
 * @date: 2022/9/28 00:26
 * @description:
 */
public interface StockApi {
  @PostMapping("/deduct")
  Boolean deductStock(@RequestBody OrderInfo orderInfo);
}
复制代码

至此,这几个服务的核心代码就全部搞定,我们千万别忘记了在项目入口类中加上对应的注解,比如awesome-business入口类:

package com.example.awesomebusiness;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
// 开启nacos,不过仅仅使用服务发现,不需要注册
@EnableDiscoveryClient(autoRegister = false)
// 开启OpenFeign
@EnableFeignClients
@SpringBootApplication
public class AwesomeBusinessApplication {
  public static void main(String[] args) {
    SpringApplication.run(AwesomeBusinessApplication.class, args);
  }
}
复制代码

awesome-order的入口类有些许不同:

package com.example.awesomeorder;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
// 增加mybatis的MapperScan注解
@MapperScan(basePackages = "com.example.awesomeorder.dao.mapper")
// RPC调用才需要OpenFeign
@EnableFeignClients
// 既要注册当前服务,也要发现其他服务,比如storage库存服务
@EnableDiscoveryClient
@SpringBootApplication
public class AwesomeOrderApplication {
  public static void main(String[] args) {
    SpringApplication.run(AwesomeOrderApplication.class, args);
  }
}
复制代码

如果小伙伴们对于mybatis的配置有疑虑的话,可以查看博客:手把手教你springboot集成mybatis;另外我怕也会将当前项目代码上传到github,想了解的小伙伴可以自己下载:awesome-seata

小结

通过以上示例,我们需要了解到以下几点,才能帮助我们更好地继承seata AT模式到我们的项目当中:

1.TM只负责全局事务的管理,比如全局事务的开启、提交或回滚;

2.TM可以和RM共存,也就是说,一个服务中,可以既有TM,也有RM;

3.RM只负责分支事务的管理,比如分支事务的注册、上报、提交或回滚;

4.多个分支事务是否在同一个分布式事务当中,是通过XID来判断的,XID是通过RPC来传递的;

5.TC相当于一个传话筒和记事本,负责记录和维护全局事务和分支事务的状态,以及下发TM提交或回滚的指令给到RM;

6.在RM当中是不需要使用@GlobalTransactional注解的,只需要使用@Transactional注解来保证分支事务的原子性;

7.所有的RM服务都需要有undo_log数据表,这样才能使用AT模式;


相关文章
|
9月前
|
数据可视化 Java BI
将 Spring 微服务与 BI 工具集成:最佳实践
本文探讨了 Spring 微服务与商业智能(BI)工具集成的潜力与实践。随着微服务架构和数据分析需求的增长,Spring Boot 和 Spring Cloud 提供了构建可扩展、弹性服务的框架,而 BI 工具则增强了数据可视化与实时分析能力。文章介绍了 Spring 微服务的核心概念、BI 工具在企业中的作用,并深入分析了两者集成带来的优势,如实时数据处理、个性化报告、数据聚合与安全保障。同时,文中还总结了集成过程中的最佳实践,包括事件驱动架构、集中配置管理、数据安全控制、模块化设计与持续优化策略,旨在帮助企业构建高效、智能的数据驱动系统。
447 1
将 Spring 微服务与 BI 工具集成:最佳实践
|
11月前
|
XML 人工智能 Java
Spring Boot集成Aviator实现参数校验
Aviator是一个高性能、轻量级的Java表达式求值引擎,适用于动态表达式计算。其特点包括支持多种运算符、函数调用、正则匹配、自动类型转换及嵌套变量访问,性能优异且依赖小。适用于规则引擎、公式计算和动态脚本控制等场景。本文介绍了如何结合Aviator与AOP实现参数校验,并附有代码示例和仓库链接。
698 0
|
11月前
|
安全 Java 数据库
第16课:Spring Boot中集成 Shiro
第16课:Spring Boot中集成 Shiro
1159 0
|
11月前
|
消息中间件 存储 Java
第15课: Spring Boot中集成ActiveMQ
第15课: Spring Boot中集成ActiveMQ
690 0
|
9月前
|
监控 Cloud Native Java
Spring Integration 企业集成模式技术详解与实践指南
本文档全面介绍 Spring Integration 框架的核心概念、架构设计和实际应用。作为 Spring 生态系统中的企业集成解决方案,Spring Integration 基于著名的 Enterprise Integration Patterns(EIP)提供了轻量级的消息驱动架构。本文将深入探讨其消息通道、端点、过滤器、转换器等核心组件,以及如何构建可靠的企业集成解决方案。
830 0
|
11月前
|
Java 关系型数据库 数据库连接
Spring Boot项目集成MyBatis Plus操作PostgreSQL全解析
集成 Spring Boot、PostgreSQL 和 MyBatis Plus 的步骤与 MyBatis 类似,只不过在 MyBatis Plus 中提供了更多的便利功能,如自动生成 SQL、分页查询、Wrapper 查询等。
1050 2
|
Java 数据库
在Java中使用Seata框架实现分布式事务的详细步骤
通过以上步骤,利用 Seata 框架可以实现较为简单的分布式事务处理。在实际应用中,还需要根据具体业务需求进行更详细的配置和处理。同时,要注意处理各种异常情况,以确保分布式事务的正确执行。
|
消息中间件 运维 数据库
Seata框架和其他分布式事务框架有什么区别
Seata框架和其他分布式事务框架有什么区别
622 153
|
存储 Java 关系型数据库
在Spring Boot中整合Seata框架实现分布式事务
可以在 Spring Boot 中成功整合 Seata 框架,实现分布式事务的管理和处理。在实际应用中,还需要根据具体的业务需求和技术架构进行进一步的优化和调整。同时,要注意处理各种可能出现的问题,以保障分布式事务的顺利执行。
1563 160