事件驱动架构(翻译)

事件驱动的架构模式时一个非常流行的分布式异步架构模式,通常用来生成高扩展性的应用。它的适应性非常强,可以用在小应用也可以用在大的复杂的应用上。事件驱动的架构是由高度解耦、单目的的事件处理单元组成,这些单元异步地接受和处理事件。

时间驱动架构模式主要由两种拓扑结构组成,中继器与代理。如果你需要把一个事件中各个步骤通过中央中继器组合起来,那么就使用中继器拓扑结构。当你不想有中央中继器,而是将各个步骤串起来,就使用代理拓扑结构。因为不同拓扑结构的特性和实现区别挺大,所以有必要搞清楚这两者来选择最适合应用的结构。

中继器拓扑

如果事件有许多步骤而且需要有效的组合在一起来处理事件,中继器拓扑会很实用。举例来说,一个单一的事件,完成证券交易,这个事件需要你首先验证这个交易,然后检查交易的合规性,再讲交易委托给代理,计算佣金,最终通过代理完成交易。所以这些步骤都需要有效的组合,来决定步骤的顺序,以及哪些步骤应该串行,哪些该并行。

中继器拓扑主要有四种不同的架构组成部分:事件队列,事件中继器,事件通道,事件处理器。事件流从一个客户端发送事件到事件队列开始,事件队列将事件传给中继器。中继器接受最初事件然后将事件分解成各个步骤,发送到不痛的事件通道来执行每一步。事件处理器,会监听事件通道,从事件中继器接受事件并执行具体的业务逻辑。图2-1说明了事件驱动的中继器拓扑结构。

在事件驱动架构中,通常有十几到几百个事件队列。事件队列的实现并没有限制,可以是一个消息队列,一个wed服务端点,或者是他们的组合。

在这种模式下,有两类事件:初始事件和待处理事件。初始事件是中继器接受的原始事件,而待处理事件是由中继器生成并由事件处理单元接收。

事件中继器负责组织初始事件包含的步骤。对每一个步骤,中继器会发送一个特定的待处理事件到事件通道,然后待处理事件会被事件处理器接收并处理。需要重点关注的是,中继器并不直接执行处理初始事件的业务逻辑,而是将初始事件分为几个步骤。

事件通道是中继器用来给事件处理器异步发送待处理事件的,这些待处理事件是初始事件经过中继器生成的。事件通道可以是消息队列或者消息主题,待处理事件被多个事件处理器处理(每个处理器根据接收的事件处理不同的任务)。

事件处理器包含对事件处理的业务逻辑。事件处理器是独立的、互不依赖的、高度解耦的结构,处理应用中某个具体的任务。尽管时间处理器的粒度可能从很精细(根据书序计算商业税)到很粗糙(处理一项保险申购),还是要注意大体上,每个事件处理器都应该执行不同的任务而且不应该依赖其他的处理器来完成它自身的任务。

事件中继器可以通过各种各样的方法来实现。作为一个架构师,我们应该理解每一种实现来确保我们选择的方案是最适应应用场景的。

最简单常见的事件中继器实现方案是开元的集成蜀绣,比如Spring Integration, Apache Camel, 或Mule ESB。这些开元集成枢纽的事件流通常是用Java或DSL实现的。对于更复杂的中继器和组合方法,可以用BPEL(business process execution language)耦合一个BPEL引擎比如开元的Apache ODE。BPES是一个像XML一样的标准语言,它描述可以描述处理初始事件的数据和步骤。对非常大型的应用来说,如果包含非常复杂的组合(保罗需要人为干预的步骤),可以用一个BPM比如jBPM来实现。

理解需求毕竟找到最适合场景的中继器实现,是中继器拓扑结构最重要的事情。使用一个开源的基础枢纽来进行非常复杂的业务处理管理是失败的,就想使用BPM来处理非常简单的逻辑,一样也是失败的。

为了说明中继器逻辑是如何工作的,假设你已经被保险公司确认投保,然后你决定执行。在这个例子中,初始事件可以称作“重定位事件”。图2-2表明了中继器怎么处理一个重定位事件。对每个初始事件步骤,事件中继器会创建一个待处理事件(比如修改地址,重新计算报价等),将待处理事件发送给事件通道并等待事件通道被相应的事件处理器处理。这个处理会一直持续知道所有的初始事件被处理完毕。重新计算报价和更新理赔的步骤,是可以同时异步执行的。

代理人拓扑

代理人拓扑和中继器拓扑最大的区别是代理人没有中央事件中继器;小溪流像链条一样分布到事件处理器中,这过程由一个轻量级的消息代理(如ActiveMQ,HornetQ等)完成。当你需要一个相对简单的事件处理过程而且你不需要中央组成器的时候,这种拓扑结构就会非常有用。

代理人拓扑也有两类主要的成分:代理人和事件处理器。代理人组件可以是集中式或联邦式的,而且包含所有事件流中用到的的事件通道。这些事件通道可以是消息队列,消息主体或者两者结合。

图2-3就是代理人拓扑结构。可以从图表中看出,没有中央事件中继器组件控制和组合初始事件;二是每个事件处理器组件负责处理一个事件并且发布一个新的事件来说明它刚才执行的动作。举例来说,一个事件处理器比如平衡一个证券投资组合,可能会接受一个叫做股票拆分的初始事件。基于这个初始事件,事件处理器会做一些投资组合调整,然后发布一个新的事件“调整投资组合”给代理人,这个事件会被另外一个不同的事件处理器接收。注意到会有一个事件被发出来后没有被另一个事件处理器接收的情况发生。这通常发生在你给应用升级或者提供一些未来的功能和拓展的时候。

为了说明代理人拓扑的工作方式,我们使用和中继器拓扑一样的例子(一个确认投保的人搬家)来说。因为代理人拓扑没有一个中央事件中继器来接收初始事件,消费者处理组件直接接收这个事件,改变消费者的地址(如 地址变更事件)。在这个例子中,有两个事件处理器对地址变更事件感兴趣:报价处理和理赔处理。报价处理器根据地址变更重新计算新的汽车保险赔率,并发布一个事件告诉其他处理器它所做的事情。理赔处理组件,另一方面,接收同样的地址变更事件,但是它会更新一个没有理赔的保险并发布一个新的事件“更新理赔”到系统中去。这些新的事件会被其他事件处理组件接收,而且这个链式的系统会一直进行直到没有和初始事件有关的事件产生。

从图2-4中可以看到,代理人拓扑所有都是有关业务逻辑处理的整个过程。最好的理解方式就是把它想象成一个接力赛。在接力赛中,运动员拿着一个接力棒然后跑一段固定的距离,然后将接力棒交给下一个运动员,依次进行知道最后一个运动员冲过终点。在接力赛中,一旦运动员交接完接力棒,他就和接力赛无关了。这对代理人拓扑也是适用的:一旦一个事件处理器交接了事件,它就不会再参与到这个事件的处理过程中。

思考

事件驱动架构模式实现起来相对来说比较复杂,根本上是因为它的异步分布式本质。当我们实现这个模式时,一定要处理各种各样的分布式架构的问题,比如远程处理能力,相应的缺乏,以及代理或中继器失败后的重连逻辑。

一个需要考虑的点是当选择这种模式时,需要考虑单个业务处理之间的原子性。因为事件处理器是高度解耦和分布的所以很难维持一个工作的食物单元。出于这个原因,在使用这种模式设计应用时,一定要持续思考哪些事件可以或不可以独立执行,以及根据事件来设置事件处理器的粒度。如果需要将单一工作拆分到多个事件处理器中,即需要强行把一个能单独处理的事物拆分到多个事件处理器,那么可能不太适合这种模式。

也许事件驱动最难的部分是事件处理器组件数据结构的创造、维护和管理。每个事件通常都有一个特定的结构(如 传给处理器的数据值和数据格式)。当使用这种模式时,选定一个标准的数据格式非常重要,然后从一开始就建立数据结构的控制。

模式分析

全局灵活性

评分:高

分析:全局灵活性是对持续变化的环境做出快速响应的能力。由于事件驱动组件都是单一目的而且和其他处理器是完全解耦的,所以改变也会隔离在一个或少数处理器中因此使得它能够快速的对改变做出应对且不影响其他组件。

部署容易程度

评分:高

分析:总体来说这种模式相对来说是比较容易部署的,因为各个事件处理器天然是解耦的。代理人拓扑比中继器拓扑更容易部署,主要是因为中继器拓扑有中央中继器,中继器和事件处理器都和中央中继器耦合,所以事件处理器的改变可能也会引起事件中继器的改变,两者都需要重新部署。

可测试性

评分:低

分析:尽管独立单元测试总体来说并不难,也不需要一些特定的客户端或工具来获得测试结果。但是由于这种模式的异步天性,测试还是一件复杂的事情。

性能

评分:高

分析:尽管由于事件驱动架构包含一个消息队列结构,会造成这种架构性能的下降,但是通常,由于异步特性,这种模式还是具有非常高的性能。换句话说异步和解耦分布式带来的好处比消息队列带来的坏处要大。

可扩展性

评分:高

分析:扩展性是这种架构模式的天性,因为整个架构都是解耦的,分布式的。每个事件处理器都可以独立的扩展。

开发容易程度

评分:低

分析:由于异步特性,这种模式开发起来可能不那么容易。

发布了42 篇原创文章 · 获赞 33 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/gaussrieman123/article/details/89138205