事件驱动架构如何解决现代Web应用程序的挑战

哈Ha!







虽然我们的销售将继续以最挑剔的口味进行,但我们将您的注意力转向我们创造性搜索的另一个主题:事件驱动架构(EDA)。在剪切下方,您将找到漂亮的流程图以及有关此创新范例如何帮助Web应用程序开发的故事。



本文将探讨推动现代Web开发创新的一些挑战。接下来,我们将深入研究事件驱动架构(EDA),以通过重新定义服务器架构来解决这些问题。



自服务器提供静态HTML内容以来,Web应用程序已经走了很长一段路。今天,Web应用程序变得更加复杂,它们使用各种框架,数据中心和技术。在过去的几年中,可以注意到两个趋势决定了IT市场的发展:



  • 将应用程序转移到云中;
  • 微服务架构的实现


这些想法在很大程度上决定了当今软件的设计和构建方式。可以说,今天我们不再构建应用程序,而是构建平台。应用程序不再占用共享的计算空间。相反,它们必须通过轻量级的通信协议(例如REST API或远程过程调用(RPC))相互交换信息。这种模式带来了很棒的产品,例如Facebook,Netflix,Uber等。

本文将探讨推动现代Web开发创新的一些挑战。接下来,我们将深入研究事件驱动架构(EDA),以通过重新定义服务器架构来应对这些挑战。



现代网络的当前问题



任何Web技术都必须应对旨在平稳运行的现代多用户异步应用程序必须满足的挑战:



可用性



现在,我们不使用一个应用程序,而是使用许多(甚至数十个)相关服务,并且每个服务必须每周七天全天候解决他们的问题。如何做到这一点?通常,服务会水平扩展到多个实例,这些实例可以分布在多个数据中心中,以确保高可用性。对该特定服务的所有请求都会路由并在所有实例之间平均分配。一些部署工具提供了自我修复功能,因此,如果一个实例失败,则会创建另一个实例来代替它。



可扩展性



可伸缩性在许多方面都类似于可用性。可访问性的实质是确保至少有一个服务实例已启动并正在运行,可以为传入的请求提供服务。反过来,可伸缩性主要与性能有关。如果应用程序过载,则会创建该应用程序的新实例以适应增加的请求数。但是垂直扩展应用程序并不是一件容易的事,尤其是在有状态应用程序方面



真理之源



在微服务出现之前,这是一个相当简单的任务。所有数据都位于一个位置,通常它是一个或另一个关系数据库。但是,当许多服务共享一个数据库时,可能会产生问题,例如不同团队之间关于架构更改或性能问题的依赖性。通常,通过为每个服务分配自己的数据库来解决此问题。分布式真相源对于维护干净的体系结构非常有帮助,但是在这种情况下,您必须处理分布式事务和支持多个数据库的复杂性。



同步性



在典型的请求-响应方案中,客户端等待服务器响应。它会阻止所有操作,直到收到响应或指定的延迟到期为止。如果您采取这种行为并将其实现到具有贯穿整个系统的调用链的微服务体系结构中,则可以轻松地陷入所谓的“微服务地狱”。一切始于对一项服务的调用,我们称其为“服务A”。但是随后服务A必须调用服务B,然后乐趣就开始了。此行为的问题是这样的:如果服务本身与阻塞的资源关联(例如,线程挂起),则延迟成倍增长。如果我们允许每个服务延迟500毫秒,并且链中有五个服务调用,那么第一个服务将需要2500毫秒(2.5秒)的延迟,最后一个则需要500毫秒。







现代网络的挑战



事件驱动架构简介



事件驱动体系结构(EDA)是一种软件体系结构范例,可促进事件的生成,发现,使用以及对事件的响应。




在经典的三层应用程序中,系统的核心是数据库。在EDA中,重点转移到事件及其在系统中的流动方式。重点的转移使我们可以完全改变设计应用程序的方式并解决上述问题。



在详细了解EDA中的操作方式之前,让我们看一下“事件”是什么。事件是启动一些通知或更改应用程序状态的操作。灯打开(通知),恒温器关闭加热系统(通知),用户更改了地址(状态更改),您的一个朋友更改了电话号码(状态更改)。这些都是事件,但我们还不应该将它们添加到事件驱动的解决方案中。假定仅将与业务相关的事件添加到体系结构。从业务角度来看,“用户下订单”事件很重要,但“用户吃订购的比萨饼或午餐”则不重要。



如果您考虑某些事件,那么对于某些事件而言,立即清楚的是它们对业务很重要,而对于某些事件则不重要。特别是那些响应其他事件而发生的事件。使用一种称为“事件攻击的技术来识别通过系统的事件。召集应用程序开发的参与者(从程序员到业务逻辑开发人员和主题专家),并共同映射所有业务流程,并以特定事件的形式进行呈现。准备好此类地图后,将以开发应用程序时必须满足的要求的形式来表述工作的结果。







事件攻击方法描述的预订应用程序示例



在确定了我们感兴趣的事件并决定了如何识别它们之后,让我们看一下该范例如何解决上述典型问题。



事件的流向是单向的:从生产者到消费者。将这种情况与REST调用进行比较。事件生产者原则上不会期望使用者的响应,而在REST调用的情况下,总是会有响应。没有响应意味着除非发生其他情况,否则无需阻塞代码的执行。在这种情况下,事件本质上是异步的,从而完全消除了陷入延迟的风险。



事件是由于操作而发生的,因此没有目标系统。不能说服务A触发了服务B上的事件;但是我们可以说服务B对服务A生成的事件感兴趣。确实,该系统中可能还有其他“感兴趣方”,例如服务C或D。



我们如何确保某个系统中发起的事件能够到达所有“感兴趣”的服务?通常,使用消息代理来解决此类系统。代理只是一个应用程序,它充当事件发送方(生成事件的应用程序)和事件使用者之间的中介。因此,可以很好地将应用程序彼此分离,同时注意可访问性问题,这已在上文中讨论。如果该应用程序当前不可用,则返回在线状态后,它将开始使用事件并对其进行处理,以弥补在该应用程序不可用期间发生的所有事件。



数据仓库呢?事件可以存储在数据库中,还是需要其他东西而不是数据库?当然,事件可以存储在数据库中,但是在这种情况下,它们的“事件”本质丢失了。一旦事件发生,我们将无法对其进行更正,因此事件本质上是不可变的。反过来,数据库是可更改的……将数据输入数据库后,就可以更改它们。



最好将事件存储在事件日志中。事件日志只不过是集中式的数据仓库,其中每个事件都记录为一系列不可更改的记录,即所谓的“日志”。可以将日志与日志进行比较,在日志中将每个新事件添加到列表的末尾。您始终可以通过重播从开始到现在的所有日志事件来重新创建最新状态。



因此,我们涵盖了除可伸缩性之外的所有问题。事件驱动的服务始终设计为跨多个实例部署。由于状态本身存储在事件日志中,因此服务本身将是无状态的,这允许对任何感兴趣的服务进行手术精确缩放。



该原则的唯一例外是旨在创建实例化视图的服务。...本质上,实例化视图是描述特定时间点的事件日志的状态。使用此方法可以更轻松地查询数据。回到可伸缩性问题,实例化视图只是事件的汇总视图,类似于表。但是我们将这些表存储在哪里?大多数情况下,您会在内存中看到此类聚合,与此同时,我们的服务会自动变为有状态聚合。一种快速简便的解决方案是为创建实例化视图的每个服务提供一个本地数据库。这样,状态就存储在数据库中,并且服务可以无状态运行。







尽管事件驱动的体系结构已经存在了15年以上,但它直到最近才得到了广泛的普及,这绝非偶然。大多数公司正面临着巨大需求的“数字转型阶段由于这些要求的复杂性,工程师必须掌握软件设计的新方法,这特别意味着削弱了服务之间的连通性并降低了维护服务的成本。EDA是解决这些问题的一种可能的解决方案,但不是唯一的解决方案。另外,不要期望所有问题都能得到解决,您只需要切换到EDA。某些功能可能仍需要健壮的老式REST API或将信息存储在数据库中。选择最适合您的一款并正确设计!



All Articles