TCC:
try阶段已经提交了。
try成功confrim肯定成功的。
重试都有幂等的问题。
同一段代码执行多次要保证结果相同:一个请求执行多少次都是可以的。
TM首先发起所有的分支事务的try操作。任何一个分支失败,则发起所有的分支的cancle操作。try成功则发起所有分支的confirm操作。两个CC操作失败则TM会重试。
Hmily是利用aop进行处理的。
需要提供一个数据库进行日志的存储。
--------------------------------------------------------------17-------------------------------------------------------------------
需要数据库的。hmily数据库用来储存框架记录的数据。
只要表明哪个方法是try方法就可以了。
空回滚:没有调用try就调用cancel。try confirm cancel是独立的线程去调用的。try加了钱,但是没有调用就调用cancel减钱了。
悬挂:cancel在try之前执行。try调用出现网络问题了,执行cancel,后来好了,继续执行try。
幂等:confirm和cancel可能会重复执行。
注意:TCC每一步执行完毕之后都会提交的。
幂等校验都有的。
----------------------------------------------------------------18------------------------------------------------------------------------
第一步:导入数据库
数据库总览:
三个log控制幂等。
一个Hmily数据库记录数据,注意是数据库不是表注意了。
第二步:导入工程
第三步:依赖
第三步:微服务的配置信息
我们看到这个被激活了:
找到:application-local
org:
dromara:
hmily :
serializer : kryo
recoverDelayTime : 30
retryMax : 30
scheduledDelay : 30
scheduledThreadMax : 10
repositorySupport : db
started: true
hmilyDbConfig :
driverClassName : com.mysql.jdbc.Driver
url : jdbc:mysql://192.168.244.130:3306/hmily?useUnicode=true
username : root
password : 123456
保存管理事务的数据。
谁开启事务在谁那里指定为true
这个信息都是读取的配置项的信息。
第四步启动类,要加个包的扫描的:
开启AOP:
----------------------------------------------------------------19-------------------------------------------------------------------------
逻辑要点:
1.在cancel执行回滚逻辑不是回滚。
2.单独的数据库存储日志。
3.空回滚:没有进行try阶段就宕机了执行回滚。
4.幂等
5.悬挂:cancel比try先执行,张三调用李四在转账,李四超时通知rm回滚,过一会好了,执行到try了。提前通知回滚了。就是先回滚预留资源了。
6.执行完try我就提交了,只是数据记录在hmily里面,在数据库里面存上了。
try:
第一步:
第二步:Dao
1.基本的加减钱
2.日志控制幂等
3.写代码:这个框架只要求暴露一个try方法即可。
/**
* try幂等校验
* try悬挂处理
* 检查余额是够扣减金额
* 扣减金额
* @param accountNo
* @param amount
*/
@Override
@Transactional
//只要标记@Hmily就是try方法,在注解中指定confirm、cancel两个方法的名字 就在本类中定义的 参数是一样的
@Hmily(confirmMethod="commit",cancelMethod="rollback") // 这个注解标上代理对象就会产生一个全局的事务id
public void updateAccountBalance(String accountNo, Double amount) {
//获取全局事务id 当前事务的事务id 注解标注了就会获取全局事务的id
String transId = HmilyTransactionContextLocal.getInstance().get().getTransId();
log.info("bank1 try begin 开始执行...xid:{}",transId);
//幂等判断 判断local_try_log表中是否有try日志记录,如果有则不再执行
if(accountInfoDao.isExistTry(transId)>0){
log.info("bank1 try 已经执行,无需重复执行,xid:{}",transId);
return ;
}
//try悬挂处理,如果在本次事务中cancel、confirm有一个已经执行了,try不再执行
if(accountInfoDao.isExistConfirm(transId)>0 || accountInfoDao.isExistCancel(transId)>0){
log.info("bank1 try悬挂处理 cancel或confirm已经执行,不允许执行try,xid:{}",transId);
return ;
}
//扣减金额
if(accountInfoDao.subtractAccountBalance(accountNo, amount)<=0){
//扣减失败
throw new RuntimeException("bank1 try 扣减金额失败,xid:{}"+transId);
}
//插入try执行记录,用于幂等判断
accountInfoDao.addTry(transId);
//远程调用李四,转账
if(!bank2Client.transfer(amount)){
throw new RuntimeException("bank1 远程调用李四微服务失败,xid:{}"+transId);
}
if(amount == 2){
throw new RuntimeException("人为制造异常,xid:{}"+transId);
}
log.info("bank1 try end 结束执行...xid:{}",transId);
}
----------------------------------------------------------------20----------------------------------------------------------------------
rollback:
/** cancel方法
* cancel幂等校验
* cancel空回滚处理
* 增加可用余额
* @param accountNo
* @param amount
*/
@Transactional
public void rollback(String accountNo, Double amount){
//获取全局事务id
String transId = HmilyTransactionContextLocal.getInstance().get().getTransId();
log.info("bank1 cancel begin 开始执行...xid:{}",transId);
// cancel幂等校验
if(accountInfoDao.isExistCancel(transId)>0){
log.info("bank1 cancel 已经执行,无需重复执行,xid:{}",transId);
return ;
}
//cancel空回滚处理,如果try没有执行,cancel不允许执行
if(accountInfoDao.isExistTry(transId)<=0){
log.info("bank1 空回滚处理,try没有执行,不允许cancel执行,xid:{}",transId);
return ;
}
// 增加可用余额
accountInfoDao.addAccountBalance(accountNo,amount);
//插入一条cancel的执行记录
accountInfoDao.addCancel(transId);
log.info("bank1 cancel end 结束执行...xid:{}",transId);
}
feign:
@FeignClient(value="tcc-demo-bank2",fallback=Bank2ClientFallback.class)
public interface Bank2Client {
//远程调用李四的微服务
@GetMapping("/bank2/transfer")
@Hmily //这个注解一定要加的
public Boolean transfer(@RequestParam("amount") Double amount);
}
----------------------------------------------------------------21----------------------------------------------------------------------
bank2:
@Override
@Hmily(confirmMethod="confirmMethod", cancelMethod="cancelMethod")
public void updateAccountBalance(String accountNo, Double amount) {
//获取全局事务id
String transId = HmilyTransactionContextLocal.getInstance().get().getTransId();
log.info("bank2 try begin 开始执行...xid:{}",transId);
}
/**
* confirm方法
* confirm幂等校验
* 正式增加金额
* @param accountNo
* @param amount
*/
@Transactional
public void confirmMethod(String accountNo, Double amount){
//获取全局事务id
String transId = HmilyTransactionContextLocal.getInstance().get().getTransId();
log.info("bank2 confirm begin 开始执行...xid:{}",transId);
if(accountInfoDao.isExistConfirm(transId)>0){
log.info("bank2 confirm 已经执行,无需重复执行...xid:{}",transId);
return ;
}
//增加金额
accountInfoDao.addAccountBalance(accountNo,amount);
//增加一条confirm日志,用于幂等
accountInfoDao.addConfirm(transId);
log.info("bank2 confirm end 结束执行...xid:{}",transId);
}
----------------------------------------------------------------22----------------------------------------------------------------------
测试:
这两个表是自动生成的,记录事务协调的信息,表名是服务名称。
李四加异常。
李四执行失败不会回滚。会重试的,不会回滚。所以cancel为空。confirm执行必须成功。
李四不能在try里面提钱会先花掉的。
-------------------------
李四有问题这个日志有内容了用于回滚。
是因为:在张三这里把微服务信息带给李四。
------------
张三出现异常:回滚。
李四的微服务断掉:没有回滚因为没有执行完try,在try里面本地事务没有提交。
结论:事务ID传给了李四则认为try成功结束。
----------------------------------------------------------------23----------------------------------------------------------------------
测试:
hmily的全局事务数据。
记录事务协调的信息。
表的命名规则:hmily_微服务的名称