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


相关文章
|
4天前
|
Java 数据安全/隐私保护 Spring
Java 中 Spring Boot 框架下的 Email 开发
Java 中 Spring Boot 框架下的 Email 开发
29 2
|
4天前
|
缓存 前端开发 Java
【框架】Spring 框架重点解析
【框架】Spring 框架重点解析
18 0
|
4天前
|
XML Java 数据格式
Spring框架入门:IoC与DI
【5月更文挑战第15天】本文介绍了Spring框架的核心特性——IoC(控制反转)和DI(依赖注入)。IoC通过将对象的创建和依赖关系管理交给容器,实现解耦。DI作为IoC的实现方式,允许外部注入依赖对象。文章讨论了过度依赖容器、配置复杂度等常见问题,并提出通过合理划分配置、使用注解简化管理等解决策略。同时,提醒开发者注意过度依赖注入和循环依赖,建议适度使用构造器注入和避免循环引用。通过代码示例展示了注解实现DI和配置类的使用。掌握IoC和DI能提升应用的灵活性和可维护性,实践中的反思和优化至关重要。
17 4
|
4天前
|
前端开发 Java 开发者
【JavaEE】面向切面编程AOP是什么-Spring AOP框架的基本使用
【JavaEE】面向切面编程AOP是什么-Spring AOP框架的基本使用
10 0
|
4天前
|
JSON 前端开发 Java
【JavaEE】让“单车变摩托”的神级框架—Spring MVC的深入讲解(下)
【JavaEE】让“单车变摩托”的神级框架—Spring MVC的深入讲解
7 0
|
4天前
|
JSON 前端开发 Java
【JavaEE】让“单车变摩托”的神级框架—Spring MVC的深入讲解(上)
【JavaEE】让“单车变摩托”的神级框架—Spring MVC的深入讲解
5 0
|
4天前
|
XML Java 应用服务中间件
【JavaEE】JavaEE进阶:框架的学习 - Spring的初步认识
【JavaEE】JavaEE进阶:框架的学习 - Spring的初步认识
8 0
|
4天前
|
NoSQL Java 关系型数据库
【Redis系列笔记】分布式锁
分布式锁:满足分布式系统或集群模式下多进程可见并且互斥的锁。 分布式锁的核心思想就是让大家都使用同一把锁,只要大家使用的是同一把锁,那么我们就能锁住线程,不让线程进行,让程序串行执行,这就是分布式锁的核心思路
131 2
|
4天前
|
存储 监控 NoSQL
【Redis】分布式锁及其他常见问题
【Redis】分布式锁及其他常见问题
16 0
|
4天前
|
NoSQL Java Redis
【Redis】Redis实现分布式锁
【Redis】Redis实现分布式锁
7 0