Sagas属于一个错误管理模式,也同时用于控制复杂事务的执行和回滚等。同时,Compensating-Transaction模式的的实现也是也是类似于Sagas策略的,可以对比参考一下。

Sagas的最开始的出现是因为一些长时间的事务的实现(最开始的时候仅仅是因为数据内的事务),现在也包括一些跨越多个区域的分布式事务。这些长时间持续的事务无法简单地通过一些典型的ACID模型使用多段提交配合持有锁的方式来实现。Sagas策略正式用来解决这个问题,和多段式处理不同,Sagas会将工作分成单独的事务,包含正常的操作和回滚的操作。

如下图:

上图展示了一个简单的Saga。如果有旅客预订了行程,需要预订汽车,航班以及旅店。如果无法获取全部的信息,可能最好就是不要出发。对开发者来说,肯定无法将所有的服务都定义为分布式的ACID事务。这时可以将租车行为定义为一个整体,其中包含如何去预订以及如何取消,当然,机票和酒店也提供同样的服务。

然后这些行为可以组合在一起构成了一个行为链。开发者还可以将整个行为链加密,这样只有该行为链的接收者才能够操控这个行为链。当一个行为完成后,会将完成的信息记录到一个集合(比如说,是一个队列)中,之后可以通过这个集合访问到对应的行为。当一个行为失败的实收,行为将本地清理完毕,然后将消息发送给该集合,从而路由到之前执行成功的行为,然后回滚所有的事务。

如果开发者对于旅行行程了解一些的话,就会知道上面的行为链其实是有风险的。一般来说,提前预订租车服务几乎都会成功,因为租车公司都会有足够的时间来帮助你安排车辆。但是预订宾馆就有一些风险了,一般无押金的情况下,只能提前24小时预订,而航班的退改一般情况下还要收费的,所以最后预订是对的。

使用举例

下面的程序作为样例可以帮助我们更好的了解Sagas策略

程序会生成一个典型的集合用来访问对应的行为链中的行为,会创建3个独立的进程,每一个进程都会负责一个指定的任务。分别是租车,预订酒店以及预订机票三个独立的任务。

static ActivityHost[] processes;

static void Main(string[] args)
{
var routingSlip = new RoutingSlip(new WorkItem[]
{
new WorkItem<ReserveCarActivity>(new WorkItemArguments),
new WorkItem<ReserveHotelActivity>(new WorkItemArguments),
new WorkItem<ReserveFlightActivity>(new WorkItemArguments)
}); // imagine these being completely separate processes with queues between them
processes = new ActivityHost[]
{
new ActivityHost<ReserveCarActivity>(Send),
new ActivityHost<ReserveHotelActivity>(Send),
new ActivityHost<ReserveFlightActivity>(Send)
}; // hand off to the first address
Send(routingSlip.ProgressUri, routingSlip);
} static void Send(Uri uri, RoutingSlip routingSlip)
{
// this is effectively the network dispatch
foreach (var process in processes)
{
if (process.AcceptMessage(uri, routingSlip))
{
break;
}
}
}

其中的AcitivityHost就是对外部服务的一个抽象,RoutingSlip是对前面说的集合的抽象。

下面是具体的一个服务的简化实现,ReserveHotelActivity以及ReserveFlightActivity的实现就不在此处列出了,下面是ReserveCarActivity的实现。其中主要包括的几个方法:

DoWork以及Compensate方法是Activity抽象出来的用来执行实际操作以及回滚的补偿方法。

WorkItemQueueAddress以及CompensationQueueAddress都是用来索引到对应服务的。参考如下代码:

class ReserveCarActivity : Activity
{
static Random rnd = new Random(2); public override WorkLog DoWork(WorkItem workItem)
{
Console.WriteLine("Reserving car");
var car = workItem.Arguments["vehicleType"];
var reservationId = rnd.Next(100000);
Console.WriteLine("Reserved car {0}", reservationId);
return new WorkLog(this, new WorkResult
{
{ "reservationId", reservationId }
});
} public override bool Compensate(WorkLog item, RoutingSlip routingSlip)
{
var reservationId = item.Result["reservationId"];
Console.WriteLine("Cancelled car {0}", reservationId);
return true;
} public override Uri WorkItemQueueAddress
{
get { return new Uri("sb://./carReservations"); }
} public override Uri CompensationQueueAddress
{
get { return new Uri("sb://./carCancellactions"); }
}
}

RoutingSlip是对成功行为集合的抽象,用来索引到对应的服务,包含了两个队列,一个是完成的任务,一个是等待执行的任务。RoutingSlip主要用来控制连接多个行为。如果成功就会将任务向前执行,如果失败就会向后执行

RoutingSlip使用队列来向前执行,使用栈来向后执行。

class RoutingSlip
{
readonly Stack<WorkLog> completedWorkLogs = new Stack<WorkLog>();
readonly Queue<WorkItem> nextWorkItem = new Queue<WorkItem>(); public RoutingSlip()
{
} public RoutingSlip(IEnumerable<WorkItem> workItems)
{
foreach (var workItem in workItems)
{
this.nextWorkItem.Enqueue(workItem);
}
} public bool IsCompleted
{
get { return this.nextWorkItem.Count == 0; }
} public bool IsInProgress
{
get { return this.completedWorkLogs.Count > 0; }
} public bool ProcessNext()
{
if (this.IsCompleted)
{
throw new InvalidOperationException();
} var currentItem = this.nextWorkItem.Dequeue();
var activity = (Activity) Activator.CreateInstance(
currentItem.ActivityType); try
{
var result = activity.DoWork(currentItem);
if (result != null)
{
this.completedWorkLogs.Push(result);
return true;
}
}
catch (Exception e)
{
Console.WriteLine("Exception {0}", e.Message);
}
return false;
} public Uri ProgressUri
{
get
{
if (IsCompleted)
{
return null;
}
else
{
return
((Activity)Activator.CreateInstance(this.nextWorkItem.Peek().ActivityType)).
WorkItemQueueAddress;
}
}
} public Uri CompensationUri
{
get
{
if (!IsInProgress)
{
return null;
}
else
{
return ((Activity) Activator.CreateInstance(
this.completedWorkLogs.Peek().ActivityType)).
CompensationQueueAddress;
}
}
} public bool UndoLast()
{
if (!this.IsInProgress)
{
throw new InvalidOperationException();
} var currentItem = this.completedWorkLogs.Pop();
var activity = (Activity) Activator.CreateInstance(
currentItem.ActivityType); try
{
return activity.Compensate(currentItem, this);
}
catch (Exception e)
{
Console.WriteLine("Exception {0}", e.Message);
throw;
}
}
}

ActivityHost会调用RoutingSlip上面的ProcessNext方法来解析下一个行为并正向调用DoWork()或者反向调用Compensate()方法.

abstract class ActivityHost
{
Action<Uri, RoutingSlip> send; public ActivityHost(Action<Uri, RoutingSlip> send)
{
this.send = send;
} public void ProcessForwardMessage(RoutingSlip routingSlip)
{
if (!routingSlip.IsCompleted)
{
// if the current step is successful, proceed
// otherwise go to the Unwind path
if (routingSlip.ProcessNext())
{
// recursion stands for passing context via message
// the routing slip can be fully serialized and passed
// between systems.
this.send(routingSlip.ProgressUri, routingSlip);
}
else
{
// pass message to unwind message route
this.send(routingSlip.CompensationUri, routingSlip);
}
}
} public void ProcessBackwardMessage(RoutingSlip routingSlip)
{
if (routingSlip.IsInProgress)
{
// UndoLast can put new work on the routing slip
// and return false to go back on the forward
// path
if (routingSlip.UndoLast())
{
// recursion stands for passing context via message
// the routing slip can be fully serialized and passed
// between systems
this.send(routingSlip.CompensationUri, routingSlip);
}
else
{
this.send(routingSlip.ProgressUri, routingSlip);
}
}
} public abstract bool AcceptMessage(Uri uri, RoutingSlip routingSlip);
}

相关的其他模式

  • Compensating-Transaction模式。Compensating-Transaction通常被用来回滚需要实现最终一致性模型的功能,其中针对于数据一致性以及补偿失败的考虑更为详细。
  • Data Consistency PrimerData Consistency Primer中的内容提供了更多关于最终一致性的特性的说明。

Sagas模式的更多相关文章

  1. Compensating-Transaction模式

    在应用中,会将一系列相关的操作定义为一个连续的操作,当其中一个或者多个步骤失败的时候,Compensating-Transaction模式会重置(回滚)这个连续的操作.在云应用中,这些需要保证一致性的 ...

  2. Compensating Transaction Pattern(事务修正模式)

    Undo the work performed by a series of steps, which together define an eventually consistent operati ...

  3. saga中的saga(A Saga on Sagas)

    此文翻译自msdn,侵删. 原文地址:https://msdn.microsoft.com/en-us/library/jj591569.aspx Process Managers, Coordina ...

  4. 让你的saga更具有可伸缩性(Scaling NServiceBus Sagas)

    https://lostechies.com/jimmybogard/2013/03/26/scaling-nservicebus-sagas/ 当我们使用NServiceBus sagas (pro ...

  5. Saga的实现模式——进化(Saga implementation patterns – variations)

    在之前的几个博客中,我主要讲了两个saga的实现模式: 基于command的控制者模式 基于事件的观察者模式 当然,这些都不是实现saga的唯一方式.我们甚至可以将这些结合起来. 发布者——收集者 回 ...

  6. Saga的实现模式——观察者(Saga implementation patterns – Observer)

    https://lostechies.com/jimmybogard/2013/03/11/saga-implementation-patterns-observer/ 侵删. NServiceBus ...

  7. 分布式事务:Saga模式

    1 Saga相关概念 1987年普林斯顿大学的Hector Garcia-Molina和Kenneth Salem发表了一篇Paper Sagas,讲述的是如何处理long lived transac ...

  8. 【原】谈谈对Objective-C中代理模式的误解

    [原]谈谈对Objective-C中代理模式的误解 本文转载请注明出处 —— polobymulberry-博客园 1. 前言 这篇文章主要是对代理模式和委托模式进行了对比,个人认为Objective ...

  9. 彻底理解AC多模式匹配算法

    (本文尤其适合遍览网上的讲解而仍百思不得姐的同学) 一.原理 AC自动机首先将模式组记录为Trie字典树的形式,以节点表示不同状态,边上标以字母表中的字符,表示状态的转移.根节点状态记为0状态,表示起 ...

随机推荐

  1. Expo大作战(一)--什么是expo,如何安装expo clinet和xde,xde如何使用

    简要:本系列文章讲会对expo进行全面的介绍,本人从2017年6月份接触expo以来,对expo的研究断断续续,一路走来将近10个月,废话不多说,接下来你看到内容,讲全部来与官网 我猜去全部机翻+个人 ...

  2. LeetCode题解之Contains Duplicate II

    1.题目描述 2.题目分析 使用哈希表 和分情况讨论的方法 3.代码 bool containsNearbyDuplicate(vector<int>& nums, int k) ...

  3. Oracle EBS CST 成本请求报错

    (文档 ID 430533.1) When running CMCPAW, Periodic Actual Cost Worker,  an error is received in the logf ...

  4. Mysql binlog 无法删除(purge命令无法删除)

    1.版本 1)操作系统 cat /etc/issueCentOS release 6.6 (Final)Kernel \r on an \m cat /proc/versionLinux versio ...

  5. sysbench 多线程异步io模拟mysql测试的脚本

    用于测试的脚本: for size in 100 do cd /mnt/stec sysbench --test=fileio --file-num=1 --file-total-size=${siz ...

  6. jquery.validate,错误信息位置

    好长时间没有用jquery.validate.js这个插件了,忘得差不多了.唉,好东西还是要经常拿出来看看的,今天用jquery.validate来做一个小东西,遇到一个问题,就是错误提示信息的位置问 ...

  7. AD账号解锁

    Get-ADUser -Filter * -Properties *  -SearchBase "dc=uxin,dc=youxinpai,dc=com"| ? {$_.locke ...

  8. beta冲刺————第三天(3/5)

    完善的具体内容: 前端: (1)可以进行修改文字大小背景 其中,金色的文字个人觉得很好看,点赞.(我很满意啊) (2)可以改变成夜间模式(也很不错啊) 后端: 尝试将本地的后端war文件,以及数据库传 ...

  9. 阿里八八Alpha阶段Scrum(4/12)

    今日进度 叶文滔: 整合了一下已完成的界面设计,修复了一些BUG. 问题困难:制作多级悬浮按钮阻碍重重,首先是刚更新不久的Andriod Studio 3.0向前兼容性差,一些语句规则的修改无所适从, ...

  10. Java实现XML和JavaBean互转 - XStream

    1.简介 XStream是一个序列化JavaBean为XML文件以及反序列化的工具. 2.准备 2.1.XML回顾 在学习这部分内容前,假设你对XML以及有一定的了解,至少能够分辨出XML的元素.属性 ...