文章目录
- 分布式计算宣言
- 背景
- 关键概念
- 基于服务的模型
- 基于工作流的模型和数据域
- 应用概念
- 跟踪状态变化
- 对进行中的工作流程元素进行更改
- 工作流程和 DC 客户订单处理
分布式计算宣言
创建时间: 1998 年 5 月 24 日
修订日期: 1998 年 7 月 10 日
背景
很明显,如果亚马逊的处理能力要扩展到可以支持我们当前订单量十倍的程度,我们就需要创建和实施一个新的架构。问题是,新架构应该采取什么形式,我们如何实现它?
我们当前的两层客户端-服务器架构本质上是一种数据绑定架构。运行业务的应用程序直接访问数据库并了解其中嵌入的数据模型。这意味着应用程序和数据模型之间存在非常紧密的耦合,即使功能保持不变,数据模型更改也必须伴随应用程序更改。由于应用程序对数据元素之间的相互依赖关系很敏感,因此这种方法无法很好地扩展,并且很难根据数据所在的位置来分配和隔离处理。
关键概念
我们提出的新架构中有两个关键概念可以解决当前系统的缺点。第一个是转向基于服务的模型,第二个是改变我们的处理方式,使其更接近于工作流方法的模型。本文没有说明应该使用什么具体技术来实现新架构。只有当我们确定新架构能够满足我们的要求并且我们着手实施它时,才应该确定这一点。
基于服务的模型
我们建议转向三层架构,其中表示(客户端)、业务逻辑和数据是分离的。这也被称为基于服务的架构。应用程序(客户端)将不再能够直接访问数据库,而只能通过一个定义良好的接口来访问,该接口封装了执行该功能所需的业务逻辑。这意味着客户端不再依赖底层数据结构甚至数据所在的位置。业务逻辑(在服务中)和数据库之间的接口可以改变而不影响客户端,因为客户端通过它自己的接口与服务交互。同样,客户端接口可以在不影响服务与底层数据库交互的情况下发展。
结合工作流的服务必须提供同步和异步方法。同步方法可能适用于立即响应的操作,例如添加客户或查找供应商信息。但是,其他本质上异步的操作不会提供即时响应。这方面的一个示例是调用服务以将工作流元素传递到链中的下一个处理节点。请求者不希望立即返回结果,只是指示工作流元素已成功排队。但是,请求者可能有兴趣最终收到请求的结果. 为此,服务必须提供一种机制,请求者可以借此接收异步请求的结果。有几个模型,轮询或回调。在回调模型中,请求者传递例程的地址以在请求完成时调用。当请求和回复之间的时间相对较短时,最常使用这种方法。回调方法的一个显着缺点是,当请求完成使回调地址无效时,请求者可能不再处于活动状态。然而,轮询模型会受到定期检查请求是否已完成所需的开销的影响。轮询模型可能是与异步服务交互最有用的模型。
在我们转向基于服务的模型时,必须考虑几个重要的影响。
首先是我们将不得不采用更加规范的软件工程方法。目前,我们的大部分数据库访问都是临时的,大量的 Perl 脚本在很大程度上运行着我们的业务。转向基于服务的架构将需要在一段时间内逐步取消客户端对数据库的直接访问。否则,我们甚至无法希望在不对客户产生负面影响的情况下实现三层架构的优势,例如数据位置透明性和演进数据模型的能力。服务及其接口的规范、设计和开发不应随意发生。它必须仔细协调,这样我们就不会以目前同样混乱的扩散而告终。
与第一个相关的基于服务的方法的第二个含义是所有软件开发人员都需要进行重大的思维转变。我们当前的思维方式是以数据为中心,当我们为业务需求建模时,我们使用以数据为中心的方法。我们的解决方案涉及更改数据库表或列以实施解决方案,并且我们将数据模型嵌入到访问应用程序中。基于服务的方法将要求我们将业务需求的解决方案至少分成两部分。第一部分是数据元素之间关系的建模,就像我们一直做的那样。这包括将在与数据交互的服务中强制执行的数据模型和业务规则。然而,第二件是我们以前从未做过的事情,它正在设计客户端和服务之间的接口,以便客户端不会暴露或依赖底层数据模型。这与上面讨论的软件工程问题密切相关。
基于工作流的模型和数据域
亚马逊的业务非常适合基于工作流的处理模型。我们已经有了一个“订单流水线”,从客户下订单到发货,各种业务流程都在处理这条流水线。我们的大部分处理已经是面向工作流的,尽管工作流“元素”是静态的,主要驻留在单个数据库中。我们当前工作流模型的一个示例是 customer_orders 在系统中的进展。每个 customer_order 的条件属性指示工作流中的下一个活动。但是,当前的数据库工作流模型无法很好地扩展,因为处理是针对中央实例执行的。随着工作量的增加(每单位时间的订单数量增加),针对中央实例的处理量将增加到不再可持续的程度。一个解决方案是分布工作流处理,以便它可以从中央实例卸载。实现这一点需要像 customer_orders 这样的工作流元素在可能位于不同机器上的业务处理(“节点”)之间移动。数据将传输到流程,而不是流程来获取数据。这意味着每个工作流元素都需要工作流中的下一个节点对其采取行动所需的所有信息。这个概念与面向消息的中间件中使用的概念相同,其中工作单元表示为从一个节点(业务流程)分流到另一个节点的消息。
工作流的一个问题是它是如何被引导的。每个处理节点是否具有根据嵌入式业务规则(自主)将工作流元素重定向到下一个节点的自主权,或者是否应该有某种工作流协调器来处理节点之间的工作转移(定向)?为了说明差异,考虑一个执行信用卡收费的节点。它是否具有内置的“智能”,可以将成功的订单提交给订单管道中的下一个处理节点,并将失败的订单分流到其他节点进行异常处理?或者信用卡收费节点是否被认为是一种可以从任何地方调用并将其结果返回给请求者的服务?在这种情况下,请求者将负责处理失败情况,并确定处理中的下一个节点是成功请求还是失败请求。定向工作流模型的一个主要优点是它的灵活性。它在其间移动的工作流处理节点是可互换的构建块,可用于不同的组合并用于不同的目的。某些处理非常适合定向模型,例如信用卡收费处理,因为它可能在不同的上下文中调用。在更大的范围内,被视为单个逻辑过程的 DC 处理受益于定向模型。DC 将接受客户订单来处理并将结果(装运、异常情况等)返回给任何赋予其执行工作的任务。另一方面,如果某些进程与相邻进程的交互是固定的并且不太可能改变,则它们将从自治模型中受益。这方面的一个例子是,多本书发货总是从拣货清单到重新装箱。
分布式工作流方法有几个优点。其中之一是可以轻松地对业务流程(例如履行订单)进行建模以提高可扩展性。例如,如果信用卡收费成为瓶颈,可以在不影响工作流模型的情况下添加额外的收费节点。另一个优点是工作流路径上的节点不必依赖于访问远程数据库来对工作流元素进行操作。这意味着当工作流系统的其他部分(如数据库)不可用时,某些处理可以继续,从而提高系统的整体可用性。
然而,基于消息的分布式工作流模型有一些缺点。以数据库为中心的模型,其中每个进程都访问同一个中央数据存储,允许数据更改在系统中快速有效地传播。例如,如果客户想要更改用于他的订单的信用卡号码,因为他最初指定的信用卡号码已经过期或被拒绝,这可以很容易地完成,并且该更改将立即在系统的任何地方显示出来。在基于消息的工作流模型中,这变得更加复杂。工作流的设计必须适应这样一个事实,即当工作流元素从系统的一端移动到另一端时,一些基础数据可能会发生变化。此外,使用经典的基于队列的工作流,更难确定任何特定工作流元素的状态。为了克服这个问题,必须创建允许记录状态转换的机制,以便于外部流程,而不会影响工作流流程的可用性和自主性。这些问题使得正确的初始设计比在单体系统中重要得多,并且回到其他地方讨论的软件工程实践。
工作流模型适用于我们系统中的瞬态数据,并且会经历明确定义的状态变化。然而,还有另一类数据不适合工作流方法。此类数据在很大程度上是持久的,并且不会像工作流数据那样以相同的频率或可预测性发生变化。在我们的例子中,这些数据描述了客户、供应商和我们的目录。重要的是,这些数据必须高度可用,并且我们维护这些数据之间的关系(例如知道哪些地址与客户相关联)。创建数据域的想法使我们能够根据此类数据与其他数据的关系来拆分此类数据。例如,与客户有关的所有数据将构成一个域,所有关于供应商的数据将构成一个域,所有关于我们目录的数据将构成一个域。这使我们能够创建客户与各种数据域交互的服务,并开辟了复制域数据的可能性,使其更接近其消费者。这方面的一个例子是将客户数据域复制到英国和德国,以便客户服务组织可以在本地数据存储之外运行,而不依赖于单个数据实例的可用性。数据的服务接口将是相同的,但它们访问的域的副本将不同。创建数据域和访问它们的服务接口是将客户端与数据的内部结构和位置知识分开的重要元素。
应用概念
DC 处理很适合作为上面讨论的工作流和数据域概念的应用示例。通过 DC 的数据流分为三个不同的类别。第一个是非常适合顺序队列处理的。这方面的一个例子是 vreceive 填充的 received_items 队列。第二类是由于其持久性或广泛可用的要求而应驻留在数据域中的数据。库存信息 (bin_items) 属于此类,因为 DC 和其他业务功能(如采购和客户支持)都需要它。第三类数据既不适合队列模型也不适合域模型。此类数据是暂时的,仅在本地(DC 内)需要。它不太适合顺序队列处理,但是,因为它是聚合操作的。这方面的一个例子是生成选择列表所需的数据。必须积累一批客户发货,以便提货单有足够的信息来根据发货方式等打印出提货。完成提货单处理后,发货将继续其工作流程中的下一站。这第三类数据的保存区域称为 聚合队列,因为它们展示了队列和数据库表的属性。
跟踪状态变化
外部进程能够通过系统跟踪工作流元素的移动和状态变化的能力是必不可少的。在 DC 处理的情况下,客户服务和其他功能需要能够确定客户订单或货物在管道中的位置。我们建议使用的机制是,工作流中的某些节点将一行插入到某个集中式数据库实例中,以指示正在处理的工作流元素的当前状态。此类信息不仅可用于跟踪工作流程中的某个位置,而且还可以提供重要的洞察力,让我们了解订单管道中的运作和效率低下情况。状态信息只会在客户订单处于活动状态时保存在生产数据库中。一旦实现,
对进行中的工作流程元素进行更改
工作流处理会产生数据流通问题,因为工作流元素包含移动到下一个工作流节点所需的所有信息。如果客户想在处理订单时更改订单的送货地址怎么办?目前,CS 代表可以更改 customer_order 中的送货地址(前提是在创建 pending_customer_shipment 之前),因为订单和客户数据都位于中心位置。然而,在工作流模型中,客户订单将在其他地方通过各个阶段进行处理,直至成为交付给客户的货物。要影响对正在进行的工作流元素的更改,必须有一种传播属性更改的机制。发布和订阅模型是执行此操作的一种方法。为了实施 P&S 模型,工作流处理节点将订阅以接收特定事件或异常的通知。属性变化将构成一类事件。要更改飞行中订单的地址,将向订阅该特定事件的所有处理节点发送一条消息,指示订单和已更改的属性。此外,状态更改行将被插入到跟踪表中,指示请求了属性更改。如果其中一个节点能够影响属性更改,它将在状态更改表中插入另一行以指示它已经对订单进行了更改。这种机制意味着将永久记录属性更改事件以及它们是否被应用。属性变化将构成一类事件。要更改飞行中订单的地址,将向订阅该特定事件的所有处理节点发送一条消息,指示订单和已更改的属性。此外,状态更改行将被插入到跟踪表中,指示请求了属性更改。如果其中一个节点能够影响属性更改,它将在状态更改表中插入另一行以指示它已经对订单进行了更改。这种机制意味着将永久记录属性更改事件以及它们是否被应用。属性变化将构成一类事件。要更改飞行中订单的地址,将向订阅该特定事件的所有处理节点发送一条消息,指示订单和已更改的属性。此外,状态更改行将被插入到跟踪表中,指示请求了属性更改。如果其中一个节点能够影响属性更改,它将在状态更改表中插入另一行以指示它已经对订单进行了更改。这种机制意味着将永久记录属性更改事件以及它们是否被应用。此外,状态更改行将被插入到跟踪表中,指示请求了属性更改。如果其中一个节点能够影响属性更改,它将在状态更改表中插入另一行以指示它已经对订单进行了更改。这种机制意味着将永久记录属性更改事件以及它们是否被应用。此外,状态更改行将被插入到跟踪表中,指示请求了属性更改。如果其中一个节点能够影响属性更改,它将在状态更改表中插入另一行以指示它已经对订单进行了更改。这种机制意味着将永久记录属性更改事件以及它们是否被应用。
P&S 模型的另一种变体是工作流协调器而不是工作流处理节点影响对进行中的工作流元素而不是工作流处理节点的更改。与上述机制一样,工作流协调器将订阅以接收事件或异常的通知,并在处理它们时将它们应用于适用的工作流元素。
将更改同步应用到运行中的工作流元素是更改请求的异步传播的替代方法。这有利于向变更请求的发起者提供有关变更是否受到影响的即时反馈。但是,此模型要求工作流中的所有节点都可用于同步处理更改,并且应该仅用于可以接受由于暂时不可用而导致请求失败的更改。
工作流程和 DC 客户订单处理
下图展示了客户订单如何通过 DC 中的各个工作流程阶段的简化视图。这主要是根据事物当前的工作方式进行建模,并进行一些更改以表示由于 DC 隔离,事物将如何工作。在此图中,客户订单或客户发货不是保留在静态数据库表中,而是在菱形框表示的工作流处理节点之间物理移动。从图中,您可以看到 DC 处理使用数据域(用于客户和库存信息)、真实队列(用于接收的物品和分销商发货)以及聚合队列(用于收费处理、拣货等)。每个队列公开一个服务接口,请求者可以通过该接口插入一个工作流元素,由队列处理’ 各自的工作流处理节点。例如,准备好收费的订单将被插入到收费服务的队列中。收费处理(可能是多个物理过程)将从队列中删除订单以进行处理,并在完成后将它们转发到下一个工作流节点(或返回到收费服务的请求者,具体取决于使用的是协调工作流还是自主工作流收费服务)。