好啦,开始今天的正文吧。
Saga 模式是 Seata 提供的长事务解决方案,在 Saga 模式中,业务流程中每个参与者都提交本地事务,当出现某一个参与者失败则补偿前面已经成功的参与者,一阶段正向服务和二阶段补偿服务都由业务开发实现。
注意最后一句话很关键,说明 Saga 模式的回滚其实和 AT、TCC 的回滚一样,都是反向补偿操作(区别于 XA 模式)。
官方给了下面一张流程图,我们一起来看下:
可以看到,T1、T2、T3 一直到 Tn 分别代表分布式事务中的分支事务,这条线都是事务的正常状态,如果在执行的过程中,有某一个抛出异常,则执行 C3、C2 一直到 C1 进行事务的回滚,这里的回滚实际上就是反向补偿操作。
一般来说,Saga 模式适用于业务流程长、业务流程多的分布式事务,就像上面的流程图这样,不过当业务流程比较长的时候,如何去定义每一个事务的状态也就成了问题。
这里就涉及到 Saga 分布式事务的状态机。
状态图这个东西,如果小伙伴们用过 Activiti 流程引擎,那么基本上就知道什么是状态图,Saga 的状态图跟那个也差不多。
Saga 中的状态图是这样:
- 首先我们需要定义一个状态流程图,像下面这样:
这个流程图官方提供了绘制工具,地址如下:
https://seata.io/saga_designer/index.html
官方还为此提供了一个视频教程,松哥看了下,录视频的人估计也是第一次录视频,没啥经验,视频各种问题没法看,所以我就不放链接了,小伙伴们在工作中如果需要绘制状态图,可以参考这个文档:
https://help.aliyun.com/document_detail/172550.html
流程图上记录了每一个分支事务的状态以及相关的补偿操作,流程图画好之后,会自动生成 JSON 状态语言定义文件,把这个文件将来拷贝到项目中。
-
状态图中的每一个节点可以调用一个服务,每一个节点都可以配置它的补偿节点,当节点出现异常时状态引擎反向执行已成功节点对应的补偿节点将事务回滚(是否回滚可由用户自行决定)。
-
状态图可以实现服务编排需求,支持单项选择、并发、子流程、参数转换、参数映射、服务执行状态判断、异常捕获等功能。
我们来看一个 Saga 模式的案例,看完案例大家就懂什么是 Saga 模式了。
3.1 准备工作
我们还是使用官方的案例。不过还是松哥之前说的,官方的案例容易导入失败,并且里边有的地方有问题,所以小伙伴们可以直接在公众号后台回复 seata-demo
下载本文案例。
Saga 的例子我们用这个:
如果小伙伴们直接使用官方的案例,需要做如下修改:
- 修改 Dubbo 的版本为 2.7.3,原本默认的 3.0.1 这个版本运行时候有问题。
org.apache.dubbo
dubbo
org.springframework
spring
2.7.3
- 官方默认提供的数据库脚本少一个字段,不知道咋回事,这么明显的 BUG。这个需要我们在
src/main/resources/sql/h2_init.sql
文件中,为 seata_state_inst 表添加一个gmt_updated timestamp(3) not null
字段。
准备工作就算完成啦。
3.2 测试运行
接下来我们来测试运行。
首先我们先来执行 src/main/java/io/seata/samples/saga/starter/DubboSagaProviderStarter.java
中的 main 方法,启动服务端。
然后打开 src/main/java/io/seata/samples/saga/starter/DubboSagaTransactionStarter.java
类,这个类需要修改一下才能运行。
public static void main(String[] args) {
AbstractApplicationContext applicationContext = new ClassPathXmlApplicationContext(new String[] {“spring/seata-saga.xml”, “spring/seata-dubbo-reference.xml”});
StateMachineEngine stateMachineEngine = (StateMachineEngine) applicationContext.getBean(“stateMachineEngine”);
transactionCommittedDemo(stateMachineEngine);
transactionCompensatedDemo(stateMachineEngine);
new ApplicationKeeper(applicationContext).keep();
}
可以看到,这个 main 方法中有两个测试方法:
-
transactionCommittedDemo
-
transactionCompensatedDemo
第一个方法是二阶段提交的测试,第二个方法是二阶段补偿的测试,我们注释掉其中一个,每次执行的时候只要执行其中一个就可以了。
另外,对于 transactionCommittedDemo 方法,它里边提供了两种状态的获取方式:同步和异步,我们需要注释掉其中一种然后进行测试,像下面这样:
private static void transactionCommittedDemo(StateMachineEngine stateMachineEngine) {
Map<String, Object> startParams = new HashMap<>(3);
String businessKey = String.valueOf(System.currentTimeMillis());
startParams.put(“businessKey”, businessKey);
startParams.put(“count”, 10);
startParams.put(“amount”, new BigDecimal(“100”));
//sync test
//StateMachineInstance inst = stateMachineEngine.startWithBusinessKey(“reduceInventoryAndBalance”, null, businessKey, startParams);
//Assert.isTrue(ExecutionStatus.SU.equals(inst.getStatus()), "saga transaction execute failed. XID: " + inst.getId());
//System.out.println("saga transaction commit succeed. XID: " + inst.getId());
//async test
businessKey = String.valueOf(System.currentTimeMillis());
StateMachineInstance inst = stateMachineEngine.startWithBusinessKeyAsync(“reduceInventoryAndBalance”, null, businessKey, startParams, CALL_BACK);
waittingForFinish(inst);
Assert.isTrue(ExecutionStatus.SU.equals(inst.getStatus()), "saga transaction execute failed. XID: " + inst.getId());
System.out.println("saga transaction commit succeed. XID: " + inst.getId());
}
注释掉同步的代码块或者注释掉异步的代码块,注释掉之后,执行 main 方法进行测试。
如果测试 transactionCommittedDemo 方法,控制台打印日志如下:
saga transaction commit succeed. XID: 192.168.1.105:8091:2612256553007833092
如果测试 transactionCompensatedDemo 方法,控制台打印日志如下:
saga transaction compensate succeed. XID: 192.168.1.105:8091:2612256553007833094
能看到如上两个日志,说明案例运行没问题了。
接下来我们就来分析一下,这个案例到底讲了个啥!
3.3 案例分析
3.3.1 JSON 状态描述分析
这个案例并没有一个明确的业务,就单纯是一个案例。
首先定义了两个 Action:
-
InventoryAction
-
BalanceAction
这两个 Action 中各自定义了两个方法:
-
reduce
-
compensateReduce
从方法名就能看出,reduce 方法就是正常的执行逻辑,compensateReduce 方法则是代码补偿逻辑,即回滚的时候需要执行的代码。
具体到这两个方法的实现上,并没有啥,都是打印日志,所以这个项目我们只需要认真观察打印出来的日志,就能知道事务是提交了还是回滚了。
在 src/main/resources/statelang/reduce_inventory_and_balance.json
文件中定义了各个事务的状态,我们可以大概看一下,由于完整 JSON 文件比较长,我就分段贴出来。
{
“Name”: “reduceInventoryAndBalance”,
“Comment”: “reduce inventory then reduce balance in a transaction”,
“StartState”: “ReduceInventory”,
“Version”: “0.0.1”,
…
…
上面这段定义了状态机的名称为 reduceInventoryAndBalance,在一个项目中,我们可以同时存在多个这样的 JSON 文件,每一个都有一个 name 属性,这样在 Java 代码调用的时候就可以通过具体的名字去指定需要调用哪一个流程了。StartState 则定义了整个流程从 ReduceInventory 开始,ReduceInventory 是后面定义的节点。
“ReduceInventory”: {
“Type”: “ServiceTask”,
“ServiceName”: “inventoryAction”,
“ServiceMethod”: “reduce”,
“CompensateState”: “CompensateReduceInventory”,
“Next”: “ChoiceState”,
“Input”: [
“$.[businessKey]”,
“$.[count]”
],
“Output”: {
“reduceInventoryResult”: “$.#root”
},
“Status”: {
“#root == true”: “SU”,
“#root == false”: “FA”,
小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Java工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Java开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
Kafka进阶篇知识点
Kafka高级篇知识点
44个Kafka知识点(基础+进阶+高级)解析如下
由于篇幅有限,小编已将上面介绍的**《Kafka源码解析与实战》、Kafka面试专题解析、复习学习必备44个Kafka知识点(基础+进阶+高级)都整理成册,全部都是PDF文档**
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
[外链图片转存中…(img-lA2a2aHc-1711098926741)]
Kafka进阶篇知识点
[外链图片转存中…(img-0fGolo4c-1711098926742)]
Kafka高级篇知识点
[外链图片转存中…(img-LVmDWPfL-1711098926742)]
44个Kafka知识点(基础+进阶+高级)解析如下
[外链图片转存中…(img-7qBlYI68-1711098926743)]
由于篇幅有限,小编已将上面介绍的**《Kafka源码解析与实战》、Kafka面试专题解析、复习学习必备44个Kafka知识点(基础+进阶+高级)都整理成册,全部都是PDF文档**