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模式;


相关文章
|
15天前
|
算法 API Apache
Flink CDC:新一代实时数据集成框架
本文源自阿里云实时计算团队 Apache Flink Committer 任庆盛在 Apache Asia CommunityOverCode 2024 的分享,涵盖 Flink CDC 的概念、版本历程、内部实现及社区未来规划。Flink CDC 是一种基于数据库日志的 CDC 技术实现的数据集成框架,能高效完成全量和增量数据的实时同步。自 2020 年以来,Flink CDC 经过多次迭代,已成为功能强大的实时数据集成工具,支持多种数据库和数据湖仓系统。未来将进一步扩展生态并提升稳定性。
322 1
Flink CDC:新一代实时数据集成框架
|
14天前
|
数据采集 分布式计算 MaxCompute
MaxCompute 分布式计算框架 MaxFrame 服务正式商业化公告
MaxCompute 分布式计算框架 MaxFrame 服务于北京时间2024年09月27日正式商业化!
38 3
|
14天前
|
消息中间件 Java 对象存储
数据一致性挑战:Spring Cloud与Netflix OSS下的分布式事务管理
数据一致性挑战:Spring Cloud与Netflix OSS下的分布式事务管理
26 2
|
16天前
|
负载均衡 监控 Dubbo
分布式框架-dubbo
分布式框架-dubbo
|
1月前
|
运维 NoSQL Java
SpringBoot接入轻量级分布式日志框架GrayLog技术分享
在当今的软件开发环境中,日志管理扮演着至关重要的角色,尤其是在微服务架构下,分布式日志的统一收集、分析和展示成为了开发者和运维人员必须面对的问题。GrayLog作为一个轻量级的分布式日志框架,以其简洁、高效和易部署的特性,逐渐受到广大开发者的青睐。本文将详细介绍如何在SpringBoot项目中接入GrayLog,以实现日志的集中管理和分析。
114 1
|
16天前
|
XML 负载均衡 监控
分布式-dubbo-简易版的RPC框架
分布式-dubbo-简易版的RPC框架
|
2月前
|
存储 消息中间件 前端开发
Web2py框架下的神秘力量:如何轻松集成第三方API,让你的应用不再孤单!
【8月更文挑战第31天】在开发现代Web应用时,常需集成第三方服务如支付网关、数据存储等。本文将指导你使用Web2py框架无缝接入第三方API。通过实例演示从注册获取API密钥、创建控制器、发送HTTP请求到处理响应的全过程。利用`requests`库与Web2py的内置功能,轻松实现API交互。文章详细介绍了如何编写RESTful控制器,处理API请求及响应,确保数据安全传输。通过本教程,你将学会如何高效整合第三方服务,拓展应用功能。欢迎留言交流心得与建议。
37 1
|
1月前
|
分布式计算 资源调度 Hadoop
在YARN集群上运行部署MapReduce分布式计算框架
主要介绍了如何在YARN集群上配置和运行MapReduce分布式计算框架,包括准备数据、运行MapReduce任务、查看任务日志,并启动HistoryServer服务以便于日志查看。
42 0
|
2月前
|
测试技术 Java Spring
Spring 框架中的测试之道:揭秘单元测试与集成测试的双重保障,你的应用真的安全了吗?
【8月更文挑战第31天】本文以问答形式深入探讨了Spring框架中的测试策略,包括单元测试与集成测试的有效编写方法,及其对提升代码质量和可靠性的重要性。通过具体示例,展示了如何使用`@MockBean`、`@SpringBootTest`等注解来进行服务和控制器的测试,同时介绍了Spring Boot提供的测试工具,如`@DataJpaTest`,以简化数据库测试流程。合理运用这些测试策略和工具,将助力开发者构建更为稳健的软件系统。
40 0
|
2月前
|
测试技术 持续交付 开发者
Xamarin 高效移动应用测试最佳实践大揭秘,从框架选择到持续集成,让你的应用质量无敌!
【8月更文挑战第31天】竞争激烈的移动应用市场,Xamarin 作为一款优秀的跨平台开发工具,提供了包括单元测试、集成测试及 UI 测试在内的全面测试方案。借助 Xamarin.UITest 框架,开发者能便捷地用 C# 编写测试案例,如登录功能测试;通过 Xamarin 模拟框架,则可在无需真实设备的情况下模拟各种环境测试应用表现;Xamarin.TestCloud 则支持在真实设备上执行自动化测试,确保应用兼容性。结合持续集成与部署策略,进一步提升测试效率与应用质量。掌握 Xamarin 的测试最佳实践,对确保应用稳定性和优化用户体验至关重要。
48 0