在本文中,我们将学习如何使用事件驱动的架构模式,并将其应用于Axon框架。让我们开始吧。
给定问题
一般而言,在谈论企业解决方案时,会选择客户端-服务器结构而不是其他模型。客户端和服务器通常位于单独的硬件上,因此,为了从服务器访问资源,将需要在某种网络上建立通信通道。
因此,要在其他系统之间进行通信,我们的情况将如下图所示。
然后,我们有这种请求驱动的体系结构的缺点。
- 如果任何系统中存在某些错误,则很难跟踪日志。
- 当我们有多个其他系统时,将它们集成在一起是一个难题。
- 独立扩展系统的某些部分很困难。
连续地,单片应用程序通常使用分层的方法来构造我们的代码,其中每一层都有特定的功能。两层应用程序的结构将包括一个由用户界面表示的表示层,该表示层将任务转换为用户可以理解的内容。数据层是信息存储和检索的地方,通常是数据库或其他某种存储解决方案。还有所有业务逻辑所在的业务层。该层通过处理和评估用户界面给出的命令来控制应用程序的功能。
在这种情况下,业务层由多个模块组成,例如订单,促销,用户,付款和建议。当功能数量增加时,应用程序的复杂性也会增加,维护和开发它的成本可能会更高。
为了解决这个问题,出现了一种新的模式,微服务。在微服务体系结构中,应用程序分解为一组松散耦合的服务,其中每个服务或模型都解决了一个特定问题,并通过简单,轻量级的协议与其他服务进行通信。微服务架构可以轻松支持多个客户端,例如Web或移动客户端,而无需任何额外开销。
考虑到先前定义的模型,将它们转换为微服务模式将产生独立的服务。订单将接受并处理订单。建议,根据购物车中的当前物品,它可以建议其他物品。用户,负责授权客户。付款处理和管理付款。而促销,根据一些特定的规则,客户可以从折扣中受益。
为了确保松散耦合,每个服务都有一个数据库是必不可少的。而且,每个服务都可以使用最合适的数据库类型。服务通过某种轻量级的通信协议(通常是REST API或RPC)进行互连。要为客户端提供一个单一的入口点,需要一个API网关。API网关会将来自客户端的所有请求转发到适当的服务,但同时可以包括一些功能,例如身份验证或监视。对于复杂的系统,微服务是一个很好的解决方案。但是,正如我们在图上看到的那样,尽管系统变得越来越复杂,但是服务之间的依赖关系也导致了微服务地狱的出现。
所有这些都是从客户端发出的单个请求开始的,但是为了完成请求,整个系统都会触发许多其他请求,这使得在整个系统中跟踪和管理我们的数据变得非常困难。为了解决这个问题,需要一种不同的思维方式。因此,事件驱动的体系结构应运而生。
事件驱动架构模式的解决方案
事件驱动的体系结构是一种软件体系结构模式,可促进事件的产生,检测,使用和响应。换句话说,在事件驱动的体系结构中,一切都围绕事件而不是数据。在EDA中可以遇到多种编程模型。在它们当中,最受欢迎的仍然是微服务模式,其中将每个微服务集中于处理某些类型的事件,并使用基于代理的技术将它们分离。
事件驱动的微服务架构可以很好地解决许多问题。无服务器应用程序和现在流行的云计算服务Faas-功能即服务可以解决需要小型且寿命短的应用程序的问题。由于公司需要更快地响应客户的需求,因此如今的流媒体模型非常流行。在流传输过程中,事件将在事件到达系统时进行处理,因此我们可以将流视为永无止境的操作,这使公司几乎可以实时做出反应。事件源意味着在基于日志重新创建当前状态时,将数据存储为一系列事件。尝试对日记进行成像,在该日记中,我们记帐他们用现金进行的所有交易,而不是每次进行交易时都更改当前余额。
CQRS代表命令查询责任隔离,它通过分离用于读写的接口来工作。在传统系统中,读取数据和写入数据都是通过同一组实体执行的。使用CQRS,接口将被分离并通过不同的API公开。
以下是EDA中通常使用的一些概念。
-
消息是通信的基本单位,它实际上可以是任何东西。它可以是字符串,数字或完整的对象。消息可以描述为没有特殊意图的通用接口。
-
事件是一条消息,它通知各种听众有关已发生的事情。
事件的真实示例是在广告板上发布广告。发布事件的人将被称为生产者,然后被称为消费者的感兴趣方将订阅该广告,然后对发布的事件做出反应。应该始终以过去式来引用事件,因为它定义了已经发生的事情或已经触发的事情。
域事件是系统中正在发生且与业务相关的事件。通常,一个域事件会引起反应,甚至可能引发另一个事件,而我们的系统需要对此做出反应。
-
一个命令将通过在生产者和消费者之间建立一对一的连接来提出有针对性的行动。例如,订购炸玉米饼可以看作是一个命令,因为我们通过呼叫甚至使用我们最喜欢的家庭辅助设备来下订单,因此系统之间存在相互作用,该相互作用知道如何到达另一个。呼叫一个随机数来订购炸玉米饼是很奇怪的。
何时使用
- 当我们的系统必须处理多个请求时,这将花费大量时间来检查业务逻辑。
好处与缺点
-
好处
-
所有组件都彼此分离。
当组件需要通信时,假设服务A需要向服务B传输一些数据,那么它将使用基于代理的技术。代理通过促进两个系统之间的数据传输而充当中间件,因此,他们唯一需要了解的就是代理的位置。服务A和服务B之间的直接通信将被禁止,因为这将导致紧密耦合的情况。
-
封装形式
事件可以在不同的功能范围内进行分类,并且可以在相同的范围内进行处理。例如,在典型的电子商务系统中,我们将遇到与分析,付款,促销,建议甚至身份验证相关的事件。每种类型的事件之间都有明确的界限,而不必担心混淆它们。
-
速度
事件驱动的体系结构旨在通过对传入事件到达系统时的反应做出几乎实时的运行。
让我们比较一下具有事件驱动功能的传统系统,以及它们在常见情况(例如处理通知)中的反应方式。在一个典型的系统中,用户会告诉应用程序-“让我知道您什么时候又有这件令人敬畏的T恤库存吗?”。当库存增加时,用户可能必须等待排定的工作来扫描所有想要在该物品重新入库时通知的人员。该作业完成后,将通知用户。
在事件驱动的情况下,用户将通过发送一个名为notification_enabled的事件来告诉系统他希望得到通知。库存量增加,传输称为stock_updated的事件,并通知用户。
-
可扩展性
在事件驱动的体系结构中,可伸缩性自然而然地使应用程序可以容纳越来越多的工作,并在不需要它们时减少资源。
例如,欺诈检测服务,其工作是检测是否有任何交易被视为可疑并最终被停止。随着传入事件数量的增加,事情变得越来越慢,某些请求可能永远不会被处理,甚至更糟的是,整个系统可能会冻结。最简单的解决方案是能够水平扩展欺诈检测以处理事件增长的能力。
-
-
缺点
设计和开发事件驱动的应用程序需要陡峭的学习曲线才能上手,因为它提出了一些传统架构本质上已经给出答案的问题。其中一些问题可能是,在事件驱动的体系结构中,什么可以被认为是真理的源头?另一个问题可能是,如果在某个时间点出现问题并且发生了重复事件该怎么办?我们准备好处理了吗?
维护可能会变得非常复杂。简单的事件驱动体系结构是在多年前引入的,但是随着时间的流逝,系统不断发展,企业解决方案通常需要复杂的事件流,这可能会变得难以理解和维护。
-
失去交互能力
在传统的解决方案中,进入系统的请求可以解释为事务,并且如果在处理该请求期间出现任何问题,则可以轻松还原事务。
在事件驱动的系统中,一个请求将导致一个或多个事件分散在整个系统中,从而在发生故障时几乎不可能还原由它们触发的动作。
在调试我们的系统时,沿袭是一个重要的主题。事件可能会丢失或损坏,并且由于应用程序松散耦合,因此几乎不可能确定事件从哪个系统到达。一个简单的解决方案是将某种标识符固定到要传递给的每个应用程序中的事件。
代码C ++ / Java / Javascript
为了了解事件驱动架构的实现方式,我们将使用Axon框架进行处理。