TaskFlow/范式转变
修订日期: 2014年3月25日,Harlowja
⚠ 警告 ⚠
这些范式转变的影响可能会伴随你一生(已警告过你)。
逐步构建
脑洞大开:原子、任务、流程,这些是什么???
在taskflow中,你的代码结构与典型的程序员习惯的方式不同(函数,或者面向对象+对象)。在taskflow中,为了拥有易于内省和易于从(以及以自动化方式回滚)恢复的工作流,taskflow引入了它最小的单元,即原子。原子在许多方面类似于抽象接口,原子指定其期望的输入数据/需求和输出/提供的价值,并赋予其名称。taskflow中的任务是具有execute()/revert()方法的原子,这些方法使用先前的需求来产生一些输出/提供的价值,并且是原子的一个关键派生类(很快会有更多)。任务和函数的主要区别在于,任务显式声明其输入并显式声明其输出(因为它继承自原子基类),并且任务具有与其关联的标识名称以及潜在的关联方式来回滚任务所做的事情(如果该任务产生副作用)。为了将这些最小单元组织成有用的东西,创建了流程的概念,它与你的任务集为实现目标而经历的预期执行流程相似。由于上述任务声明了其输入和输出,因此也可以推断出排序(尽管不必推断),这使得让一组小任务完成更大的目标变得更加简单。
注意:有关内置于taskflow中的任务和流程结构的更多详细信息,请参阅结构概述页面。
弹性
脑洞大开:在使用流程和任务并启用持久性时,是否可以使用taskflow从这些流程和任务的部分完成状态恢复?
是的!(taskflow的关键概念/目标之一是将此功能带给尽可能多的OpenStack项目)。为所有人提供弹性!
异常
脑洞大开:我的异常逻辑是否发生了变化,如果任务抛出异常意味着什么,谁来捕获它,会发生什么???
发生在任务中的异常,并且未被任务内部捕获的异常,默认情况下当前会触发任务所在整个工作流的回滚(引擎负责处理此回滚过程,因为它也负责处理快乐路径)。如果工作流中的多个任务引发异常(例如,它们通过进程/线程或分布式引擎同时执行),则导致该任务的各个路径将被回滚(如果多个失败任务共享一个祖先任务,则该任务将仅回滚一次)。
注意:在未来,回滚策略应该能够使这更加可定制(允许更多方式来处理或更改回滚过程,以便你可以更好地决定如何处理未处理的异常)。
执行流程
脑洞大开:我的所有任务都属于引擎???
当一组任务和包含这些任务的关联结构(也称为创建该结构的流程)被提供给引擎,以及一个可能的(但不是必需的)后端,引擎可以在其中存储中间结果(如果工作流应该能够在失败时恢复,则需要此后端)时,引擎成为负责可靠执行你提供的流程中包含的任务的执行单元。该引擎将确保执行时保留提供的结构。例如,通过使用线性_flow结构对任务进行线性排序将始终以线性顺序运行。以依赖顺序构建的任务集将始终以该依赖顺序运行。这些约束引擎必须遵守;请注意,引擎类型可能会强制执行其他约束(例如,单线程引擎仅在单个线程中运行,基于分布式或工作者的引擎可能在远程运行)。因此,在选择要使用的引擎时,请确保仔细选择适合你的应用程序所需的特性集。
嵌套
脑洞大开:没有函数(现在是任务),我们如何对需要组合/嵌套的动作进行建模和执行(请不要无限递归)???
首先,让我描述一下为什么这很难,因为它可能不太明显。在传统的结构和执行风格中(没有结构化的工作流),函数Y可能会调用另一个函数Z,并将Z所做的事情视为黑盒。这种结构和执行风格本身并不容易导致可以由另一方(taskflow中的引擎)执行的结构,它也不容易(没有语言级别的特性/添加)允许以任何方式从函数Z恢复,如果程序在调用函数Z时崩溃(依此类推,如果Z调用另一个函数,则会发生相同的问题)。这并非意味着精心设计的软件无法做到这一点,只是意味着它们可能会构建一些类似于taskflow的东西来解决这个问题。为了避免这个问题,并实现taskflow创建的特性(恢复、执行控制),我们需要将这种模型翻转过来(或者至少旋转90度)。
taskflow引入的思维转变,以绕过黑盒问题(Y调用Z,Z调用更多函数等等),是将正常的Y->Z结构更改为一组依赖项和任务输入/输出,结果在这些任务之间传递(类似于消息传递)。这个简单的模型然后允许taskflow(及其引擎概念)能够从给定的点重新启动,从最后一个完成的任务恢复。请注意,这仍然使得嵌套任务变得困难。为了解决这个限制,taskflow提供了一种嵌套任务和流程的方式。例如,线性_flow Y'可以包含任务[A, B, C],然后另一个线性_flow Z'可以包含[D, E, Y', F]。这意味着列出的F任务可以依赖于Y'(以及D, E)产生的所有内容,然后F才会开始执行(Y'变得像一个产生一些输出的黑盒,类似于上面的函数Z)。这种组合不会限制taskflow的恢复,因为taskflow内部知道构成子流程(如Y')的内容,并且可以在需要时从该嵌套流程的任务恢复。
注意:python未来版本中的协程pep-3156具有类似的任务模型(不完全相同,但相似)。协程的问题是它们仍然不能提供恢复、回滚或以与实际要执行的工作流密切相关的结构化代码的能力。然而,它们创建了一个可以构建的基础架构,以帮助使这更容易实现。预计taskflow的抽象应该相对容易地映射到python 3.4,预计python 3.4将具有pep-3156的版本,一旦python 3.4成熟。
控制流
脑洞大开:我的复杂控制流在哪里去了???
这是一种程序员通常编程执行控制流的略微不同的方式。为了能够跟踪工作流的执行,期望的工作流必须预先分成小块(类似于函数),而不能在运行时以很大的方式更改该执行顺序。使用这种相对静态结构的任务流引擎然后可以以一种定义明确且可恢复的方式运行你的结构(这种相对静态集合已被证明是足够好的,通过论文和研究,例如tropic)。
然而,这目前有一些副作用,即某些传统操作(if-then-else、do-while、fork-join、switch...)变得更加复杂,因为这些类型的控制流不容易映射到可以轻松恢复或以潜在分布式方式运行的表示形式(因为它们在执行时会改变控制流,或者创建任务之间复杂且难以建模的依赖关系)。为了使taskflow保持相对最小(和简单),我们试图将允许的集合减少到更易于管理且当前更小的集合(做好简单的事情,并在需要时添加复杂性)。如果这些控制流变得有价值,我们将重新审视是否以及如何使它们可供taskflow用户使用。
注意:在任务内部,该任务的execute()方法可以使用它所支持的任何现有的控制流(python支持的任何控制流),但在execute()之外,控制流运算符集更小(由于上述原因/限制)。另一种实现方法是与创建你的工作流(在日志簿创建时持久化的方法位置)关联的factory函数执行大部分复杂的控制流(同时构建所需的任务)。有关更多信息,请参阅流程工厂参考。
工作流所有权转移
脑洞大开:我读到taskflow支持一种自动将工作流转移到可以完成该工作的worker,以及自动恢复部分完成的工作的能力。这是可能的吗?是吗?
这是可能的,并且期望这是一种典型的用法模式。Taskflow的jobboard概念充当发布工作供选定的worker完成的位置;这类似于物理/虚拟招聘网站所做的事情。在taskflow使用中将工作发布到jobboard允许任何类型的worker监视该jobboard以获取新工作出现;这类似于消息传递系统,但具有通知新消息/工作出现的时间。这使worker能够意识到新工作。这是难题的一部分,第二部分是能够以原子方式声明该工作(换句话说,工作将被分配或接收并被worker接受,以便在某个结束日期完成)。这正是与消息传递系统的不同之处(因为消息传递系统没有原子所有权能力),但像zookeeper或etcd这样的系统使用它们raft/paxos/zab算法提供这些能力(它们还提供足够的发布/通知能力来提供上述消息传递功能)。因此,worker接受该工作并开始完成该工作。这一切都很好,但通常在大型分布式系统中,一定比例的worker会死亡/失败/崩溃(或其他),请求完成该工作的实体不应该知道发生了这种情况(为什么他们关心)。相反,该实体通常希望工作由另一个等效worker恢复,这就是etcd/zookeeper/...的能力,以原子方式释放所有权,从而允许另一个worker尝试恢复并完成先前失败的worker部分(或完全完成但未提交)的工作。
注意:这与具有持久性和可从某个状态转换恢复的状态(以便worker可以从上一个状态转换中恢复并尝试在请求的工作上取得进一步进展,而无需使请求该工作的实体意识到任何所有权转移)相关联。太棒了,对吧?