这里写自定义目录标题
- Sagas
- Sagas 的实现范式
Sagas
每个Sagas由一系列sub-transaction Ti (saga)组成
每个Ti 都有对应的补偿动作Ci,补偿动作用于撤销Ti造成的结果
可以看到,和TCC相比,Saga没有“预提交”阶段,它的Ti就是直接提交到库。
- Sagas两种可能的执行流程
- T1, T2, T3, …, Tn
- T1, T2, …, Ti, Ci,…, C2, C1,其中0 < i < n
- Sagas的 恢复策略
- backward recovery,向后恢复,补偿所有已完成的事务,如果任一子事务失败。即上面提到的第二种执行顺序,其中j是发生错误的sub-transaction,这种做法的效果是撤销掉之前所有成功的sub-transation,使得整个Sagas的执行结果撤销。
- forward recovery,向前恢复,重试失败的事务,假设每个子事务最终都会成功。适用于必须要成功的场景,执行顺序是类似于这样的:T1, T2, …, Tj(失败), Tj(重试),…, Tn,其中j是发生错误的sub-transaction。该情况下不需要Ci。
- Sagas的使用注意事项
- 只允许两层嵌套,整个事务我们叫主事务(Sagas)和子事务Saga,子事务(saga)是互相独立的原子操作。
- Sagas不保证ACID,只保证最终一致性。即Ci必须保证成功,即使系统不能成功也需要人工干预,这种情况下需要saga log支持。
- 基于上面,整个Sagas事务无原子性特征,即一个Sagas可以看到另外一个Sagas的中间状态,即部分结果
- Ti、Ci需要幂等,且先后执行Ti、Ci即使顺序不同也能保证结果一致,即事务被撤销
- 补充事务Ci从语义上撤销了Ti的影响,但不一定能让系统/数据库回到原先的状态。例如Ti将邮件发送出去了,Ti发射了鱼雷。这里需要特别注意
- Sagas的优缺点和使用场景
由于saga直接提交事务而没有TCC的Try的阶段,所以减少了一次通信成本,效率更高,但对于复杂的事务,提供补充机制的成本可能很高,同样没有预提交也没有资源释放及异常处理的问题,这点和TCC相比算是各有优势;另外对于长事务,即i值较大时,回退的流程可能很长,性能和效率会有较大影响;最后,没有预提交阶段,存证上节提到的Ci无法保证系统的整体状态回到Ti发生前。
基于此的场景:
- 较简单的业务,提供补充Ci较简单
- 系统集成,其中遗留子系统或第三方服务无法提供预提交(Try)能力的情况,只需要增加补偿机制即可实现
Sagas 的实现范式
下面以一个电商系统为例,该案例中Saga的实现层,基于一个可持久化且可靠的消息系统实现
- ** 基于编排模式(Choreography)**
基于编排的Sagas创建订单,包含以下步骤:
- 在Order Service接收到该POST /orders请求并创建一个PENDING状态的Order
- 然后发出一个Order Created事件
- Customer Service的事件处理程序尝试扣减信用(信用卡消费)
- 然后Customer Service发出一个指示结果的事件
- OrderService的事件处理程序完成或拒绝Order
- ** 基于编制模式(Orchestration)**
基于编制的Sagas创建订单,包含以下步骤:
- 在Order Service接收到该POST /orders请求并创建Create Order Sagas协调器(orchestrator,可看作一个编制的api,后续介绍服务编排会再提到)
- Sagas协调器创建一个PENDING状态的Order
- 然后,它发送Reserve Credit命令到Customer Service
- Customer Service尝试扣减信用
- 然后,Customer Service发送回包含结果的回复消息
- Sagas协调器完成或拒绝 Order