分布式事务中间件 TCC-Transaction 源码分析 —— 项目实战

分布式事务 TCC-Transaction 源码分析 —— TCC 实现 本文主要基于 TCC-Transaction 1.2.3.3 正式版1. 概述2. TCC 原理3. TCC-Transaction 原理4. 事务与参与者4.1 事务4.2 参与者5. 事务管理器5.1 发起根事务5.2 传播发起分支事务5.3 传播获取分支事务5.4 提交事务5.5 回滚事务5.6 添加参与者到事务6. 事务拦截器6.1 Compensable6.2 可补偿事务拦截器6.3 资 阅读详情

摘要: 原创出处 http://www.iocoder.cn/TCC-Transaction/http-sample/ 「芋道源码」欢迎转载,保留摘要,谢谢!

本文主要基于 TCC-Transaction 1.2.3.3 正式版


������关注**微信公众号:【芋道源码】**有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言都将得到认真回复。甚至不知道如何读源码也可以请教噢。
  4. 新的源码解析文章实时收到通知。每周更新一篇左右。
  5. 认真的源码交流微信群。

1. 概述

本文分享 TCC 项目实战。以官方 Maven项目 tcc-transaction-http-sample 为例子( tcc-transaction-dubbo-sample 类似 )。

建议你已经成功启动了该项目。如果不知道如何启动,可以先查看《TCC-Transaction 源码分析 —— 调试环境搭建》。如果再碰到问题,欢迎加微信公众号( 芋道源码 ),我会一一仔细回复。

OK,首先我们简单了解下这个项目。

  • 首页 => 商品列表 => 确认支付页 => 支付结果页
  • 使用账户余额 + 红包余额联合支付购买商品,并账户之间转账。

项目拆分三个子 Maven 项目:

  • tcc-transaction-http-order :商城服务,提供商品和商品订单逻辑。
  • tcc-transaction-http-capital :资金服务,提供账户余额逻辑。
  • tcc-transaction-http-redpacket :红包服务,提供红包余额逻辑。

你行好事会因为得到赞赏而愉悦同理,开源项目贡献者会因为 Star 而更加有动力为 TCC-Transaction 点赞!传送门

2. 实体结构

2.1 商城服务

  • Shop,商店表。实体代码如下:

             
    public class Shop {
    /**
    * 商店编号
    */
    private long id;
    /**
    * 所有者用户编号
    */
    private long ownerUserId;
    }

  • Product,商品表。实体代码如下:

             
    public class Product implements Serializable {
    /**
    * 商品编号
    */
    private long productId;
    /**
    * 商店编号
    */
    private long shopId;
    /**
    * 商品名
    */
    private String productName;
    /**
    * 单价
    */
    private BigDecimal price;
    }

  • Order,订单表。实现代码如下:

             
    public class Order implements Serializable {
    private static final long serialVersionUID = - 5908730245224893590L;
    /**
    * 订单编号
    */
    private long id;
    /**
    * 支付( 下单 )用户编号
    */
    private long payerUserId;
    /**
    * 收款( 商店拥有者 )用户编号
    */
    private long payeeUserId;
    /**
    * 红包支付金额
    */
    private BigDecimal redPacketPayAmount;
    /**
    * 账户余额支付金额
    */
    private BigDecimal capitalPayAmount;
    /**
    * 订单状态
    * - DRAFT :草稿
    * - PAYING :支付中
    * - CONFIRMED :支付成功
    * - PAY_FAILED :支付失败
    */
    private String status = "DRAFT";
    /**
    * 商户订单号,使用 UUID 生成
    */
    private String merchantOrderNo;
    /**
    * 订单明细数组
    * 非存储字段
    */
    private List<OrderLine> orderLines = new ArrayList<OrderLine>();
    }

  • OrderLine,订单明细。实体代码如下:

             
    public class OrderLine implements Serializable {
    private static final long serialVersionUID = 2300754647209250837L;
    /**
    * 订单编号
    */
    private long id;
    /**
    * 商品编号
    */
    private long productId;
    /**
    * 数量
    */
    private int quantity;
    /**
    * 单价
    */
    private BigDecimal unitPrice;
    }

业务逻辑:

下单时,插入订单状态为 "DRAFT" 的订单( Order )记录,并插入购买的商品订单明细( OrderLine )记录。支付时,更新订单状态为 "PAYING"。

  • 订单支付成功,更新订单状态为 "CONFIRMED"。
  • 订单支付失败,更新订单状体为 "PAY_FAILED"。

2.2 资金服务

关系较为简单,有两个实体:

  • CapitalAccount,资金账户余额。实体代码如下:

             
    public class CapitalAccount {
    /**
    * 账户编号
    */
    private long id;
    /**
    * 用户编号
    */
    private long userId;
    /**
    * 余额
    */
    private BigDecimal balanceAmount;
    }

  • TradeOrder,交易订单表。实体代码如下:

             
    public class TradeOrder {
    /**
    * 交易订单编号
    */
    private long id;
    /**
    * 转出用户编号
    */
    private long selfUserId;
    /**
    * 转入用户编号
    */
    private long oppositeUserId;
    /**
    * 商户订单号
    */
    private String merchantOrderNo;
    /**
    * 金额
    */
    private BigDecimal amount;
    /**
    * 交易订单状态
    * - DRAFT :草稿
    * - CONFIRM :交易成功
    * - CANCEL :交易取消
    */
    private String status = "DRAFT";
    }

业务逻辑:

订单支付支付中,插入交易订单状态为 "DRAFT" 的订单( TradeOrder )记录,并更新减少下单用户的资金账户余额。

  • 订单支付成功,更新交易订单状态为 "CONFIRM",并更新增加商店拥有用户的资金账户余额。
  • 订单支付失败,更新交易订单状态为 "CANCEL",并更新**增加( 恢复 )**下单用户的资金账户余额。

2.3 红包服务

关系较为简单,和资金服务 99.99% 相同,有两个实体:

  • RedPacketAccount,红包账户余额。实体代码如下:

             
    public class RedPacketAccount {
    /**
    * 账户编号
    */
    private long id;
    /**
    * 用户编号
    */
    private long userId;
    /**
    * 余额
    */
    private BigDecimal balanceAmount;
    }

  • TradeOrder,交易订单表。实体代码如下:

             
    public class TradeOrder {
    /**
    * 交易订单编号
    */
    private long id;
    /**
    * 转出用户编号
    */
    private long selfUserId;
    /**
    * 转入用户编号
    */
    private long oppositeUserId;
    /**
    * 商户订单号
    */
    private String merchantOrderNo;
    /**
    * 金额
    */
    private BigDecimal amount;
    /**
    * 交易订单状态
    * - DRAFT :草稿
    * - CONFIRM :交易成功
    * - CANCEL :交易取消
    */
    private String status = "DRAFT";
    }

业务逻辑:

订单支付支付中,插入交易订单状态为 "DRAFT" 的订单( TradeOrder )记录,并更新减少下单用户的红包账户余额。

  • 订单支付成功,更新交易订单状态为 "CONFIRM",并更新增加商店拥有用户的红包账户余额。
  • 订单支付失败,更新交易订单状态为 "CANCEL",并更新**增加( 恢复 )**下单用户的红包账户余额。

3. 服务调用

服务之间,通过 HTTP 进行调用。

红包服务和资金服务为商城服务提供调用( 以资金服务为例子 ):

  • XML 配置如下 :

             
    // appcontext-service-provider.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
    <bean name="capitalAccountRepository"
    class="org.mengyun.tcctransaction.sample.http.capital.domain.repository.CapitalAccountRepository"/>
    <bean name="tradeOrderRepository"
    class="org.mengyun.tcctransaction.sample.http.capital.domain.repository.TradeOrderRepository"/>
    <bean name="capitalTradeOrderService"
    class="org.mengyun.tcctransaction.sample.http.capital.service.CapitalTradeOrderServiceImpl"/>
    <bean name="capitalAccountService"
    class="org.mengyun.tcctransaction.sample.http.capital.service.CapitalAccountServiceImpl"/>
    <bean name="capitalTradeOrderServiceExporter"
    class="org.springframework.remoting.httpinvoker.SimpleHttpInvokerServiceExporter">
    <property name="service" ref="capitalTradeOrderService"/>
    <property name="serviceInterface"
    value="org.mengyun.tcctransaction.sample.http.capital.api.CapitalTradeOrderService"/>
    </bean>
    <bean name="capitalAccountServiceExporter"
    class="org.springframework.remoting.httpinvoker.SimpleHttpInvokerServiceExporter">
    <property name="service" ref="capitalAccountService"/>
    <property name="serviceInterface"
    value="org.mengyun.tcctransaction.sample.http.capital.api.CapitalAccountService"/>
    </bean>
    <bean id="httpServer"
    class="org.springframework.remoting.support.SimpleHttpServerFactoryBean">
    <property name="contexts">
    <util:map>
    <entry key="/remoting/CapitalTradeOrderService" value-ref="capitalTradeOrderServiceExporter"/>
    <entry key="/remoting/CapitalAccountService" value-ref="capitalAccountServiceExporter"/>
    </util:map>
    </property>
    <property name="port" value="8081"/>
    </bean>
    </beans>

  • Java 代码实现如下 :

             
    public class CapitalAccountServiceImpl implements CapitalAccountService {
    @Autowired
    CapitalAccountRepository capitalAccountRepository;
    @Override
    public BigDecimal getCapitalAccountByUserId(long userId) {
    return capitalAccountRepository.findByUserId(userId).getBalanceAmount();
    }
    }
    public class CapitalAccountServiceImpl implements CapitalAccountService {
    @Autowired
    CapitalAccountRepository capitalAccountRepository;
    @Override
    public BigDecimal getCapitalAccountByUserId(long userId) {
    return capitalAccountRepository.findByUserId(userId).getBalanceAmount();
    }
    }


商城服务调用

  • XML 配置如下:

             
    // appcontext-service-consumer.xml
    <?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">
    <bean id="httpInvokerRequestExecutor"
    class="org.springframework.remoting.httpinvoker.CommonsHttpInvokerRequestExecutor">
    <property name="httpClient">
    <bean class="org.apache.commons.httpclient.HttpClient">
    <property name="httpConnectionManager">
    <ref bean="multiThreadHttpConnectionManager"/>
    </property>
    </bean>
    </property>
    </bean>
    <bean id="multiThreadHttpConnectionManager"
    class="org.apache.commons.httpclient.MultiThreadedHttpConnectionManager">
    <property name="params">
    <bean class="org.apache.commons.httpclient.params.HttpConnectionManagerParams">
    <property name="connectionTimeout" value="200000"/>
    <property name="maxTotalConnections" value="600"/>
    <property name="defaultMaxConnectionsPerHost" value="512"/>
    <property name="soTimeout" value="5000"/>
    </bean>
    </property>
    </bean>
    <bean id="captialTradeOrderService" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
    <property name="serviceUrl" value="http://localhost:8081/remoting/CapitalTradeOrderService"/>
    <property name="serviceInterface"
    value="org.mengyun.tcctransaction.sample.http.capital.api.CapitalTradeOrderService"/>
    <property name="httpInvokerRequestExecutor" ref="httpInvokerRequestExecutor"/>
    </bean>
    <bean id="capitalAccountService" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
    <property name="serviceUrl" value="http://localhost:8081/remoting/CapitalAccountService"/>
    <property name="serviceInterface"
    value="org.mengyun.tcctransaction.sample.http.capital.api.CapitalAccountService"/>
    <property name="httpInvokerRequestExecutor" ref="httpInvokerRequestExecutor"/>
    </bean>
    <bean id="redPacketAccountService" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
    <property name="serviceUrl" value="http://localhost:8082/remoting/RedPacketAccountService"/>
    <property name="serviceInterface"
    value="org.mengyun.tcctransaction.sample.http.redpacket.api.RedPacketAccountService"/>
    <property name="httpInvokerRequestExecutor" ref="httpInvokerRequestExecutor"/>
    </bean>
    <bean id="redPacketTradeOrderService" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean">
    <property name="serviceUrl" value="http://localhost:8082/remoting/RedPacketTradeOrderService"/>
    <property name="serviceInterface"
    value="org.mengyun.tcctransaction.sample.http.redpacket.api.RedPacketTradeOrderService"/>
    <property name="httpInvokerRequestExecutor" ref="httpInvokerRequestExecutor"/>
    </bean>
    </beans>

  • Java 接口接口如下:

             
    public interface CapitalAccountService {
    BigDecimal getCapitalAccountByUserId(long userId);
    }
    public interface CapitalTradeOrderService {
    String record(TransactionContext transactionContext, CapitalTradeOrderDto tradeOrderDto);
    }
    public interface RedPacketAccountService {
    BigDecimal getRedPacketAccountByUserId(long userId);
    }
    public interface RedPacketTradeOrderService {
    String record(TransactionContext transactionContext, RedPacketTradeOrderDto tradeOrderDto);
    }

4. 下单支付流程

ps:数据访问的方法,请自己拉取代码,使用 IDE 查看。谢谢。��

下单支付流程,整体流程如下图( 打开大图 ):

点击**【支付】**按钮,下单支付流程。实现代码如下:

       
@Controller
@RequestMapping( "")
public class OrderController {
@RequestMapping(value = "/placeorder", method = RequestMethod.POST)
public ModelAndView placeOrder(@RequestParam String redPacketPayAmount,
@RequestParam long shopId,
@RequestParam long payerUserId,
@RequestParam long productId) {
PlaceOrderRequest request = buildRequest(redPacketPayAmount, shopId, payerUserId, productId);
// 下单并支付订单
String merchantOrderNo = placeOrderService.placeOrder(request.getPayerUserId(), request.getShopId(),
request.getProductQuantities(), request.getRedPacketPayAmount());
// 返回
ModelAndView mv = new ModelAndView( "pay_success");
// 查询订单状态
String status = orderService.getOrderStatusByMerchantOrderNo(merchantOrderNo);
// 支付结果提示
String payResultTip = null;
if ( "CONFIRMED".equals(status)) {
payResultTip = "支付成功";
} else if ( "PAY_FAILED".equals(status)) {
payResultTip = "支付失败";
}
mv.addObject( "payResult", payResultTip);
// 商品信息
mv.addObject( "product", productRepository.findById(productId));
// 资金账户金额 和 红包账户金额
mv.addObject( "capitalAmount", accountService.getCapitalAccountByUserId(payerUserId));
mv.addObject( "redPacketAmount", accountService.getRedPacketAccountByUserId(payerUserId));
return mv;
}
}


调用 PlaceOrderService#placeOrder(...) 方法,下单并支付订单。实现代码如下:

       
@Service
public class PlaceOrderServiceImpl {
public String placeOrder(long payerUserId, long shopId, List<Pair<Long, Integer>> productQuantities, BigDecimal redPacketPayAmount) {
// 获取商店
Shop shop = shopRepository.findById(shopId);
// 创建订单
Order order = orderService.createOrder(payerUserId, shop.getOwnerUserId(), productQuantities);
// 发起支付
Boolean result = false;
try {
paymentService.makePayment(order, redPacketPayAmount, order.getTotalAmount().subtract(redPacketPayAmount));
} catch (ConfirmingException confirmingException) {
// exception throws with the tcc transaction status is CONFIRMING,
// when tcc transaction is confirming status,
// the tcc transaction recovery will try to confirm the whole transaction to ensure eventually consistent.
result = true;
} catch (CancellingException cancellingException) {
// exception throws with the tcc transaction status is CANCELLING,
// when tcc transaction is under CANCELLING status,
// the tcc transaction recovery will try to cancel the whole transaction to ensure eventually consistent.
} catch (Throwable e) {
// other exceptions throws at TRYING stage.
// you can retry or cancel the operation.
e.printStackTrace();
}
return order.getMerchantOrderNo();
}
}

  • 调用 ShopRepository#findById(...) 方法,查询商店。

  • 调用 OrderService#createOrder(...) 方法,创建订单状态为 "DRAFT" 的商城订单。实际业务不会这么做,此处仅仅是例子,简化流程。实现代码如下:

             
    @Service
    public class OrderServiceImpl {
    @Transactional
    public Order createOrder(long payerUserId, long payeeUserId, List<Pair<Long, Integer>> productQuantities) {
    Order order = orderFactory.buildOrder(payerUserId, payeeUserId, productQuantities);
    orderRepository.createOrder(order);
    return order;
    }
    }

  • 调用 PaymentService#makePayment(...) 方法,发起支付,TCC 流程。
  • 生产代码对于异常需要进一步处理。
  • 生产代码对于异常需要进一步处理。
  • 生产代码对于异常需要进一步处理。

4.1 Try 阶段

商城服务

调用 PaymentService#makePayment(...) 方法,发起 Try 流程,实现代码如下:

       
@Compensable(confirmMethod = "confirmMakePayment", cancelMethod = "cancelMakePayment")
@Transactional
public void makePayment(Order order, BigDecimal redPacketPayAmount, BigDecimal capitalPayAmount) {
System.out.println( "order try make payment called.time seq:" + DateFormatUtils.format(Calendar.getInstance(), "yyyy-MM-dd HH:mm:ss"));
// 更新订单状态为支付中
order.pay(redPacketPayAmount, capitalPayAmount);
orderRepository.updateOrder(order);
// 资金账户余额支付订单
String result = tradeOrderServiceProxy.record( null, buildCapitalTradeOrderDto(order));
// 红包账户余额支付订单
String result2 = tradeOrderServiceProxy.record( null, buildRedPacketTradeOrderDto(order));
}

  • 设置方法注解 @Compensable

    • 事务传播级别 Propagation.REQUIRED ( 默认值 )
    • 设置 confirmMethod / cancelMethod 方法名
    • 事务上下文编辑类 DefaultTransactionContextEditor ( 默认值 )
  • 设置方法注解 @Transactional,保证方法操作原子性。

  • 调用 OrderRepository#updateOrder(...) 方法,更新订单状态为支付中。实现代码如下:

             
    // Order.java
    public void pay(BigDecimal redPacketPayAmount, BigDecimal capitalPayAmount) {
    this.redPacketPayAmount = redPacketPayAmount;
    this.capitalPayAmount = capitalPayAmount;
    this.status = "PAYING";
    }

  • 调用 TradeOrderServiceProxy#record(...) 方法,资金账户余额支付订单。实现代码如下:

             
    // TradeOrderServiceProxy.java
    @Compensable(propagation = Propagation.SUPPORTS, confirmMethod = "record", cancelMethod = "record", transactionContextEditor = Compensable.DefaultTransactionContextEditor.class)
    public String record(TransactionContext transactionContext, CapitalTradeOrderDto tradeOrderDto) {
    return capitalTradeOrderService.record(transactionContext, tradeOrderDto);
    }
    // CapitalTradeOrderService.java
    public interface CapitalTradeOrderService {
    String record(TransactionContext transactionContext, CapitalTradeOrderDto tradeOrderDto);
    }

    • 设置方法注解 @Compensable

      • propagation=Propagation.SUPPORTS :支持当前事务,如果当前没有事务,就以非事务方式执行。为什么不使用 REQUIRED ?如果使用 REQUIRED 事务传播级别,事务恢复重试时,会发起新的事务。
      • confirmMethod、cancelMethod 使用和 try 方法相同方法名:本地发起远程服务 TCC confirm / cancel 阶段,调用相同方法进行事务的提交或回滚。远程服务的 CompensableTransactionInterceptor 会根据事务的状态是 CONFIRMING / CANCELLING 来调用对应方法。
    • 调用 CapitalTradeOrderService#record(...) 方法,远程调用,发起资金账户余额支付订单。

  • 调用 TradeOrderServiceProxy#record(...) 方法,红包账户余额支付订单。和资金账户余额支付订单 99.99% 类似,不重复“复制粘贴”。


资金服务

调用 CapitalTradeOrderServiceImpl#record(...) 方法,红包账户余额支付订单。实现代码如下:

       
@Override
@Compensable(confirmMethod = "confirmRecord", cancelMethod = "cancelRecord", transactionContextEditor = Compensable.DefaultTransactionContextEditor.class)
@Transactional
public String record(TransactionContext transactionContext, CapitalTradeOrderDto tradeOrderDto) {
// 调试用
try {
Thread.sleep( 1000l);
// Thread.sleep(10000000L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println( "capital try record called. time seq:" + DateFormatUtils.format(Calendar.getInstance(), "yyyy-MM-dd HH:mm:ss"));
// 生成交易订单
TradeOrder tradeOrder = new TradeOrder(
tradeOrderDto.getSelfUserId(),
tradeOrderDto.getOppositeUserId(),
tradeOrderDto.getMerchantOrderNo(),
tradeOrderDto.getAmount()
);
tradeOrderRepository.insert(tradeOrder);
// 更新减少下单用户的资金账户余额
CapitalAccount transferFromAccount = capitalAccountRepository.findByUserId(tradeOrderDto.getSelfUserId());
transferFromAccount.transferFrom(tradeOrderDto.getAmount());
capitalAccountRepository.save(transferFromAccount);
return "success";
}

  • 设置方法注解 @Compensable

    • 事务传播级别 Propagation.REQUIRED ( 默认值 )
    • 设置 confirmMethod / cancelMethod 方法名
    • 事务上下文编辑类 DefaultTransactionContextEditor ( 默认值 )
  • 设置方法注解 @Transactional,保证方法操作原子性。

  • 调用 TradeOrderRepository#insert(...) 方法,生成订单状态为 "DRAFT" 的交易订单。

  • 调用 CapitalAccountRepository#save(...) 方法,更新减少下单用户的资金账户余额。Try 阶段锁定资源时,一定要先扣。TCC 是最终事务一致性,如果先添加,可能被使用。

4.2 Confirm / Cancel 阶段

当 Try 操作全部成功时,发起 Confirm 操作。当 Try 操作存在任务失败时,发起 Cancel 操作。

4.2.1 Confirm

商城服务

调用 PaymentServiceImpl#confirmMakePayment(...) 方法,更新订单状态为支付成功。实现代码如下:

       
public void confirmMakePayment(Order order, BigDecimal redPacketPayAmount, BigDecimal capitalPayAmount) {
// 调试用
try {
Thread.sleep( 1000l);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println( "order confirm make payment called. time seq:" + DateFormatUtils.format(Calendar.getInstance(), "yyyy-MM-dd HH:mm:ss"));
// 更新订单状态为支付成功
order.confirm();
orderRepository.updateOrder(order);
}

  • 生产代码该方法需要加下 @Transactional 注解,保证原子性。

  • 调用 OrderRepository#updateOrder(...) 方法,更新订单状态为支付成功。实现代码如下:

             
    // Order.java
    public void confirm() {
    this.status = "CONFIRMED";
    }


资金服务

调用 CapitalTradeOrderServiceImpl#confirmRecord(...) 方法,更新交易订单状态为交易成功。

       
@Transactional
public void confirmRecord(TransactionContext transactionContext, CapitalTradeOrderDto tradeOrderDto) {
// 调试用
try {
Thread.sleep( 1000l);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println( "capital confirm record called. time seq:" + DateFormatUtils.format(Calendar.getInstance(), "yyyy-MM-dd HH:mm:ss"));
// 查询交易记录
TradeOrder tradeOrder = tradeOrderRepository.findByMerchantOrderNo(tradeOrderDto.getMerchantOrderNo());
// 判断交易记录状态。因为 `#record()` 方法,可能事务回滚,记录不存在 / 状态不对
if ( null != tradeOrder && "DRAFT".equals(tradeOrder.getStatus())) {
// 更新订单状态为交易成功
tradeOrder.confirm();
tradeOrderRepository.update(tradeOrder);
// 更新增加商店拥有者用户的资金账户余额
CapitalAccount transferToAccount = capitalAccountRepository.findByUserId(tradeOrderDto.getOppositeUserId());
transferToAccount.transferTo(tradeOrderDto.getAmount());
capitalAccountRepository.save(transferToAccount);
}
}

  • 设置方法注解 @Transactional,保证方法操作原子性。

  • 判断交易记录状态。因为 #record() 方法,可能事务回滚,记录不存在 / 状态不对。

  • 调用 TradeOrderRepository#update(...) 方法,更新交易订单状态为交易成功。

  • 调用 CapitalAccountRepository#save(...) 方法,更新增加商店拥有者用户的资金账户余额。实现代码如下:

             
    // CapitalAccount.java
    public void transferTo(BigDecimal amount) {
    this.balanceAmount = this.balanceAmount.add(amount);
    }


红包服务

和资源服务 99.99% 相同,不重复“复制粘贴”。

4.2.2 Cancel

商城服务

调用 PaymentServiceImpl#cancelMakePayment(...) 方法,更新订单状态为支付失败。实现代码如下:

       
public void cancelMakePayment(Order order, BigDecimal redPacketPayAmount, BigDecimal capitalPayAmount) {
// 调试用
try {
Thread.sleep( 1000l);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println( "order cancel make payment called.time seq:" + DateFormatUtils.format(Calendar.getInstance(), "yyyy-MM-dd HH:mm:ss"));
// 更新订单状态为支付失败
order.cancelPayment();
orderRepository.updateOrder(order);
}

  • 生产代码该方法需要加下 @Transactional 注解,保证原子性。

  • 调用 OrderRepository#updateOrder(...) 方法,更新订单状态为支付失败。实现代码如下:

             
    // Order.java
    public void cancelPayment() {
    this.status = "PAY_FAILED";
    }


资金服务

调用 CapitalTradeOrderServiceImpl#cancelRecord(...) 方法,更新交易订单状态为交易失败。

       
@Transactional
public void cancelRecord(TransactionContext transactionContext, CapitalTradeOrderDto tradeOrderDto) {
// 调试用
try {
Thread.sleep( 1000l);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println( "capital cancel record called. time seq:" + DateFormatUtils.format(Calendar.getInstance(), "yyyy-MM-dd HH:mm:ss"));
// 查询交易记录
TradeOrder tradeOrder = tradeOrderRepository.findByMerchantOrderNo(tradeOrderDto.getMerchantOrderNo());
// 判断交易记录状态。因为 `#record()` 方法,可能事务回滚,记录不存在 / 状态不对
if ( null != tradeOrder && "DRAFT".equals(tradeOrder.getStatus())) {
// / 更新订单状态为交易失败
tradeOrder.cancel();
tradeOrderRepository.update(tradeOrder);
// 更新增加( 恢复 )下单用户的资金账户余额
CapitalAccount capitalAccount = capitalAccountRepository.findByUserId(tradeOrderDto.getSelfUserId());
capitalAccount.cancelTransfer(tradeOrderDto.getAmount());
capitalAccountRepository.save(capitalAccount);
}
}

  • 设置方法注解 @Transactional,保证方法操作原子性。

  • 判断交易记录状态。因为 #record() 方法,可能事务回滚,记录不存在 / 状态不对。

  • 调用 TradeOrderRepository#update(...) 方法,更新交易订单状态为交易失败。

  • 调用 CapitalAccountRepository#save(...) 方法,更新增加( 恢复 )下单用户的资金账户余额。实现代码如下:

             
    // CapitalAccount.java
    public void cancelTransfer(BigDecimal amount) {
    transferTo(amount);
    }


红包服务

和资源服务 99.99% 相同,不重复“复制粘贴”。

666. 彩蛋

知识星球

嘿嘿,代码只是看起来比较多,实际不多。

蚂蚁金融云提供了银行间转账的 TCC 过程例子,有兴趣的同学可以看看:《蚂蚁金融云 —— 分布式事务服务(DTS) —— 场景介绍》。

本系列 EOF ~撒花

胖友,分享个朋友圈,可好?!

TWINCAT3保姆级教程:手把手教你给Ecat从站烧录EEPROM(含XML配置避坑指南) 本文提供TWINCAT3环境下Ecat从站EEPROM烧录的完整教程,涵盖XML配置避坑指南。详细讲解环境准备、设备扫描、XML文件配置、EEPROM烧录流程及故障排查,帮助工程师高效解决设备识别问题,提升产线调试效率。 阅读详情

相关推荐

FPGA波束形成:从算法到硬件的艺术之旅

本文深入探讨了FPGA在波束形成技术中的应用,从算法原理到Verilog硬件实现的全过程。文章详细分析了FPGA并行处理、低延迟和可重构特性如何优化波束形成系统性能,并提供了Verilog关键模块代码示例和资源优化策略,为工程师实现高效FPGA波束形成解决方案提供实用指导。

tea88的博客 1044

分布式事务--TCC的中间件--选型/对比

本文介绍分布式事务的TCC的一些中间件。

IT利刃出鞘的博客 4063

手把手教你用Arduino IDE玩转ESP32-S3-wroom(附串口调试技巧)

本文详细介绍了如何在Arduino IDE环境下配置和使用ESP32-S3-WROOM模块,包括开发环境搭建、硬件连接、程序烧录及高级调试技巧。通过实战步骤和常见问题解决方案,帮助开发者快速上手这一高性能Wi-Fi/蓝牙双模模组,提升物联网开发效率。

weixin_30487201的博客 292

【数据库与事务系列】分库分表中间件

分库分表Sharding-Sphere

qq_35789269的博客 2321

AgentScope深入分析-ReActAgent

ReActAgent 是 AgentScope 框架中最核心的智能体实现,它完美诠释了"推理-行动"(Reasoning-Acting)循环的精髓。本文将深入分析 ReActAgent 的类继承关系、核心执行流程,以及状态管理、钩子机制、实时介入等关键技术点。通过阅读本文,你会理解智能体如何与模型交互、如何执行工具、如何处理中断,以及这些机制背后的设计考量。模块化:通过组合不同的组件(模型、格式化器、工具集、记忆)来构建智能体透明性:所有关键流程都对开发者可见,可以精确控制可扩展性。

842

GPU硬件架构与微架构深度解析28-稀疏计算:稀疏矩阵计算的硬件支持

本文介绍了GPU加速稀疏计算的关键技术。主要内容包括:1)稀疏数据的存储格式(COO、CSR、CSC)及其特点;2)现代GPU的稀疏计算硬件优化,如NVIDIA的稀疏张量核心和内存访问优化;3)稀疏计算在推荐系统、NLP和科学计算等领域的应用案例;4)性能优化技巧,包括存储格式选择、硬件特性利用和混合精度计算等。文章指出,专用稀疏计算技术可显著提升AI和大规模科学计算的效率,相比传统方法可获得5-10倍的性能提升。

2301_79143213的博客 113

分布式事务中间件 TCC-Transaction 源码解析合集

目录如下:TCC-Transaction 源码解析 —— 调试环境搭建TCC-Transaction 源码分析 —— TCC 实现TCC-Transaction 源码分析...

芋道源码 647

软考高级架构师笔记-10数学计算题

专栏《软考高级架构设计师》更新了,本章列举了10个场景计算题的解法,温故而知新,大家快来卷一卷

java后端开发的博客,不仅仅是后端开发 1598

分布式事务协调中间件---seata快速入门

为了保证分布式服务中的acid ,需要学习了解分布式服务协调框架

qq_55272229的博客 2621

12.工具高级参数CogCalibNPointToNPointTool

CogCalibNPointToNPointTool是一种多点2D坐标校正工具,用于将图像像素坐标转换为物理世界坐标。该工具通过两组点(图像坐标点和物理坐标点)计算最佳转换关系,并将结果保存供后续使用。操作步骤包括获取图像、添加坐标点对、计算转换参数。校正结果会显示平移、缩放、旋转等参数及RMS误差值,较大的误差可能表明点位置不准确或自由度不足。该工具广泛应用于机器视觉领域,通过9点标定等案例可验证坐标转换的准确性并优化参数设置。

203

【大模型】DeepSeek + Coze 打造个人专属AI智能体使用详解

DeepSeek + Coze 打造个人专属AI智能体使用详解

congge 1万+

首次公开:京东数科强一致、高性能分布式事务中间件 JDTX

在分布式数据库、云原生数据库、NewSQL 等名词在数据库领域层出不穷的当今,变革——在这个相对稳定的领域已愈加不可避免。相比于完全革新,渐进式增强的方案在拥有厚重沉淀的...

weixin_44742132的博客 925

【制作100个Unity游戏之36】用unity从零开始实现类《开心消消乐》的三消类消除解压小游戏

Unity甜品消除游戏开发摘要 本文介绍了使用Unity开发甜品消除游戏的核心实现步骤: 冰块阵列生成 通过GlobalDef定义全局参数(9行7列网格) IceSpawner脚本动态生成网格化冰块阵列 添加Resolution脚本实现多分辨率适配 随机水果生成 创建FruitSpawner作为水果容器 FruitItem脚本管理水果属性(行列索引、类型) 支持动态创建水果精灵并设置位置 关键技术点 使用Instantiate动态生成预制体 通过localPosition精确控制网格布局 分辨率适配算法保持

向宇的博客,专注php/web全栈 unity游戏开发,欢迎大家评论纠错 1291

TCC-Transaction 源码分析 —— 项目实战

TCC-Transaction框架是一个基于Spring的分布式事务解决方案,采用Try-Confirm-Cancel三阶段模式确保最终一致性。文章详细介绍了框架的核心实现:1.通过@Compensable注解定义事务方法;2.事务拦截器管理Try/Confirm/Cancel流程;3.事务恢复机制定期处理异常事务。以电商订单系统为例,展示了订单、库存、账户等服务的TCC实现方案,包括资源预留(Try)、确认(Confirm)和取消(Cancel)的具体业务逻辑。此外还分析了Redis存储实现、性能优化策略

油墨香^-^的博客 619

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

TCC-Transaction 源码分析 —— Dubbo 支持

本文详细介绍了TCC-Transaction框架与Dubbo的集成实现。该方案通过Dubbo Filter机制拦截服务调用,实现TCC模式(Try-Confirm-Cancel)的分布式事务管理。核心组件包括事务上下文过滤器、消费者/提供者端处理逻辑、事务参与者注册等模块。框架支持事务上下文传播、多种异常处理机制和事务恢复功能,并提供了连接池优化、事务超时配置等性能优化措施。通过转账业务示例展示了实际应用场景,体现了该方案在分布式系统中的事务处理能力与Dubbo集成优势。

油墨香^-^的博客 1067

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

tcc-transaction源码详解

本文主要介绍TCC的原理,以及从代码的角度上分析如何实现的;不涉及具体使用示例。本文通过分析tcc-transaction源码带大家了解TCC分布式事务的实现原理。 需要注意的是,本文所有代码都基于master-1.7.x分支,不同版本的源码会存在一定的差异。完整代码注释请参考:bigcoder84/tcc-transaction 一. 概述 1.1 项目模块 本文对 tcc-transaction 源码分析。主要涉及如下四个模块: tcc-transaction-core:tcc-transaction

听得到微笑的博客 4446

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

分布式事务 TCC-Transaction 源码分析 —— 项目实战

本文主要基于 TCC-Transaction 1.2.3.3 正式版1. 概述2. 实体结构2.1 商城服务2.2 资金服务2.3 红包服务3. 服务调用4. 下单支付流程4.1 Try 阶段4.2 Confirm / Cancel 阶段666. 彩蛋友情提示:欢迎关注公众号【芋道源码】。?关注后,拉你进【源码圈】微信群和【芋艿】搞基嗨皮。友情提示:欢迎关注公众号【芋道源码】。?关注后,拉你进【源

芋道源码 825

视频教程-分布式事务管理器tcc-transaction实战加源码分析(纯java实现)-Java

分布式事务管理器tcc-transaction实战加源码分析(纯java实现) ...

weixin_34651901的博客 350

TCC-Transaction 源码分析 —— 运维平台

TCC-Transaction运维平台是一个基于SpringBoot+Vue.js的分布式事务管理系统,提供全生命周期的事务监控和管理功能。平台采用前后端分离架构,后端包含数据访问层、服务层和控制层,实现事务查询、手动恢复、统计分析等核心功能;前端基于Vue.js和Element UI构建可视化界面,支持事务列表展示、详情查看和操作管理。系统特色包括:实时监控(通过WebSocket实现)、智能告警(基于规则引擎)、多维统计分析和动态配置管理。部署方案采用Nginx+SpringBoot+MySQL架构,支

油墨香^-^的博客 730

@transaction 提交事务_分布式事务 TCC-Transaction 源码分析——TCC 实现

1. 概述本文分享 TCC 实现。主要涉及如下三个 Maven 项目:tcc-transaction-core :tcc-transaction 底层实现。tcc-transaction-api :tcc-transaction 使用 API。tcc-transaction-spring :tcc-transaction Spring 支持。你行好事会因为得到赞赏而愉悦同理,开源项目贡献者会因为 ...

weixin_42515120的博客 284

TCC-Transaction 源码分析 —— TCC 实现

1. 概述 本文分享TCC 实现。主要涉及如下三个 Maven 项目: tcc-transaction-core:tcc-transaction 底层实现。 tcc-transaction-api:tcc-transaction 使用 API。 tcc-transaction-spring:tcc-transaction Spring 支持。 你行好事会因为得到赞赏而愉悦 同理,开源项目贡献者会因为 Star 而更加有动力 为 TCC-Transaction 点赞!传送门 OK,开始我...

weixin_42073629的博客 657

TCC-Transaction 源码分析 —— TCC 实现详解

本文深入分析了TCC-Transaction分布式事务框架的核心架构与实现机制。该框架基于TCC(Try-Confirm-Cancel)模式,通过事务管理器协调分布式事务执行,包含四大核心组件:事务拦截器、参与者管理器、恢复调度器和存储模块。文章详细阐述了事务生命周期的源码实现,包括Try阶段的资源预留、Confirm/Cancel阶段的最终操作,以及基于定时任务的事务恢复机制。框架采用Spring AOP实现方法拦截,支持多种存储后端,并提供完善的异常处理和监控功能。通过异步Confirm、批量处理等优化

油墨香^-^的博客 523

分布式事务解决方案:Seata实战与源码分析

![分布式事务示意图]想象一下这个场景:某电商平台的"双11"活动中,用户小张点击了"下单"按钮,系统开始处理这个看似简单的操作:库存服务扣减库存、订单服务创建订单、支付服务完成扣款、积分服务增加积分…突然,在完成了库存扣减和订单创建后,支付服务崩溃了!此时系统陷入了一个尴尬的状态:库存已减,订单已创建,但用户并未支付,积分也未增加。这就是分布式事务问题的一个典型场景——一个横跨多个服务的操作无法保证"全部成功或全部失败"。某大型电商平台的技术负责人曾在一次技术分享中透露:在没有合适的分布式事务解决方案前,

2401_88760782的博客 1388
上一篇: 阿里巴巴Java面试题锦集
下一篇: Redis面试题及分布式集群
lldouble
博客等级 码龄16年 135粉丝 8原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值