Java基础之《分布式事务(4)—分布式开源框架使用》
一、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)—分布式开源框架使用》相关推荐
- Netty解决TCP粘包/拆包导致的半包读写问题
一.TCP粘包/拆包问题说明 TCP是个"流"协议,就是没有界限的一串数据.TCP底层并不了解上层业务数据的具体含义,它会根据TCP缓冲区的实际情况进行包拆分,所以在业务上认为,一 ...
- Netty学习总结(5)——Netty之TCP粘包/拆包问题的解决之道
无论是服务端还是客户端,读取或者发送消息的时候,都需要考虑TCP底层的粘包/拆包机制. TCP粘包/拆包 TCP是个"流"协议. 流:没有界限的一串数据.如同河里的流水,它们是连成 ...
- netty解决TCP粘包/拆包导致的半包读写问题的三种方案
解决方案一:LineBasedFrameDecoder+StringDecoder来解决TCP的粘包/拆包问题 只需要在客户端和服务端加上45.46两行代码并且在发送消息的时候加上换行符即可解决TCP ...
- Java基础之《netty(28)—TCP粘包拆包原理》
一.基本介绍 1.TCP是面向连接的,面向流的,提供高可靠性服务.收发两端(客户端和服务器端)都要有一一成对的socket,因此,发送端为了将多个发给接收端的包,更有效的发给对方,使用了优化方法(Na ...
- Netty 之 TCP粘包拆包场景
转自:http://blog.csdn.net/z69183787/article/details/52595980 TCP编程底层都有粘包和拆包机制,因为我们在C/S这种传输模型下,以TCP协议传输 ...
- Netty(二)——TCP粘包/拆包
转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/7814644.html 前面讲到:Netty(一)--Netty入门程序 主要内容: TCP粘包/拆包的基础知 ...
- java tcp怎么拆包_Java网络编程基础之TCP粘包拆包
TCP是个"流"协议,所谓流,就是没有界限的一串数据.大家可以想象河里的流水,他们是连成一片的,其间并没有分界线.TCP底层并不了解上层业务数据的具体含义,他会根据TCP缓冲区的实 ...
- 《精通并发与Netty》学习笔记(13 - 解决TCP粘包拆包(一)概念及实例演示)
一.粘包/拆包概念 TCP是一个"流"协议,所谓流,就是没有界限的一长串二进制数据.TCP作为传输层协议并不不了解上层业务数据的具体含义,它会根据TCP缓冲区的实际情况进行数据包的 ...
- Netty详解(五):Netty TCP粘包 拆包
1. 概述 无论是服务端还是客户端,我们读取或者发送消息的时候,都需要考虑TCP底层的粘包和拆包机制.下面我们来通过Netty来详解TCP底层的粘包和拆包机制. 2. TCP底层的粘包和拆包机制 TC ...
- 一起学Netty(六)之 TCP粘包拆包场景
TCP编程底层都有粘包和拆包机制,因为我们在C/S这种传输模型下,以TCP协议传输的时候,在网络中的byte其实就像是河水,TCP就像一个搬运工,将这流水从一端转送到另一端,这时又分两种情况: 1)如 ...
最新文章
- python读取数据库数据类型有哪些_数据库varchar 相当于python里的哪个类型
- ASP.NET跨页面传值技巧
- 来自Riot 的一份游戏美术教程(三):角色设计
- OC-@dynamic 关键字
- 【转】WPF Expander 收缩不占空间的用法
- python消息队列celery_python异步任务神器celery
- python在线编程翻译器-用Python做一个简单的翻译工具
- 备份和还原Windows DHCP服务器
- 遵循学术规范,避免学术不端
- 提问的智慧 | 推荐好文
- 《玩透嵌入式C的角角落落》深入分析sprintf和printf函数
- 风寒感冒一般低烧或者不发烧,清鼻涕。风热感冒一般有发烧比较厉害,黄鼻涕的反应
- [论文阅读]Spatio-Temporal Graph Routing for Skeleton-Based Action Recognition
- 魅色U盘精灵(U盘加密.文档同步.计算机锁定)
- Python创建excel,并写入数据
- java实现快速排序算法
- 项目管理文化:开展有效的总结会议
- mupdf 生成dll
- kali各种攻击手法笔记
- 特效loading图以备不时之需
热门文章
- 手机短信删除了怎么恢复
- Nginx工作原理和优化总结。
- VS2017 调用的目标发生了异常 以及提示脚本错误
- C++图形化编程(五子棋)
- python hasattr函数_python学习-type(),isinstance(),dir(),getattr(),setattr(),hasattr()
- html标签库大全,HTML的标签大全.
- Linux下新建一个MySQL数据库
- XV6 Lab2:Page Tables
- 自动驾驶和智慧交通有它会更好
- vivo X3拆机 5.75mm全球最薄顶级Hi-Fi