微服务和事件驱动架构的概念介绍及分析

414 阅读13分钟

实现微服务架构

本文旨在让你对围绕微服务和事件驱动架构的概念有一个概述。

本文旨在让你了解围绕微服务和事件驱动架构的概念,这应该有助于你在是否应该采用微服务架构方面做出明智的决定。

什么是微服务?

除了是一个流行语外,微服务代表了一个被分成多个较小的应用程序。每个应用程序或微服务都与其他应用程序互动,以创建一个可扩展的系统。通常情况下,微服务作为容器化或无服务器的应用程序被部署到云中。

在了解太多细节之前,这里有几个原则,在构建微服务时要牢记:

  • 每个微服务都应该是一个有凝聚力的业务单元。
  • 每个微服务都应该拥有自己的数据。
  • 每个微服务都应该独立于其他的微服务。

此外,到目前为止,我们所研究的一切--也就是设计软件的其他原则--都适用于微服务,只不过是在另一个规模上。例如,你不想要微服务之间的紧密耦合(通过微服务的独立性解决),但耦合是不可避免的(和任何代码一样)。有许多方法来解决这个问题,比如发布-订阅模式。

关于如何设计微服务,如何划分微服务,微服务应该有多大,以及把什么放在哪里,都没有硬性规定。既然如此,我将打下几个基础,以帮助你开始并确定你的微服务之旅。

凝聚力强的业务单元

一个微服务应该有一个单一的业务责任。在设计系统时一定要考虑到领域,这应该有助于你将应用分成多个部分。如果你知道 领域驱动的设计(DDD),一个微服务很可能会代表一个 有界限的上下文,而这又是我所说的一个有凝聚力的业务单元。基本上,一个有凝聚力的业务单元(或称有边界的上下文)是领域中一个自成一体的部分,它与领域中的其他部分互动有限。

即使一个微服务的名字里有 ""字,将逻辑操作归入其下也比以微观规模为目标更重要。不要误会我的意思,如果你的单元很小,那就更好了。然而,假设你把一个业务单元分割成多个小部分,而不是把它放在一起(打破内聚力)。

在这种情况下,你很可能在你的系统内引入无用的聊天(微服务之间的耦合)。这可能导致性能下降,并导致系统更难调试、测试、维护、监控和部署。

此外,把一个大的微服务拆成小块比把多个微服务组装在一起更容易。

尝试将单一责任原则(SRP)应用于你的微服务:一个微服务应该只有一个改变的理由,除非你有很好的理由去做。

数据的所有权

每个微服务都是其凝聚的业务单元的真相来源。一个微服务应该通过API(例如Web API/HTTP)或其他机制(例如集成事件)共享其数据。它应该拥有这些数据,而不是直接在数据库层面与其他微服务共享。

例如,两个不同的微服务不应该访问同一个关系型数据库表。如果第二个微服务需要一些相同的数据,它可以创建自己的缓存,复制数据,或者查询该数据的所有者,但不能直接访问数据库;绝对不能

这个数据所有权的概念可能是微服务架构中最关键的部分,它导致了微服务的独立性。如果在这一点上失败,很可能会导致大量的问题。例如,如果多个微服务可以读取或写入同一数据库表中的数据,那么每次该表发生变化时,所有的微服务都必须更新以反映变化。如果不同的团队管理这些微服务,这意味着跨团队的协调。如果发生这种情况,每个微服务就不再是独立的了,这就为我们下一个话题打开了局面。

微服务的独立性

在这一点上,我们的微服务是有凝聚力的业务单位,并且拥有自己的数据。这就定义了独立性

这种独立性为系统提供了扩展的能力,同时对其他微服务的影响最小甚至没有。每个微服务也可以独立扩展,而不需要整个系统被扩展。此外,当业务需求增长时,该领域的每一部分都可以独立发展。

此外,你可以在不影响其他微服务的情况下更新一个微服务,甚至可以在整个系统不停止的情况下让一个微服务下线。

当然,微服务必须彼此互动,但它们的互动方式应该定义你的系统的运行情况。有点像垂直切片架构,你并不局限于使用一套架构模式;你可以独立地为每个微服务做出具体决定。例如,你可以为两个微服务与其他两个微服务之间的通信方式选择不同的方式。你甚至可以为每个微服务使用不同的编程语言。

提示

我建议小型企业和组织坚持使用一种或几种编程语言,因为你很可能有较少的开发人员,而且每个人都有更多的事情要做。根据我的经验,当人们离开时,你要确保业务的连续性,并确保你可以替换他们,而不是因为在这里和那里使用一些晦涩的技术(或太多的技术)而使船沉没。

现在我们已经定义了基础知识,让我们跳到使用事件驱动架构的微服务的不同通信方式。

事件驱动架构简介

事件驱动的架构(EDA)是一个围绕着消费事件流或运动中的数据的范式,而不是消费静态状态。

我所定义的静态状态是指存储在关系型数据库表中的数据或其他类型的数据存储,如NoSQL文档存储。这些数据蛰伏在一个中心位置,等待行动者去消费和变异。它在每次变异之间都是陈旧的,数据(例如记录)代表了一种有限的状态。

另一方面,运动中的数据则相反:你消费有序的事件,并决定每个事件带来的状态变化。

什么是事件?人们经常把事件、信息和命令这几个词互换。让我们试着澄清一下:

  • 消息是代表某种事物的数据。
  • 消息可以是一个对象,一个JSON字符串,字节,或任何其他你的系统可以解释的东西。
  • 一个事件是一个代表过去发生的事情的消息。
  • 命令是一条消息,用来告诉一个或多个接收者做什么。
  • 命令是发送的(过去式),所以我们也可以把它看作是一个事件。

一条消息通常有一个有效载荷(或主体)、标头(元数据),以及一种识别它的方法(可以通过主体或标头)。

我们可以使用事件将一个复杂的系统分成更小的部分,或者让多个系统相互交谈而不产生紧密的耦合。这些系统可以是子系统或外部应用,如微服务。

数据传输对象(DTO)的网络API,事件成为将多个系统联系在一起的数据契约(耦合)。在设计事件时,必须仔细思考这个问题。当然,我们无法预知未来,所以我们只能做很多事情来让它第一次就变得完美。有一些方法可以使事件版本化,但这不在本文的讨论范围之内。

EDA是打破微服务之间紧密耦合的绝佳方式,但需要重新调整你的大脑来学习这种较新的范式。与更多的线性思维方式(如使用点对点通信和关系型数据库)相比,工具不太成熟,专业知识也很稀缺,但这正在慢慢改变,非常值得学习(在我看来)。

我们可以把事件分为以下几个重叠的桶:

  • 领域事件
  • 集成事件
  • 应用事件
  • 企业事件

正如我们接下来要探讨的,所有类型的事件在不同的意图和范围内发挥着类似的作用。

领域事件

领域事件是一个基于DDD的术语,代表领域中的一个事件。然后,该事件可以触发其他的逻辑片段,以便随后执行。它允许将一个复杂的流程划分为多个较小的流程。领域事件与以领域为中心的设计(如Clean Architecture)配合得很好,因为我们可以用它们将复杂的领域对象分成多个小块。领域事件通常是应用事件。我们可以使用MediatR来发布应用程序内的领域事件。

总而言之,领域事件将领域逻辑的碎片整合在一起,同时保持领域逻辑的隔离,导致松散耦合的组件,每个组件持有一个领域责任(单一责任原则)。

集成事件

集成事件与领域事件类似,但用于向外部系统传播消息,将多个系统集成在一起,同时保持它们的独立性。例如,一个微服务可以发送new user registered 事件消息,其他微服务对此作出反应,如保存user id ,以启用额外的功能,或向该新用户发送问候邮件。

我们使用一个消息代理或消息队列来发布此类事件。我们将在介绍完应用和企业事件之后,再来介绍这些事件。

总而言之,集成事件将多个系统集成在一起,同时保持它们的独立性。

应用事件

应用程序事件是一个应用程序内部的事件,它只是一个范围问题。如果事件是单个进程的内部,该事件也是一个领域事件(最有可能)。如果该事件跨越了你的团队拥有的微服务边界(同一个应用程序),它也是一个集成事件。事件本身不会有什么不同;是它存在的原因和它的范围描述了它是否是一个应用事件。

总而言之,应用程序事件是一个应用程序的内部。

企业事件

企业事件描述了一个跨越企业内部边界的事件。这些都是与你的组织结构紧密相连的。例如,一个微服务发送了一个事件,其他团队,属于其他部门或部门,消费了这个事件。

围绕这些事件的治理模式应该与只有你的团队消费的应用事件不同。有人必须考虑谁可以消费这些数据,在什么情况下,改变事件模式(数据合同)的影响,模式所有权,命名惯例,数据结构惯例,等等,否则就有可能建立一个不稳定的数据高速公路。

注意事项

我喜欢把EDA看作是应用、系统、集成和组织边界中间的一条中央数据高速公路,事件(数据)以松散耦合的方式在系统之间流动。

这就像一条高速公路,汽车在城市之间流动(没有交通堵塞)。这些城市并不控制什么车去哪里,而是对游客开放。

总而言之,企业事件是跨越组织边界的集成事件。

我们在这个事件驱动架构的快速概述中定义了事件、消息和命令。一个事件是过去的一个快照,一个消息是一个数据,一个命令是建议其他系统采取行动的事件。由于所有的消息都是过去的,所以调用事件是准确的。然后我们把事件组织成几个重叠的桶,以帮助识别意图。我们可以为不同的目标发送事件,但无论它是关于设计独立的组件还是联系业务的不同部分,一个事件仍然是尊重某种格式(模式)的有效载荷。该模式是这些事件的消费者之间的数据合同(耦合)。这个数据合同可能是最重要的部分;破坏合同,破坏系统。

现在,让我们看看事件驱动架构如何帮助我们在云规模上遵循SOLID 原则:

  • S:系统通过引发和响应事件而相互独立。事件本身是将这些系统联系在一起的胶水。每一块都有单一的责任。
  • O:我们可以通过向某一事件添加新的消费者来修改系统的行为,而不影响其他应用。我们也可以提出新的事件,开始建立一个新的过程,而不影响现有的应用程序。
  • L: 不适用。
  • I: EDA允许我们创建多个较小的系统,这些系统通过数据合约(事件)进行整合,这些合约成为系统的消息传递接口,而不是建立一个单一的流程。
  • D: EDA使系统能够通过依赖事件(接口/抽象)来打破紧密耦合,而不是彼此直接通信,颠倒了依赖流。

EDA不仅有优点,它也有一些缺点:

  • 微服务是有代价的,对于许多项目来说,构建一个单体仍然是一个好主意。
  • 给单体添加新功能比给微服务应用添加新功能更容易。
  • 大多数时候,单体中的错误比微服务应用中的错误成本更低。

微服务架构与构建单体是不同的。我们不再是一个大的应用程序,而是把它分成多个小的应用程序,我们称之为微服务。微服务必须相互独立;否则,我们将面临与紧耦合类相关的相同问题,但在云规模上。

当你需要扩展,想走无服务器路线,或在多个团队之间分担责任时,微服务是很好的选择,但要考虑到运营成本。从单片机开始,在扩展时将其迁移到微服务是另一种解决方案。你也可以规划你未来向微服务的迁移,这将导致两全其美,同时保持低运营复杂性。

我不希望你抛弃微服务架构,但我想确保你在盲目跳入之前权衡这样一个系统的利弊。你的团队的技能水平和学习新技术的能力也可能影响到跳入微服务船的成本。