事件驱动架构模式是一个非常流行的异步分布模式,可生成高可扩展性应用。而且它也具有强适应能力,可被用于小程序或者大型复杂程序。事件驱动架构是由高耦合度、单一目的的事件处理模块构成,这些模块异步接收、处理事件。
事件驱动架构模式有两种主要拓扑结构,“调度员”(mediator)和“经纪人”(broker)拓扑结构。“调度员”拓扑结构通常用在一个事件中由多个步骤组成,而你需要通过中央“调度员”模块去调度这些步骤。然而“经纪人”结构是当需要执行一系列事件链,而不需要中央“调度员”模块。由于这两种结构的特征和执行策略不同,深入理解两者的用法能帮助你在自己的案例中做出正确的判断。
调度员拓扑(Mediator Topology)
如果应用中的事件由多个步骤组成,并且要求一定程度的调度去执行事件,对于这样的情况,“调度员”拓扑很有帮助。例如,一个处理股票交易的单独事件可能需要以下步骤,首先验证交易,然后检查股票交易是否符合不同的监管条例,指派交易给一个经纪人,计算佣金,最后把交易放置给指定的经纪人。所有这些步骤要求有一定的事件调度机制去决定步骤的顺序以及哪些步骤要顺序或者并行执行。
结构上,该拓扑结构由4个主要部分组成:事件队列,事件调度员(event mediator),事件通道,事件处理器。事件流程按照:客户发送初始事件到事件队列,事件队列传递初始事件给事件调度员,事件调度员在收到初始事件后,把初始事件转化为多个小事件,然后通过异步发送这些小事件给事件通道,实现调度。每个小事件都是执行业务的一个步骤,其对应的每个事件处理器监听事件通道,当收到事件后,执行指定的业务逻辑来处理业务。图2-1描述了大体的调度员拓扑结构模式。
在一个事件触发结构,通常会有一打到几百的事件队列。这种拓扑模式没有指明事件队列模块具体实现方式,它可以是消息队列,网络服务终端或者任何组合。
在这种拓扑结构中,它有两种事件:初始事件和处理中的事件。初始事件是原始事件调度员收到的事件,而处理中的事件是由调度员产生的,由事件处理模块监听接收的事件。
事件调度员模块负责调度安排所有包含在一个初始事件的子步骤。对于每个包含的步骤,调度员会发出指定的处理事件给事件通道。事件通道收到后,由事件处理模块进行处理。重要的一点是,事件调度员实际不进行任何业务逻辑的处理,但它清楚所有处理的步骤。
事件调度员利用事件通道去异步传输特定的处理事件。事件通道可以是消息队列或者消息主题(message topics),尽管消息主题更广泛使用在调度拓扑结构,以至于处理事件过程可以被多个事件处理模块处理(每个模块处理不同的工作,根据接收的处理事件)。
事件处理模块包含应用的业务逻辑去处理事件。事件处理器是闭环的、独立的、高耦合度的模块,在应用和系统中执行指定的任务。然而事件处理模块的细致程度(granularity)既可以设计成细粒度(如计算订单销售税)或者粗粒度(如处理保险索赔)。总体而言,从设计上来说,事件处理模块需要处理一个单一业务任务,不能依赖于其他模块。
事件调度员可以以多种方式实现。作为架构师,你需要理解不同的实现选项,从而找出最符合你的需要和要求的一种。
事件调度员最简单和最普遍的实现方式是通过开源集成中心,比如Spring Integration, Apache Camel, 或者 Mule ESB。这些开源集成中心的事件流通常由JAVA或者DSL执行。对于更负责的调度和编排,可以使用BPEL(业务处理执行语言),并结合BPEL引擎,例如开源Apache ODE。BPEL是一个标准的类XML语言,它描述了处理初始事件的数据和步骤。对于大的要求更多复杂调度的应用,你可以使用BPM例如jBPM去实现事件调度员。
理解你的需求和找到匹配的事件调度模块实现方式是很重要的。利用开源集成环境中心去做复杂的业务处理管理是典型的失败范例,就如使用BPM方案去执行简单的业务逻辑。
举例描述调度员拓扑结构如何工作,假设你通过保险公司投保同时你决定搬家。在这个案例中,初始事件可以被称为重定位事件(relocation event)。处理定位事件中涉及到每个事件调度员使用的处理事件,如图2-2所示。对于每个初始事件步骤,事件调度员创建一个处理事件(如改变地址,重新计算报价等),发送处理事件给事件通道,并等待相应的事件处理模块处理完事件(如用户处理,报价处理)。处理工作持续到整个包含的处理事件执行完毕。图中,在重计算报价和更新声明两个步骤中的单一进度条说明,这些步骤是可以同时进行的。
经纪人拓扑结构(Broker Topology)
经纪人拓扑结构和调度员拓扑结构不同点在于,没有中央事件调度员,而是:消息执行流程沿着类似链条连接的各个事件处理模块,通过轻量级的消息经纪人(比如AcitveMQ, HornetQ)实现。这样的拓扑结构,在你只需要处理一个相对简单的事件处理流程,同时不想要中央事件调度员时候,特别适用。
在经纪人拓扑结构中,有两种主要的模块:经纪人模块和事件处理模块。经纪人模块可以是集中的,包含所有的用于事件处理流程的事件通道(event channel),该事件通道可以做成消息队列或者消息主题(message topics),或者两种的结合。
该拓扑结构如图2-3所示。从图中,你可看到,它没有中央事件调度员控制和编排初始事件。每个事件处理模块负责响应一个事件并会发布出新的小事件(该小事件就是它执行过的事件)。例如,一个负责平衡股票投资组合的事件处理模块可能会收到一个股票分离的初始事件消息。基于这个初始事件,该事件处理模块会做一些平衡股票投资组合的操作,然后发布一个新的称为重新平衡投资组合的事件消息给经纪人,这个事件消息又会被其他事件处理模块所执行。注意也会有新的事件消息被发布出来,却没有事件处理模块处理的情况。这在当扩展你的应用和为开发未来的功能的时候常见。
我们用在调度员拓扑结构的同样的例子来说明经纪人拓扑结构如何工作。由于没有中央事件调度员去接收初始事件,因此客户处理模块直接接收事件,改变客户地址,发送一个事件说明它改变了客户地址。在这个例子里,有两个事件处理模块对改变地址这一事件感兴趣的:报价处理模块和声明处理模块。报价处理模块基于改变的地址重计算新的保险汇率,并且对剩下的系统发布事件告知它做了什么。另一方面,声明处理模块接收到地址更改的消息,更新保险声明并发布声明更新的事件消息。这些新事件又会被其他的事件处理模块接收,事件链会继续贯穿整个系统直到没有更多得事件被发布位置。
从图2-4,你可以看出,经纪人拓扑结构是关于执行一个业务功能的事件链。可以想象它是一个接力比赛。在接力比赛中,运动员拿起跑步棒,跑过一段路程,然后就递交给下一名运动员,按照这样的流程,一直到最后一名运动员跑完整个路程。经纪人拓扑结构即是这样的。
考量
事件驱动结构模式实施起来是相对复杂的,这主要归于它异步分布的特征。当实施这个模式的时候,你必须解决不同的分布架构的问题,比如远程进程的可能性,回复的缺失性,问题发生时经纪人重新联结的逻辑。
当选择此结构模式需要考虑的另一个是问题是缺少原子量级上的执行操作。因为事件处理模块是高耦合度和分布的,所以维持一个单一的执行交易单元很难。因此,当用这个模型设计你的系统,你需要持续地思考哪些事件可以或者不可以独立运行,以此计划你的事件处理模块的粒子度。如果你发现你需要分散一个作业的单元到不同的事件处理模块—即,如果你需要把一个不可分割的交易分散到独立的处理模块—这说明你选用的是不对的模式。
也许一个最难的方面是创建、维持、管理事件处理模块的协议。每个事件通常有与它联系的独立协议(比如传递的数据和数据格式)。当使用这个模式,解决标准数据格式(XML, JSON, java实例等),并从一开始建立协议版本管控政策是很重要的。
模式分析
下面的表格包含各项平常架构特征的评分和分析。
整体敏捷度
评分:高
分析:整体敏捷度是反映是否能快速适应变化环境的能力。由于事件处理模块是单一用途并完全的和其他事件处理模块接触耦合,改变通常只在于一个或几个事件处理模块,并可以被快速执行而不影响其他模块。
部署容易性
评分:高
分析:总的来说,这个模式是相对容易部署,这由于事件处理模块间解耦的关系。经纪人结构比起调度员结构更易于部署,主要由于事件调度员模块和具体执行事件的处理模块是耦合的,所以事件处理模块上出现的改动,可能要求事件调度员模块上也有相应的改动,这需要改动双方都需要重新部署。
测试容易性
评分:低
分析:虽然独立事件处理模块的单元测试不艰难,但这需要一些专门的测试客户端或测试工具去产生事件。同时异步性也增加测试难度。
表现
评分:高
分析:虽然也有实施的事件驱动模型运行表现不好的情况(这可能关系消息结构),通常,该模型能达到更好的效果,通过它的异步执行能力。换句话说,解耦同时异步操作优点远远掩盖了接发消息开销的弱点。
扩展性
评分:高
分析:扩展性是该模式的高独立性及解耦的事件处理模块两个特征,所自然达成的。每个事件处理模块可被单独扩展。
开发容易性
评分:低
分析:由于异步的特征、契约创建、更高级的错误处理机制使得开发过程会相对更复杂。