Transactions 执行顺序操作的最佳实践
同时执行两个任务的最佳方式是什么?如果一个任务失败,则不应完成下一个任务?我知道如果是数据库操作,那么我应该使用事务,但我谈论的是不同类型的操作,例如: 所有任务必须通过: 发送电子邮件 档案室数据库 创建文件 在上述场景中,所有任务都必须通过,否则整个批处理操作必须回滚 在C中#Transactions 执行顺序操作的最佳实践,transactions,Transactions,同时执行两个任务的最佳方式是什么?如果一个任务失败,则不应完成下一个任务?我知道如果是数据库操作,那么我应该使用事务,但我谈论的是不同类型的操作,例如: 所有任务必须通过: 发送电子邮件 档案室数据库 创建文件 在上述场景中,所有任务都必须通过,否则整个批处理操作必须回滚 在C中# 返回sendmail()&&ArchiveResportsInDatabase()&&CreateAFile() 如果您的语言允许,这是非常整洁的: 将任务放入代码块或函数指针数组中 迭代数组 如果任何块返回失败,则
返回sendmail()&&ArchiveResportsInDatabase()&&CreateAFile() 如果您的语言允许,这是非常整洁的:
try {
task1();
task2();
task3();
...
taskN();
}
catch (TaskFailureException e) {
dealWith(e);
}
您没有提到您正在使用的编程语言/环境。如果是.NET框架,您可能想看看。它描述了Microsoft Robotics Studio的并发和控制运行时,它允许您对一组(异步)事件应用各种规则:例如,您可以等待任意数量的事件完成,如果一个事件失败则取消,等等。它还可以在多个线程中运行,因此,您可以获得一种非常强大的方法来完成任务。您不需要指定您的环境。在unixshell脚本中,&&操作符就是这样做的
SendEmail () {
# ...
}
ArchiveReportsInDatabase () {
# ...
}
CreateAFile () {
# ...
}
SendEmail && ArchiveReportsInDatabase && CreateAFile
有几点建议: 在分布式场景中,可能需要某种两阶段提交协议。基本上,您向所有参与者发送一条消息,告诉他们“准备做X”。然后,每个参与者必须发送一个回复,说“好的,我保证我能做X”或“不,不能做”。如果所有参与者都保证他们能完成,然后发送消息告诉他们去做。“保证”可以根据需要严格 另一种方法是为每个操作提供某种撤销机制,然后具有如下逻辑:
try:
SendEmail()
try:
ArchiveReportsInDatabase()
try:
CreateAFile()
except:
UndoArchiveReportsInDatabase()
raise
except:
UndoSendEmail()
raise
except:
// handle failure
(您不希望您的代码看起来像这样;这只是逻辑应该如何流动的一个示例。)如果您使用的是使用(Java和C#do)的语言,您可以简单地执行以下操作:
return SendEmail() && ArchiveResportsInDatabase() && CreateAFile();
如果所有函数都返回true,则返回true,并且在第一个函数返回false时立即停止。异常通常适用于此类情况。伪Java/JavaScript/C++代码:
try {
if (!SendEmail()) {
throw "Could not send e-mail";
}
if (!ArchiveReportsInDatabase()) {
throw "Could not archive reports in database";
}
if (!CreateAFile()) {
throw "Could not create file";
}
...
} catch (Exception) {
LogError(Exception);
...
}
如果您的方法本身抛出异常,则更好:
try {
SendEmail();
ArchiveReportsInDatabase();
CreateAFile();
...
} catch (Exception) {
LogError(Exception);
...
}
这种风格的一个非常好的结果是,当您沿着任务链向下移动时,代码不会越来越缩进;所有方法调用都保持在相同的缩进级别。缩进过多会使代码更难阅读
此外,代码中只有一点用于错误处理、日志记录、回滚等。要真正做到这一点,您应该使用异步消息传递模式。我刚刚完成了一个项目,在这个项目中我使用了和MSMQ 基本上,每一步都是通过向队列发送消息来完成的。当nServiceBus发现队列中等待的消息时,它将调用对应于该消息类型的句柄方法。这样,每个单独的步骤都可以独立地失败和恢复。如果一个步骤失败,消息将进入错误队列,以便您稍后可以轻松重试
所建议的这些纯代码解决方案并没有那么健壮,因为如果一个步骤失败,您将无法在将来仅重试该步骤,并且您必须实现回滚代码,而在某些情况下甚至不可能实现回滚代码。回滚非常困难-好吧,实际上只有两种方法。要么是a,要么是。你真的必须找到一种方法,以这种方式来组织你的任务 通常,更好的办法是利用其他人的辛勤工作,使用已经内置了2PC或补偿的技术。这就是RDBMS如此流行的原因之一 因此,细节取决于任务……但模式相当简单:
class Compensator {
Action Action { get; set; }
Action Compensate { get; set; }
}
Queue<Compensator> actions = new Queue<Compensator>(new Compensator[] {
new Compensator(SendEmail, UndoSendEmail),
new Compensator(ArchiveReportsInDatabase, UndoArchiveReportsInDatabase),
new Compensator(CreateAFile, UndoCreateAFile)
});
Queue<Compensator> doneActions = new Queue<Compensator>();
while (var c = actions.Dequeue() != null) {
try {
c.Action();
doneActions.Add(c);
} catch {
try {
doneActions.Each(d => d.Compensate());
} catch (EXception ex) {
throw new OhCrapException("Couldn't rollback", doneActions, ex);
}
throw;
}
}
类补偿器{
动作动作{get;set;}
动作补偿{get;set;}
}
队列操作=新队列(新补偿器[]{
新补偿器(SendEmail、UndoSendEmail),
新补偿器(ArchiveReportsInDatabase、UndoArchiveReportsInDatabase),
新补偿器(CreateAFile、UndoCreateAFile)
});
Queue doneActions=new Queue();
while(var c=actions.Dequeue()!=null){
试一试{
c、 动作();
加入(c);
}抓住{
试一试{
每个(d=>d.Compensate());
}捕获(例外情况除外){
抛出新异常(“无法回滚”,doneActions,ex);
}
投掷;
}
}
当然,对于你的特定任务,你可能很幸运
- 显然,RDBMS工作已经可以包装在事务中李>
- 如果您使用的是Vista或Server2008,那么您就可以介绍CreateFile场景
- 电子邮件有点棘手-我不知道有任何2PC或补偿(不过,如果有人指出Exchange有一个,我只会有点惊讶),所以我可能会写一个通知,让订阅者拿起它并最终发送电子邮件。此时,您的事务实际上只包括将消息发送到队列,但这可能已经足够好了
所有这些都可以参与事务,因此您的状态应该很好。当一个流程失败时,它会停止该流程,并且在完成您想要的任务时,我可以看到的步骤/结构最短。更简单:return sendmail()&&ArchiveResportsInDatabase()&&CreateAFile();如果CreateAFile()失败,其他步骤仍将完成。这就是你想要的吗?大量的代码缩进使其更难阅读。如果你的操作不止3次,而是10次呢?如果CreateFile失败了,一切都会失败。使用.net C#windows应用程序我正在使用上述方法,但考虑是否还有其他更好的方法!但如果CreateAFile返回false,则SendEmail和ArchiverReportsInDatabase已经执行。我想