事件驱动架构模式是一种流行的分布式异步架构模式,用于产生高度可扩展的应用程序。它也有很强的适应性,可以用于小型应用,也可以用于大型的、复杂的应用。事件驱动架构是由高度解耦的、单一用途的事件处理组件组成的,这些组件异步地接收和处理事件。
事件驱动架构模式由两个主要的拓扑结构组成:调解器(Mediator)和经纪人(Broker)。当你需要通过一个中央调解器来协调一个事件中的多个步骤时,通常会使用调解器拓扑,而当你想在不使用中央调解器的情况下将事件串联起来时,则会使用经纪人拓扑。由于这两种拓扑结构的特点和实施策略不同,了解每一种拓扑结构以知道哪一种最适合你的特定情况是很重要的。
调解器拓扑结构
调解器拓扑结构对于有多个步骤并需要某种程度的协调来处理事件的事件是有用的。例如,一个进行股票交易的单一事件可能需要你首先验证交易,然后根据各种合规规则检查该股票交易的合规性,将交易分配给一个经纪人,计算佣金,最后在该经纪人处进行交易。所有这些步骤都需要某种程度的协调,以确定这些步骤的顺序,以及哪些步骤可以连续和平行地进行。
调解器拓扑结构内有四种主要的架构组件:事件队列、事件调解器、事件通道和事件处理器。事件流从客户端向事件队列发送事件开始,队列被用来将事件传送到事件调解器。事件调解器接收初始事件,并通过向事件通道发送额外的异步事件来协调该事件,以执行流程的每个步骤。在事件通道上监听的事件处理程序,从事件调解器接收事件并执行特定的业务逻辑来处理该事件。图 2-1 说明了事件驱动架构模式的一般调解器拓扑结构。
图 2-1. 事件驱动架构的调解器拓扑结构
在一个事件驱动的架构中,有十几个到几百个事件队列是很常见的。该模式没有指定事件队列组件的实现;它可以是一个消息队列,一个 Web 服务端点,或其任何组合。在这个模式中,有两种类型的事件:初始事件和处理事件。初始事件是由调解器接收的原始事件,而处理事件是由调解器产生并由事件处理组件接收的事件。
事件调解器组件负责协调初始事件中包含的步骤。对于初始事件中的每一个步骤,事件调解器发送一个特定的处理事件到一个事件通道,然后由事件处理器接收和处理。值得注意的是,事件调解器实际上并不执行处理初始事件所需的业务逻辑;相反,它知道处理初始事件所需的步骤。
事件信道被事件调解器用来异步传递与初始事件中的每个步骤相关的具体处理事件到事件处理器。事件通道可以是消息队列或消息主题,尽管消息主题最广泛地用于调解器拓扑结构,以便处理事件可以由多个事件处理器处理(每个处理器根据收到的处理事件执行不同任务)。
事件处理器组件包含处理事件所需的应用业务逻辑。事件处理器是自足的、独立的、高度解耦的架构组件,在应用程序或系统中执行特定的任务。虽然事件处理器组件的粒度可以从细粒度(例如,计算订单的销售税)到粗粒度(例如,处理保险索赔)不等,但重要的是要记住,一般来说,每个事件处理器组件应该执行单一的业务任务,而不是依赖其他事件处理器来完成其特定任务。
事件调解器可以用多种方式实现。作为一个架构师,你应该了解这些实施方案中的每一种,以确保你为事件媒介器选择的解决方案符合你的需求和要求。
事件中介最简单和最常见的实现是通过开源集成中心,如 Spring Integration、Apache Camel 或 Mule ESB。这些开源集成中心的事件流通常通过 Java 代码或 DSL(特定领域语言)实现。对于更复杂的调解和协调,你可以使用 BPEL(业务流程执行语言)和 BPEL 引擎,如开源的 Apache ODE。BPEL 是一种标准的类似 XML 的语言,它描述了处理一个初始事件所需的数据和步骤。对于需要更复杂的协调的非常大的应用程序(包括涉及人类互动的步骤),你可以使用业务流程管理器(BPM)来实现事件调解器,如 jBPM。
了解你的需求并将其与正确的事件调解器实施相匹配,对于使用这种拓扑结构的任何事件驱动架构的成功至关重要。使用一个开源的集成中心来做非常复杂的业务流程管理协调是一个失败的秘诀,就像实施一个 BPM 解决方案来执行简单的路由逻辑一样。
为了说明中介拓扑结构是如何工作的,假设你通过一家保险公司投保,并决定搬家。在这种情况下,最初的事件可能被称为搬迁事件之类的。如 图 2-2 所示,处理搬迁事件的步骤包含在事件调解器中。对于每个初始事件的步骤,事件调解器创建一个处理事件(例如,改变地址,重新计算报价,等等),将该处理事件发送到事件通道,并等待处理事件被相应的事件处理器处理(例如,客户进程,报价进程,等等)。这个过程一直持续到初始事件中的所有步骤都被处理。事件调解器中重新计算报价和更新索赔步骤上方的单杠表示这些步骤可以同时运行。
图 2-2. 调解器拓扑结构示例
经纪人拓扑结构
经纪人拓扑结构与调解器拓扑结构不同,因为没有中央事件调解人;相反,消息流通过一个轻量级的消息经纪人(如 ActiveMQ、HornetQ 等),以链状方式分布在事件处理器组件上。当你有一个相对简单的事件处理流程,并且你不希望(或需要)中央事件协调时,这种拓扑结构是有用的。
在经纪人拓扑结构中,有两种主要类型的架构组件:经纪人组件和事件处理器组件。经纪人组件可以是集中式的,也可以是联合式的,包含所有在事件流中使用的事件通道。 包含在经纪人组件中的事件通道可以是消息队列、消息主题或两者的组合。
这种拓扑结构如 图 2-3 所示。从图中可以看出,没有中央事件中介组件控制和协调初始事件;相反,每个事件处理器组件负责处理一个事件,并发布一个新的事件,表明它刚刚执行的行动。例如,一个平衡股票组合的事件处理器可能会收到一个名为股票分割的初始事件。基于这个初始事件,事件处理器可以做一些投资组合的重新平衡,然后向经纪人发布一个新的事件,称为重新平衡投资组合,然后由不同的事件处理器接收。请注意,有时一个事件被一个事件处理器发布,但没有被任何其他事件处理器接收。当你正在发展一个应用程序或提供未来的功能和扩展时,这很常见。
图 2-3. 事件驱动架构的经纪人拓扑结构
为了说明经纪人拓扑结构是如何工作的,我们将使用与调解器拓扑结构中相同的例子(一个被保险人搬家)。由于在经纪人拓扑中没有中央事件调解器来接收初始事件,客户处理组件直接接收事件,改变客户地址,并发出一个事件说它改变了客户的地址(例如,改变地址事件)。在这个例子中,有两个事件处理器对更改地址事件感兴趣:报价流程和索赔流程。报价处理组件根据地址的变化重新计算新的自动保险费率,并向系统的其他部分发布一个事件,说明它所做的事情(例如,重新计算报价事件)。另一方面,索赔处理组件收到相同的地址变更事件,但在这种情况下,它更新一个未完成的保险索赔,并向系统发布一个事件作为更新索赔事件。然后,这些新的事件被其他事件处理组件接收,事件链在系统中继续进行,直到没有更多的事件被发布给那个特定的发起事件。
图 2-4. 经纪人拓扑结构示例
正如你从 图 2-4 中看到的,经纪人拓扑结构是关于执行业务功能的事件链的全部。理解经纪人拓扑结构的最好方法是把它想成一场接力赛。在接力赛中,选手手持接力棒,跑完一定距离后,将接力棒交给下一个选手,如此循环往复,直到最后一个选手冲过终点线。在接力赛中,一旦跑者交出接力棒,她就完成了比赛。经纪人拓扑结构也是如此:一旦一个事件处理器交出事件,它就不再参与该特殊事件的处理。
一些思考
事件驱动架构模式是一个相对复杂的实现模式,主要是由于它的异步分布性质。在实现这种模式时,你必须解决各种分布式架构的问题,如远程进程的可用性、缺乏响应性,以及在经纪人或调解人失败时的经纪人重连逻辑。
在选择这种架构模式时,需要考虑的一个问题是单一业务流程缺乏原子交易。因为事件处理器组件是高度解耦和分布式的,在它们之间保持一个事务性的工作单元是非常困难的。出于这个原因,在使用这种模式设计你的应用程序时,你必须不断地思考哪些事件可以独立运行,哪些不可以,并相应地规划你的事件处理器的颗粒度。如果你发现你需要在事件处理器之间分割一个工作单元 —— 也就是说,如果你使用单独的处理器来处理应该是一个不分割的事务 —— 这可能不是适合你的应用程序的模式。
也许事件驱动架构模式最困难的方面之一是事件处理器组件合同的创建、维护和管理。每个事件通常都有一个与之相关的特殊合同(例如,传递给事件处理器的数据值和数据格式)。在使用这种模式时,最重要的是确定一个标准的数据格式(如 XML、JSON、Java Object 等),并从一开始就建立一个合同的版本策略。
模式分析
下表包含了对事件驱动架构模式的常见架构特征的评级和分析。每个特征的评级是基于该特征作为一种能力的自然趋势,基于该模式的典型实现,以及该模式通常被称为什么。关于该模式与本报告中其他模式的关系的侧面比较,请参考本报告末尾的 附录 A。
整体敏捷性评级:高
整体敏捷性是指对不断变化的环境做出快速反应的能力。由于事件处理器组件用途单一,与其他事件处理器组件完全脱钩,变化通常与一个或几个事件处理器有关,可以快速进行而不影响其他组件。
易于部署的评级:高
总的来说,由于事件处理器组件的解耦性质,这种模式相对容易部署。经纪人拓扑结构往往比调解器拓扑结构更容易部署,主要是因为事件调解器组件与事件处理器有一定程度的紧密耦合:事件处理器组件的变化可能也需要事件调解器的变化,这就要求在任何给定的变化中都要部署两者。
可测试性等级:低
虽然单个单元测试不是太难,但它确实需要某种专门的测试客户端或测试工具来生成事件。由于这种模式的异步性,测试也很复杂。
性能评级:高
虽然由于涉及到所有的消息传递基础设施,当然有可能实现一个性能不佳的事件驱动架构,但一般来说,该模式通过其异步能力实现了高性能;换句话说,执行解耦、并行的异步操作的能力超过了排队和取消排队消息的成本。
可扩展性等级:高
在这种模式中,通过高度独立和解耦的事件处理器,自然而然地实现了可扩展性。每个事件处理器都可以单独扩展,从而实现细粒度的可扩展性。
易于开发的评级:低
由于该模式的异步性,以及合约的创建和需要在代码中对无响应的事件处理器和失败的经纪商进行更高级的错误处理,开发可能会有些复杂。
