一、hmily框架

1、到github拉取最新的源码
idea:File - New - Project form Version Control

2、切换release版本
此时拉下来的是master版本,我们就用master分支吧。在右下角可以选择分支,比如选择分支2.2.1版本

3、pom.xml中添加hmily-demo

    <modules><module>hmily-common</module><module>hmily-core</module><module>hmily-annotation</module><module>hmily-spring-boot-starter</module><module>hmily-spi</module><module>hmily-serializer</module><module>hmily-repository</module><module>hmily-config</module><module>hmily-spring</module><module>hmily-rpc</module><module>hmily-tcc</module><module>hmily-tac</module><module>hmily-metrics</module><module>hmily-bom</module><module>hmily-all</module><module>hmily-xa</module><module>hmily-demo</module></modules>

4、初始化sql
hmily-demo/sql/hmily-demo.sql
建立库:
hmily:框架自带的库,事务的管理数据库
hmily_account:账户
hmily_order:订单
hmily_stock:库存

5、测试项目路径
hmily-demo/hmily-demo-tcc/hmily-demo-tcc-springcloud
hmily-demo-tcc-springcloud-account
hmily-demo-tcc-springcloud-eureka
hmily-demo-tcc-springcloud-inventory
hmily-demo-tcc-springcloud-order

6、业务说明

下订单的同时,减库存,扣余额。这三个数据库要么同时提交,要么同时回滚。

7、修改数据库配置
hmily-demo-tcc-springcloud-order
application.yml文件:

spring:main:allow-bean-definition-overriding: truedatasource:driver-class-name:  com.mysql.jdbc.Driverurl: jdbc:mysql://127.0.0.1:3306/hmily_order?useUnicode=true&characterEncoding=utf8username: rootpassword: 123456application:name: order-service

hmily.yml文件:

repository:database:driverClassName: com.mysql.jdbc.Driverurl : jdbc:mysql://127.0.0.1:3306/hmily?useUnicode=true&characterEncoding=utf8username: rootpassword: 123456maxActive: 20minIdle: 10connectionTimeout: 30000idleTimeout: 600000maxLifetime: 1800000

hmily-demo-tcc-springcloud-account、hmily-demo-tcc-springcloud-inventory做相同修改。

8、启动服务
先起eureka
再起order
再起inventory
再起account

9、体验
http://127.0.0.1:8090/swagger-ui.html

测试/order/orderPay

10、查询数据库
账户:

mysql> select * from hmily_account.account;
+----+---------+---------+---------------+---------------------+---------------------+
| id | user_id | balance | freeze_amount | create_time         | update_time         |
+----+---------+---------+---------------+---------------------+---------------------+
|  1 | 10000   | 9999991 |             0 | 2017-09-18 14:54:22 | 2022-04-27 14:08:03 |
+----+---------+---------+---------------+---------------------+---------------------+
1 row in set (0.00 sec)

订单:

mysql> select * from hmily_order.order;
+----+---------------------+----------------------+--------+------------+--------------+-------+---------+
| id | create_time         | number               | status | product_id | total_amount | count | user_id |
+----+---------------------+----------------------+--------+------------+--------------+-------+---------+
|  1 | 2022-04-27 14:08:03 | -6725939227652632576 |      4 | 1          |            9 |     1 | 10000   |
+----+---------------------+----------------------+--------+------------+--------------+-------+---------+
1 row in set (0.00 sec)

库存:

mysql> select * from hmily_stock.inventory;
+----+------------+-----------------+----------------+
| id | product_id | total_inventory | lock_inventory |
+----+------------+-----------------+----------------+
|  1 | 1          |         9999999 |              0 |
+----+------------+-----------------+----------------+
1 row in set (0.00 sec)

11、hmily库

+-------------------------------+
| Tables_in_hmily               |
+-------------------------------+
| hmily_lock                    |
| hmily_participant_undo        |
| hmily_transaction_global      |
| hmily_transaction_participant |
+-------------------------------+
4 rows in set (0.00 sec)

二、分布式事务订单支付的原理

1、try
第一步:
try:插入一条订单数据,状态为支付中

    @Override@HmilyTCC(confirmMethod = "confirmOrderStatus", cancelMethod = "cancelOrderStatus")public void makePayment(Order order) {updateOrderStatus(order, OrderStatusEnum.PAYING);
//        //检查数据
//        final BigDecimal accountInfo = accountClient.findByUserId(order.getUserId());
//        final Integer inventoryInfo = inventoryClient.findByProductId(order.getProductId());
//        if (accountInfo.compareTo(order.getTotalAmount()) < 0) {
//            throw new HmilyRuntimeException("余额不足!");
//        }
//        if (inventoryInfo < order.getCount()) {
//            throw new HmilyRuntimeException("库存不足!");
//        }accountClient.payment(buildAccountDTO(order));inventoryClient.decrease(buildInventoryDTO(order));}

第二步:rpc调用,减账户
try:账户余额 - 消耗,冻结余额 + 消耗

    @Override@HmilyTCC(confirmMethod = "confirm", cancelMethod = "cancel")public boolean payment(final AccountDTO accountDTO) {LOGGER.info("============执行try付款接口===============");accountMapper.update(accountDTO);return Boolean.TRUE;}
    @Update("update account set balance = balance - #{amount}," +" freeze_amount= freeze_amount + #{amount} ,update_time = now()" +" where user_id =#{userId}  and  balance >= #{amount}  ")int update(AccountDTO accountDTO);

第三步:rpc调用,减库存
try:总库存 - 支出,冻结库存 + 支出

    /*** 扣减库存操作.* 这一个tcc接口** @param inventoryDTO 库存DTO对象* @return true*/@Override@HmilyTCC(confirmMethod = "confirmMethod", cancelMethod = "cancelMethod")public Boolean decrease(InventoryDTO inventoryDTO) {LOGGER.info("==========try扣减库存decrease===========");inventoryMapper.decrease(inventoryDTO);return true;}
    @Update("update inventory set total_inventory = total_inventory - #{count}," +" lock_inventory= lock_inventory + #{count} " +" where product_id =#{productId} and total_inventory > 0  ")int decrease(InventoryDTO inventoryDTO);

2、事务管理器
事务管理器把所有的事务记录到hmily库。
事务管理器调用confirm、cancel。异步确认或异步取消。

3、confirm
订单:
confirm:订单状态改为支付成功

    public void confirmOrderStatus(Order order) {updateOrderStatus(order, OrderStatusEnum.PAY_SUCCESS);LOGGER.info("=========进行订单confirm操作完成================");}

账户:
confirm:冻结余额 - 消耗

    public boolean confirm(final AccountDTO accountDTO) {LOGGER.info("============执行confirm 付款接口===============");return accountMapper.confirm(accountDTO) > 0;}
    @Update("update account set " +" freeze_amount= freeze_amount - #{amount}" +" where user_id =#{userId}  and freeze_amount >= #{amount} ")int confirm(AccountDTO accountDTO);

库存:
confirm:冻结库存 - 支出

    public Boolean confirmMethod(InventoryDTO inventoryDTO) {LOGGER.info("==========confirmMethod库存确认方法===========");return inventoryMapper.confirm(inventoryDTO) > 0;}
    @Update("update inventory set " +" lock_inventory = lock_inventory - #{count} " +" where product_id =#{productId} and lock_inventory > 0 ")int confirm(InventoryDTO inventoryDTO);

4、cancel
订单:
cancel:订单状态改为支付失败

    public void cancelOrderStatus(Order order) {updateOrderStatus(order, OrderStatusEnum.PAY_FAIL);LOGGER.info("=========进行订单cancel操作完成================");}

账户:
cancel:账户余额 + 消耗,冻结余额 - 消耗

    public boolean cancel(final AccountDTO accountDTO) {LOGGER.info("============执行cancel 付款接口===============");return accountMapper.cancel(accountDTO) > 0;}
    @Update("update account set balance = balance + #{amount}," +" freeze_amount= freeze_amount -  #{amount} " +" where user_id =#{userId}  and freeze_amount >= #{amount}")int cancel(AccountDTO accountDTO);

库存:
cancel:总库存 + 支出,冻结库存 - 支出

    public Boolean cancelMethod(InventoryDTO inventoryDTO) {LOGGER.info("==========cancelMethod库存取消方法===========");return inventoryMapper.cancel(inventoryDTO) > 0;}
    @Update("update inventory set total_inventory = total_inventory + #{count}," +" lock_inventory= lock_inventory - #{count} " +" where product_id =#{productId}  and lock_inventory > 0 ")int cancel(InventoryDTO inventoryDTO);

5、小结

6、hmily-admin
master分支最新代码,去除了这个模块

三、txx-transaction和例子

1、到github拉取源码
https://github.com/changmingxie/tcc-transaction.git

2、选择master分支

3、初始化数据库
tcc-transaction-tutorial-sample/src/dbscripts
create_db_tcc.sql:事务管理器初始化数据
create_db_red.sql:红包账户数据库
create_db_ord.sql:订单数据库
create_db_cap.sql:账户余额数据库

创建了4个库:
tcc
tcc_cap
tcc_ord
tcc_red

4、txx-transaction例子示例图

5、改配置
tcc-transaction/tcc-transaction-tutorial-sample/pom.xml

    <modules>
<!--        <module>tcc-transaction-dubbo-sample</module>--><module>tcc-transaction-http-sample</module><module>tcc-transaction-sample-domain</module><module>tcc-transaction-multiple-tier-sample</module></modules>

使用的模块:
tcc-transaction/tcc-transaction-tutorial-sample/tcc-transaction-http-sample/tcc-transaction-http-capital:账户余额
tcc-transaction/tcc-transaction-tutorial-sample/tcc-transaction-http-sample/tcc-transaction-http-order:订单
tcc-transaction/tcc-transaction-tutorial-sample/tcc-transaction-http-sample/tcc-transaction-http-redpacket:红包

6、改jdbc连接
没找到
经排查,例子里数据库链接用的是h2内存数据库,在tcc-transaction-sample-domain模块里

7、部署tomcat

8、体验
地址:http://localhost:8080/o/
(1)第一步:点击商品列表链接

(2)第二步:点击商品

(3)第三步:输入红包金额

(4)第四步:点击支付

(5)日志信息

capital try record called. time seq:2022-05-16 15:17:22
red packet try record called. time seq:2022-05-16 15:17:23
order confirm make payment called. time seq:2022-05-16 15:17:24
capital confirm record called. time seq:2022-05-16 15:17:25
red packet confirm record called. time seq:2022-05-16 15:17:26

四、txx-transaction组合支付原理

1、入口

    @RequestMapping(value = "/placeorder", method = RequestMethod.POST)public RedirectView 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());return new RedirectView("payresult/" + merchantOrderNo);}

2、创建订单

    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);order.needToPay(redPacketPayAmount,order.getTotalAmount().subtract(redPacketPayAmount));orderService.update(order);Boolean result = false;try {paymentService.makePayment(order.getMerchantOrderNo());} 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();}

3、使用了tcc的注解

    @Compensable(confirmMethod = "confirmMakePayment", cancelMethod = "cancelMakePayment", asyncConfirm = true)@Transactionalpublic void makePayment(String orderNo) {System.out.println("order try make payment called.time seq:" + DateFormatUtils.format(Calendar.getInstance(), "yyyy-MM-dd HH:mm:ss"));Order order = orderRepository.findByMerchantOrderNo(orderNo);String result = tradeOrderServiceProxy.record(buildCapitalTradeOrderDto(order));String result2 = tradeOrderServiceProxy.record(buildRedPacketTradeOrderDto(order));//        String result = tradeOrderServiceProxy.record(null,buildCapitalTradeOrderDto(order));
//        String result2 = tradeOrderServiceProxy.record(null,buildRedPacketTradeOrderDto(order));}

4、红包接口,也是tcc,先冻结,再提交

    @Override@Compensable(confirmMethod = "confirmRecord", cancelMethod = "cancelRecord")@Transactionalpublic String record(TransactionContext transactionContext, RedPacketTradeOrderDto tradeOrderDto) {try {Thread.sleep(1000l);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("red packet try record called. time seq:" + DateFormatUtils.format(Calendar.getInstance(), "yyyy-MM-dd HH:mm:ss"));TradeOrder foundTradeOrder = tradeOrderRepository.findByMerchantOrderNo(tradeOrderDto.getMerchantOrderNo());//check if the trade order has need recorded.//if record, then this method call return success directly.if (foundTradeOrder == null) {TradeOrder tradeOrder = new TradeOrder(tradeOrderDto.getSelfUserId(),tradeOrderDto.getOppositeUserId(),tradeOrderDto.getMerchantOrderNo(),tradeOrderDto.getAmount());try {tradeOrderRepository.insert(tradeOrder);RedPacketAccount transferFromAccount = redPacketAccountRepository.findByUserId(tradeOrderDto.getSelfUserId());transferFromAccount.transferFrom(tradeOrderDto.getAmount());redPacketAccountRepository.save(transferFromAccount);} catch (DataIntegrityViolationException e) {}}return "success";}

Java基础之《分布式事务(4)—分布式开源框架使用》相关推荐

  1. Netty解决TCP粘包/拆包导致的半包读写问题

    一.TCP粘包/拆包问题说明 TCP是个"流"协议,就是没有界限的一串数据.TCP底层并不了解上层业务数据的具体含义,它会根据TCP缓冲区的实际情况进行包拆分,所以在业务上认为,一 ...

  2. Netty学习总结(5)——Netty之TCP粘包/拆包问题的解决之道

    无论是服务端还是客户端,读取或者发送消息的时候,都需要考虑TCP底层的粘包/拆包机制. TCP粘包/拆包 TCP是个"流"协议. 流:没有界限的一串数据.如同河里的流水,它们是连成 ...

  3. netty解决TCP粘包/拆包导致的半包读写问题的三种方案

    解决方案一:LineBasedFrameDecoder+StringDecoder来解决TCP的粘包/拆包问题 只需要在客户端和服务端加上45.46两行代码并且在发送消息的时候加上换行符即可解决TCP ...

  4. Java基础之《netty(28)—TCP粘包拆包原理》

    一.基本介绍 1.TCP是面向连接的,面向流的,提供高可靠性服务.收发两端(客户端和服务器端)都要有一一成对的socket,因此,发送端为了将多个发给接收端的包,更有效的发给对方,使用了优化方法(Na ...

  5. Netty 之 TCP粘包拆包场景

    转自:http://blog.csdn.net/z69183787/article/details/52595980 TCP编程底层都有粘包和拆包机制,因为我们在C/S这种传输模型下,以TCP协议传输 ...

  6. Netty(二)——TCP粘包/拆包

    转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/7814644.html 前面讲到:Netty(一)--Netty入门程序 主要内容: TCP粘包/拆包的基础知 ...

  7. java tcp怎么拆包_Java网络编程基础之TCP粘包拆包

    TCP是个"流"协议,所谓流,就是没有界限的一串数据.大家可以想象河里的流水,他们是连成一片的,其间并没有分界线.TCP底层并不了解上层业务数据的具体含义,他会根据TCP缓冲区的实 ...

  8. 《精通并发与Netty》学习笔记(13 - 解决TCP粘包拆包(一)概念及实例演示)

    一.粘包/拆包概念 TCP是一个"流"协议,所谓流,就是没有界限的一长串二进制数据.TCP作为传输层协议并不不了解上层业务数据的具体含义,它会根据TCP缓冲区的实际情况进行数据包的 ...

  9. Netty详解(五):Netty TCP粘包 拆包

    1. 概述 无论是服务端还是客户端,我们读取或者发送消息的时候,都需要考虑TCP底层的粘包和拆包机制.下面我们来通过Netty来详解TCP底层的粘包和拆包机制. 2. TCP底层的粘包和拆包机制 TC ...

  10. 一起学Netty(六)之 TCP粘包拆包场景

    TCP编程底层都有粘包和拆包机制,因为我们在C/S这种传输模型下,以TCP协议传输的时候,在网络中的byte其实就像是河水,TCP就像一个搬运工,将这流水从一端转送到另一端,这时又分两种情况: 1)如 ...

最新文章

  1. python读取数据库数据类型有哪些_数据库varchar 相当于python里的哪个类型
  2. ASP.NET跨页面传值技巧
  3. 来自Riot 的一份游戏美术教程(三):角色设计
  4. OC-@dynamic 关键字
  5. 【转】WPF Expander 收缩不占空间的用法
  6. python消息队列celery_python异步任务神器celery
  7. python在线编程翻译器-用Python做一个简单的翻译工具
  8. 备份和还原Windows DHCP服务器
  9. 遵循学术规范,避免学术不端
  10. 提问的智慧 | 推荐好文
  11. 《玩透嵌入式C的角角落落》深入分析sprintf和printf函数
  12. 风寒感冒一般低烧或者不发烧,清鼻涕。风热感冒一般有发烧比较厉害,黄鼻涕的反应
  13. [论文阅读]Spatio-Temporal Graph Routing for Skeleton-Based Action Recognition
  14. 魅色U盘精灵(U盘加密.文档同步.计算机锁定)
  15. Python创建excel,并写入数据
  16. java实现快速排序算法
  17. 项目管理文化:开展有效的总结会议
  18. mupdf 生成dll
  19. kali各种攻击手法笔记
  20. 特效loading图以备不时之需

热门文章

  1. 手机短信删除了怎么恢复
  2. Nginx工作原理和优化总结。
  3. VS2017 调用的目标发生了异常 以及提示脚本错误
  4. C++图形化编程(五子棋)
  5. python hasattr函数_python学习-type(),isinstance(),dir(),getattr(),setattr(),hasattr()
  6. html标签库大全,HTML的标签大全.
  7. Linux下新建一个MySQL数据库
  8. XV6 Lab2:Page Tables
  9. 自动驾驶和智慧交通有它会更好
  10. vivo X3拆机 5.75mm全球最薄顶级Hi-Fi