seata入门到实战二

实战

at模式

business controller

@RestController
@RequestMapping("/business/dubbo")
@Slf4j
public class BusinessController {

    private static final Logger LOGGER = LoggerFactory.getLogger(BusinessController.class);

    @Autowired
    private BusinessService businessService;

    /**
     * 模拟用户购买商品下单业务逻辑流程
     */
    @PostMapping("/buy")
    ObjectResponse handleBusiness(@RequestBody BusinessDTO businessDTO) {
        LOGGER.info("请求参数:{}", businessDTO.toString());
        return businessService.handleBusiness(businessDTO);
    }
}

service

@Override
    @GlobalTransactional(timeoutMills = 300000, name = "dubbo-gts-seata-example")
    public ObjectResponse handleBusiness(BusinessDTO businessDTO) {
        System.out.println("开始全局事务,XID = " + RootContext.getXID());
        ObjectResponse<Object> objectResponse = new ObjectResponse<>();
        // 1、扣减库存
        CommodityDTO commodityDTO = new CommodityDTO();
        commodityDTO.setCommodityCode(businessDTO.getCommodityCode());
        commodityDTO.setCount(businessDTO.getCount());
        ObjectResponse stockResponse = stockDubboService.decreaseStock(commodityDTO);

        // 2、创建订单
        OrderDTO orderDTO = new OrderDTO();
        orderDTO.setUserId(businessDTO.getUserId());
        orderDTO.setCommodityCode(businessDTO.getCommodityCode());
        orderDTO.setOrderCount(businessDTO.getCount());
        orderDTO.setOrderAmount(businessDTO.getAmount());
        ObjectResponse<OrderDTO> response = orderDubboService.createOrder(orderDTO);

        //打开注释测试事务发生异常后,全局回滚功能
//        if (!flag) {
//            throw new RuntimeException("测试抛异常后,分布式事务回滚!");
//        }

        if (stockResponse.getStatus() != 200 || response.getStatus() != 200) {
            throw new DefaultException(RspStatusEnum.FAIL);
        }

        objectResponse.setStatus(RspStatusEnum.SUCCESS.getCode());
        objectResponse.setMessage(RspStatusEnum.SUCCESS.getMessage());
        objectResponse.setData(response.getData());
        return objectResponse;
    }

stockDubboService实现

@Service(version = "1.0.0", protocol = "${dubbo.protocol.id}", application = "${dubbo.application.id}",
    registry = "${dubbo.registry.id}", timeout = 3000)
public class StockDubboServiceImpl implements StockDubboService {

    @Autowired
    private ITStockService stockService;

    /**
     * 扣减库存
     */
    @Override
    public ObjectResponse decreaseStock(CommodityDTO commodityDTO) {
        System.out.println("全局事务id :" + RootContext.getXID());
        return stockService.decreaseStock(commodityDTO);
    }
}

orderDubboService实现

@Service
public class TOrderServiceImpl extends ServiceImpl<TOrderMapper, TOrder> implements ITOrderService {

    @Reference(version = "1.0.0")
    private AccountDubboService accountDubboService;

    /**
     * 创建订单
     */
    @Override
    public ObjectResponse<OrderDTO> createOrder(OrderDTO orderDTO) {
        ObjectResponse<OrderDTO> response = new ObjectResponse<>();
        // 扣减用户账户
        AccountDTO accountDTO = new AccountDTO();
        accountDTO.setUserId(orderDTO.getUserId());
        accountDTO.setAmount(orderDTO.getOrderAmount());
        ObjectResponse objectResponse = accountDubboService.decreaseAccount(accountDTO);

        // 随机生成订单号
        orderDTO.setOrderNo(UUID.randomUUID().toString().replace("-", ""));
        // 生成订单
        TOrder tOrder = new TOrder();
        BeanUtils.copyProperties(orderDTO, tOrder);
        tOrder.setCount(orderDTO.getOrderCount());
        tOrder.setAmount(orderDTO.getOrderAmount().doubleValue());
        try {
            baseMapper.createOrder(tOrder);
        } catch (Exception e) {
            response.setStatus(RspStatusEnum.FAIL.getCode());
            response.setMessage(RspStatusEnum.FAIL.getMessage());
            return response;
        }

        if (objectResponse.getStatus() != 200) {
            response.setStatus(RspStatusEnum.FAIL.getCode());
            response.setMessage(RspStatusEnum.FAIL.getMessage());
            return response;
        }

        response.setStatus(RspStatusEnum.SUCCESS.getCode());
        response.setMessage(RspStatusEnum.SUCCESS.getMessage());
        return response;
    }
}

tcc模式

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd"
       default-autowire="byName">

    <!-- 转出账户数据源配置 -->
    <bean id="fromAccountDataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName">
            <value>com.mysql.cj.jdbc.Driver</value>
        </property>
        <property name="url">
            <value>jdbc:mysql://127.0.0.1:3306/transfer_from_db</value>
        </property>
        <property name="username">
            <value>root</value>
        </property>
        <property name="password">
            <value>root</value>
        </property>
    </bean>

    <bean id="fromDsTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="fromAccountDataSource"/>
    </bean>

    <bean id="fromDsTransactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
        <property name="propagationBehaviorName">
            <value>PROPAGATION_REQUIRES_NEW</value>
        </property>
        <property name="transactionManager">
            <ref bean="fromDsTransactionManager"/>
        </property>
    </bean>

    <!-- mybatis -->
    <bean id="fromDsSqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="fromAccountDataSource"/>
        <property name="configLocation" value="classpath:sqlmap/sqlMapConfig.xml"/>
    </bean>

    <bean id="fromDsSqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <constructor-arg index="0" ref="fromDsSqlSessionFactory"/>
    </bean>

    <bean id="fromDsSqlMapClientDAO" abstract="true">
        <property name="sqlSession">
            <ref bean="fromDsSqlSession"/>
        </property>
    </bean>

    <!-- 转出账户 DAO -->
    <bean id="fromAccountDAO" class="io.seata.samples.tcc.transfer.dao.impl.AccountDAOImpl" parent="fromDsSqlMapClientDAO"/>

</beans>
/**
     * 一阶段准备,转入资金 准备
     */
    @Override
    public boolean prepareAdd(final BusinessActionContext businessActionContext, final String accountNo, final double amount) {
        //分布式事务ID
        final String xid = businessActionContext.getXid();

        return toDsTransactionTemplate.execute(status -> {
            try {
                // 校验账户
                Account account = toAccountDAO.getAccountForUpdate(accountNo);
                if (account == null) {
                    System.out.println("prepareAdd: 账户[" + accountNo + "]不存在, txId:" + businessActionContext.getXid());
                    return false;
                }

                // 待转入资金作为 不可用金额
                double freezedAmount = account.getFreezedAmount() + amount;
                account.setFreezedAmount(freezedAmount);
                toAccountDAO.updateFreezedAmount(account);
                System.out.println(String.format("prepareAdd account[%s] amount[%f], dtx transaction id: %s.", accountNo, amount, xid));
                return true;
            } catch (Throwable t) {
                t.printStackTrace();
                status.setRollbackOnly();
                return false;
            }
        });
    }

    /**
     * 二阶段提交
     */
    @Override
    public boolean commit(BusinessActionContext businessActionContext) {
        final String xid = businessActionContext.getXid(); // 分布式事务ID
        final String accountNo = String.valueOf(businessActionContext.getActionContext("accountNo")); // 账户ID
        final double amount = Double.valueOf(String.valueOf(businessActionContext.getActionContext("amount"))); // 转出金额

        return toDsTransactionTemplate.execute(status -> {
            try {
                Account account = toAccountDAO.getAccountForUpdate(accountNo);
                double newAmount = account.getAmount() + amount; // 加钱
                account.setAmount(newAmount);
                account.setFreezedAmount(account.getFreezedAmount() - amount); // 解除冻结金额
                toAccountDAO.updateAmount(account);

                System.out.println(String.format("add account[%s] amount[%f], dtx transaction id: %s.", accountNo, amount, xid));
                return true;
            } catch (Throwable t) {
                t.printStackTrace();
                status.setRollbackOnly();
                return false;
            }
        });

    }

    /**
     * 二阶段回滚
     */
    @Override
    public boolean rollback(BusinessActionContext businessActionContext) {
        final String xid = businessActionContext.getXid(); // 分布式事务ID
        final String accountNo = String.valueOf(businessActionContext.getActionContext("accountNo")); // 账户ID
        final double amount = Double.valueOf(String.valueOf(businessActionContext.getActionContext("amount"))); // 转出金额

        return toDsTransactionTemplate.execute(status -> {
            try {
                Account account = toAccountDAO.getAccountForUpdate(accountNo);
                if (account == null) return true; // 账户不存在, 无需回滚动作

                account.setFreezedAmount(account.getFreezedAmount() - amount); // 解除冻结金额
                toAccountDAO.updateFreezedAmount(account);

                System.out.println(String.format("Undo prepareAdd account[%s] amount[%f], dtx transaction id: %s.", accountNo, amount, xid));
                return true;
            } catch (Throwable t) {
                t.printStackTrace();
                status.setRollbackOnly();
                return false;
            }
        });
    }

at事务日志rollback_info内容解析

在这里插入图片描述

undolog manager

在这里插入图片描述

seata数据源代理

在这里插入图片描述

datasourceProxy数据源代理类

在这里插入图片描述

resourceManager资源管理器

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值