(二十)—— 调度程序代理管理者模式
最后更新于:2022-04-01 03:29:01
# 云计算设计模式(二十)——调度程序代理管理者模式
协调一系列在分布式服务集和其他远程资源的的行为,试图透明地处理故障,如果这些操作失败,或撤销,如果系统不能从故障中恢复执行工作的影响。这种模式可以分布式系统中增加弹性和灵活性,使之恢复和重试失败是由于短暂的异常,持久的故障和处理故障等操作。
[TOC=2,2]
## 背景和问题
应用程序执行其包括多个步骤,其中的一些可以调用远程服务或访问远程资源的任务。各个步骤可以是相互独立的,但它们是由实现该任务的应用程序逻辑编排。
只要有可能,应用程序应该确保任务运行完成和解决远程访问服务或资源时可能发生的任何故障。可能会因各种原因,这些故障。例如,该网络可能是崩溃,通信可能被中断,远程服务可能停止响应或处于不稳定的状态,或远程资源可能暂时无法访问,可能由于资源约束。在许多情况下,这些故障可能是暂时的,并且可以通过使用重试模式进行处理。
如果该应用程序检测到一个更永久的故障,从它可以不容易恢复,它必须能够将系统恢复到一致状态,并确保整个端至端的操作的完整性。
## 解决方案
调度代理管理者模式定义了以下角色。这些演员编排的步骤(工作,单个项目)将作为任务(整个过程)的一部分进行:
- 调度安排构成的整体任务要执行,并配合它们的操作的各个步骤。可按照下列步骤组合成一个管道或工作流,并且所述调度器是负责确保在这个工作流程中的步骤,以适当的顺序被执行。作为各步骤中进行(如“步骤还未开始”,“步运行时,”或“步骤完成”),并记录有关该状态的信息的调度器维护关于工作流的状态信息。这个状态信息也应包括的允许的步骤来完成(称为完全按时间)的时间的上限。如果一个步骤需要访问远程服务或资源时,调度程序调用适当的代理程序,通过它的工作的细节将被执行。调度通常采用异步请求/响应消息与代理进行通讯。这可以通过使用队列来实现,尽管也可以使用其它分布式消息传递技术来代替。
> 注意: 调度程序执行类似的功能,以在流程管理模式的过程管理器。实际工作流程通常被定义并通过由该调度器所控制的工作流引擎来实现。这种方法分离了业务逻辑从调度的工作流程。
- 代理包含逻辑,封装调用一个远程服务,或访问由在一个任务的步骤中引用的远程资源。每个代理通常通过它可以调用单个服务或资源,实施相应的错误处理和重试逻辑(如有超时限制,稍后说明)。如果由调度程序所运行的工作流中的步骤利用在不同步骤的若干服务和资源,每个步骤可能会引用不同代理(这是其中的模式的实现细节)。
- 监监视由调度正在执行中的任务步骤的状态。它运行周期(频率将系统专用),检查的步骤,通过调度为保持状态。如果检测到任何已超时或失败,它会安排相应的代理来恢复步骤或执行相应的补救措施(这可能涉及修改步骤的状态)。注意,恢复或补救行动通常由调度器和代理执行。主管应该简单地要求这些行动来执行。
调度程序,代理和管理者是逻辑组件和它们的物理实现取决于所使用的技术。例如,若干个逻辑代理可以被实现为一个单一的网络服务的一部分。
调度器维护关于任务的进度和持久的数据存储中的每个步骤中,被称为状态存储的状态信息。主管可以使用此信息来帮助确定一个步骤是否出现故障。图1说明了调度程序的代理,监事和状态存储之间的关系。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-09-21_55ffa43142b16.png)
图1 - 在调度程序代理管理者模式的演员
注意:
此图显示的模式的简化图。在实际的实现中,有可能是调度程序的多个实例同时运行的任务的每个子集。类似地,系统可以运行每个代理程序的多个实例,或者甚至多个监控器。在这种情况下,主管必须协调其与对方认真的工作,以确保它们不会争来恢复同样失败的步骤和任务。在领导人选举模式提供一个可能的解决了这个问题。
何时一个应用程序希望执行一个任务,它提交一个请求给调度程序。调度记录有关的任务及其步骤(例如,“步骤还未开始”)中的状态存储的初始状态的信息,然后开始执行由流程定义的操作。作为调度程序开始每一步时,它将更新关于该步骤中的状态存储(例如,“步运行的”)的状态的信息。
如果一个步骤引用远程服务或资源时,调度程序将消息发送给适当代理。该消息可以包含代理需要传递给服务或访问该资源,除了完整的通过时间的操作的信息。如果代理成功完成其操作时,它返回到调度程序的响应。然后调度程序可以更新在状态存储的状态信息(例如,“步骤完成”),并进行下一步骤。这个过程继续,直到整个任务完成。
代理可以实现任意重试逻辑需要执行它的工作。但是,如果该代理之前,完成未完成其工作期间届满的调度程序就会认为操作失败。在这种情况下,代理应该停止其工作,并没有试图东西返回到调度程序(甚至没有错误消息),或者进行任何形式的恢复。这样做的原因的限制是,后一步骤中已超时或失败,则代理的另一个实例可被调度来运行失败的步骤(该过程将在后面描述)。
如果代理本身出现故障时,调度程序将不会收到回复。该模式可能不会让这一步骤已超时,一个已经失败的真正区别。
如果一个步骤超时或失败时,状态存储将包含一个记录,指出该步骤是否正在运行(“步骤运行”),但完全通过时间已经过去了。监事查找步骤,如本,并试图恢复它们。一个可能的策略是为超级更新完成由值来扩展可用来完成步骤的时间,然后将消息发送到调度器识别已超时的步骤。然后,调度程序可以尝试重复此步骤。然而,这样的设计要求为幂等的任务。
这可能是必需的管理者防止如果连续失败或者超时被重试相同步骤。为了实现这一点,对管理可维持一个重试计数为每一步,随着状态信息,在该状态存储。如果该计数超过预定阈值的管理者可以采用一种策略,例如,通知它应重试步骤中,在期望的故障将在这段时间内可以解决调度之前等待较长的时间。或者,该管理者可以将消息发送到调度请求将整个任务通过实现补偿交易被撤消(该方法将依赖于调度程序和代理提供所必需的信息,以实现对已成功完成各步骤的补偿操作)。
注意:
这不是管理者的目的来监控调度程序和代理商,如果他们不能重新启动它们。系统的这方面应该由其中这些组件所运行的基础设施进行处理。同样,管理者不应该是由调度所执行的任务正在运行(包括如何补偿应这些任务失败)的实际业务运作的知识。这是由调度程序执行的工作流逻辑的目的。监事的责任是确定的步骤是否已失败,并安排要么为它重复或者包含失败的步骤,整个任务被取消。
如果该调度程序是失败的,或者工作流程后,重新启动正在由调度程序进行意外终止,该调度程序应能确定任何飞行任务,这是处理失败时的状态,并准备继续这个任务从该点上失败了。该方法的实现细节都可能是特定的系统。如果任务不能恢复,这可能是必要的,以撤消该任务已经完成的工作。这可能还需要执行一个补偿事务。
这种模式的主要优点是,该系统是弹性的意想不到的临时或不可恢复故障的情况下。该系统可以构造成可自愈。例如,如果一个代理程序或调度程序崩溃时,一个新的可启动的,因而管理可以安排要恢复的任务。如果管理者发生故障,另一实例可以启动,并且可以从发生故障的接管。如果管理者计划定期运行,一个新的实例可以被自动预定义的时间间隔后启动。该状态存储可以被复制以实现更大程度的弹性。
## 问题和注意事项
在决定如何实现这个模式时,您应考虑以下几点:
- 该图案可以是平凡的执行,并且需要的系统的每个可能的故障模式的全面测试。
- 通过调度实现的恢复/重试逻辑可以是复杂的并且依赖于状态存储保持状态信息。它也可能是必要的,记录在一个持久的数据存储区执行一个补偿事务处理所需的信息。
- 与监事运行是非常重要的频率。它应该运行足够频繁,以防止任何失败堵塞长时间的应用程序的步骤,但它不应该运行非常频繁,它成为一个开销。
- 由代理执行的步骤可以被执行一次以上。实现这些步骤的逻辑应该是幂等。
## 何时使用这个模式
使用这种模式时,在分布式环境中运行,例如云的方法必须是有弹性的,以通信故障和/或运行故障。
这种模式可能不适合任务不调用远程服务或访问远程资源。
## 例子
实现一个电子商务系统中的 Web 应用程序已经部署在微软的 Azure。用户可以运行此应用程序来浏览提供一个组织的产品,下订单,这些产品。用户接口运行作为一个网络的作用,并且该应用程序的命令处理元件被实现为一组工作角色。订单处理逻辑的一部分包括访问远程服务,该系统的这一方面可能是易发生的瞬态或更持久的故障。为此,设计师使用了调度程序代理管理者模式实现了系统的订单处理单元。
当客户下订单时,应用程序构建了一个消息,说明的顺序和该职位的消息到队列中。一个单独的提交过程中,工人的角色运行,检索此消息,将订单到订单数据库的详细信息,并为在国家商店的订单流程的记录。请注意,插入到常规数据库和国家存储作为同一操作的一部分执行。提交过程的设计,以确保两个刀片共同完成。
该提交进程创建的订单包括状态信息:
- 订单ID:在订单数据库中的订单的 ID。
- LockedBy:的辅助角色的处理顺序的实例 ID。有可能是运行调度程序的工人角色的多个电流情况下,但每个订单只能通过一个实例来处理。
- CompleteBy:通过该命令应处理的时间。
- ProcessState:任务处理订单的当前状态。可能的状态是:
- Pending:的顺序已被创建,但处理尚未启动。
- Processing:该命令正在处理中。
- Processed:订单已成功处理。
- Error:订单处理失败。
- FailureCount:次数的处理已经尝试了顺序的号码。
在这种状态信息,OrderID 字段从新订单的订单 ID 复制。该 LockedBy 和 CompleteBy 字段设置为 null,则 ProcessState 字段设置为待定,并且 FailureCount 字段设置为 0。
注意:
在这个例子中,为了处理逻辑比较简单,只包括一个调用的远程服务的单个步骤。在一个更复杂的多步骤的情况下,提交过程很可能涉及多个步骤,所以多个记录将在状态存储,每一个描述了一个单独的步骤中的状态被创建。
该调度程序同时作为一个辅助角色的一部分,实现了处理订单的业务逻辑。调度轮询新订单的一个实例探讨了状态存储的记录,其中 LockedBy 字段为空,并在 ProcessState 场待定。何时调度程序发现一个新的订单,立刻填充LockedBy 字段与它自己的实例 ID,设置 CompleteBy 字段到一个适当的时间,并设置 ProcessState 字段来处理。执行此代码的设计是独特和原子,以确保调度程序的两个并发实例不能试图同时处理的顺序相同。
调度程序将运行业务流程处理订单异步,从状态存储传递给它的值在 OrderID 字段。工作流处理的顺序检索来自数据库的订单订单的详细信息,并执行其工作。当订单处理流程的步骤需要调用远程服务,它使用一个代理。在工作流步骤的代理通过利用对作为请求/响应信道 Azure 的服务总线消息队列进行通信。图2示出了该解决方案的一个高层视图。
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2015-09-21_55ffa4315b443.png)
图2 - 使用调度程序代理管理者模式在 Azure 的解决方案来处理订单
从一个工作流步骤发送到代理的消息描述的顺序,并包括 CompleteBy 时间。如果代理接收来自远程服务的响应的 CompleteBy 时间到期之前,构建其上的服务总线队列在其上的工作流是听张贴答复消息。当工作流步骤接收到有效的应答消息,它完成它的处理和调度台的订单状态 ProcessState 场处理。在这一点上,为了处理已成功完成。
如果 CompleteBy 时间到期的代理接收来自远程服务的响应之前,代理简单地停止其处理,并终止处理顺序。类似地,如果工作流处理的顺序超过了 CompleteBy 时,它也将终止。在这两种情况下,在该状态存储在顺序的状态保持给定处理中,但 CompleteBy 时间指示的时间用于处理订单已经过去,该处理判定为不合格。请注意,如果正在访问远程服务,或者正在处理的顺序工作流(或两者)的代理意外终止,在状态存储的信息将再次保持设置为处理,最终将有过期 CompleteBy 值。
如果代理检测到不可恢复的非瞬时性故障当它正在尝试联系远程服务,它可以发送一个错误响应返回到工作流。该调度程序可以设置为错误,并提出了警示操作者的事件的状态。然后,操作者可以尝试手动解决失败的原因,并重新提交失败的处理步骤。
主管定期检查状态存储在寻找订单,过期 CompleteBy 值。如果管理者发现这样的记录,它增加了 FailureCount 领域。如果 FailureCount 值低于规定的阈值时,所述管理者复位 LockedBy 字段为空,更新 CompleteBy 场与新的到期时间,并设置 ProcessState 字段待定。调度程序的一个实例可以拿起这个命令并执行其处理如前。如果 FailureCount 值超过特定阈值时,对故障的原因被假定为非瞬态。监事设置为错误的状态,并引发了警报操作,如前所述的事件。
注意:在这个例子中,管理者是在一个单独的工作任务落实。您可以使用各种策略来安排监理任务运行,包括使用 Azure 的计划程序服务(不要与计划程序组件在此模式相混淆)。关于Azure的计划程序服务的更多信息,请访问调度程序页面。
虽然未在本实施例中所示,该调度程序可能需要保留在通知关于单的进度及状态首位提交的顺序应用。应用程序和调度程序彼此分离,以消除它们之间的任何相关性。该应用程序并不知道哪个调度的实例处理的顺序,调度不知道它的具体应用实例发布的顺序。
为使订单状态予以报道,该应用程序可以使用自己的私人响应队列。这个响应队列的详细信息将被纳入送往提交过程的要求,其中包括在状态存储这些信息的一部分。该调度程序随后将邮件投递到该队列表示订单的状态(“接收到的请求”,“为了完成”,“订单失败”,等等)。它应包括订单ID在这些消息中,以便它们可以与由该应用程序的原始请求相关联。