微服务痛点-基于Dubbo + Seata的分布式事务(AT)模式 (4)

StorageDao

package cn.mushuwei.storage.dao; import org.apache.ibatis.annotations.Param; import org.springframework.stereotype.Repository; /** * @author jamesmsw * @date 2020/11/30 7:46 下午 */ @Repository("storageDao") public interface StorageDao { /** * 扣减商品库存 * * @param commodityCode 商品code * @param count 扣减数量 * @return */ int decreaseStorage(@Param("commodityCode") String commodityCode, @Param("count") Integer count); }

Storage.xml

<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace="cn.mushuwei.storage.dao.StorageDao"> <update> update storage set count = count - #{count} where commodity_code = #{commodityCode} </update> </mapper>

到此为止,商品库存操作逻辑,就大致介绍完毕了,其他Account模块是扣减用户余额的操作,Order模块是新建订单数据的,具体配置和上述描述的差不懂。

Business业务逻辑操作 package cn.mushuwei.business.controller; import cn.mushuwei.business.dto.BusinessDTO; import cn.mushuwei.business.service.BusinessService; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; /** * @author jamesmsw * @date 2020/12/1 9:48 下午 */ @RestController @RequestMapping("/business") @Slf4j public class BusinessController { @Resource(name = "businessService") private BusinessService businessService; @PostMapping("/buy") public String handleBusiness(@RequestBody BusinessDTO businessDTO){ log.info("请求参数:{}",businessDTO.toString()); Boolean result = businessService.handleBusiness(businessDTO); if (result) { return "ok"; } return "fail"; } }

在business模块中,我们对外暴露接口/business/buy,用于给用户进行下单操作。

业务逻辑处理

package cn.mushuwei.business.service.impl; import cn.mushuwei.business.dto.BusinessDTO; import cn.mushuwei.business.service.BusinessService; import cn.mushuwei.order.api.OrderApi; import cn.mushuwei.order.api.dto.OrderDTO; import cn.mushuwei.storage.api.StorageApi; import cn.mushuwei.storage.api.dto.CommodityDTO; import io.seata.core.context.RootContext; import io.seata.spring.annotation.GlobalTransactional; import lombok.extern.slf4j.Slf4j; import org.apache.dubbo.config.annotation.DubboReference; import org.springframework.stereotype.Service; /** * @author jamesmsw * @date 2020/12/1 9:37 下午 */ @Slf4j @Service("businessService") public class BusinessServiceImpl implements BusinessService { @DubboReference private StorageApi storageApi; @DubboReference private OrderApi orderApi; private boolean flag; @Override @GlobalTransactional(timeoutMills = 300000, name = "seata-demo-business") public Boolean handleBusiness(BusinessDTO businessDTO) { flag = true; log.info("开始全局事务,XID = " + RootContext.getXID()); CommodityDTO commodityDTO = new CommodityDTO(); commodityDTO.setCommodityCode(businessDTO.getCommodityCode()); commodityDTO.setCount(businessDTO.getCount()); boolean storageResult = storageApi.decreaseStorage(commodityDTO); OrderDTO orderDTO = new OrderDTO(); orderDTO.setUserId(businessDTO.getUserId()); orderDTO.setCommodityCode(businessDTO.getCommodityCode()); orderDTO.setOrderCount(businessDTO.getCount()); orderDTO.setOrderAmount(businessDTO.getAmount()); boolean orderResult = orderApi.createOrder(orderDTO); //打开注释测试事务发生异常后,全局回滚功能 // if (!flag) { // throw new RuntimeException("测试抛异常后,分布式事务回滚!"); // } if (!storageResult || !orderResult) { throw new RuntimeException("失败"); } return true; } }

我们使用@DubboReference分布调用storageApi和orderApi, 用于处理库存扣减和订单数据逻辑的操作。

@GlobalTransactional()在发起业务类中是必须要加的,用于全局锁等逻辑操作。

下单正常流程

微服务痛点-基于Dubbo + Seata的分布式事务(AT)模式

第一阶段:在正常的下单流程中,storage、order、account和business应用分别注册到Seata这个事务协调器上,当用户进行下单时,数据更新前后的日志将会别记录到每个数据库下的undo_log表中,并形成一个全局的锁。以上操作全部在一个数据库事务内完成,这样保证了一阶段操作的原子性。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpwgsj.html