探索如何使用 Amazon S3 作为事件总线
Amazon S3 是 AWS 生态系统中一项强大的服务。您可以使用它来存储 PB 级数据、托管静态网站、构建数据湖以及许多其他用例。
今天,我们将了解 Amazon S3 的幕后英雄之一——事件通知。我们将探讨如何将事件通知用作事件驱动架构的事件总线。
事件啥啥???
我们可能会花几个小时来讨论事件驱动架构。事实上,市面上有很多专门讨论这个主题的书籍。但我们不会在这里深入探讨这个细节。
相反,我们将简化事件驱动架构。就我们的目的而言,事件驱动架构是一种使用事件进行通信的系统设计。事件是状态的变化。它可以携带有关状态的信息,也可以仅仅是一个通知。
架构中的服务会生成事件,我们称之为生产者。与此同时,还有其他服务会消费事件,我们称之为消费者。没有任何规定说服务不能同时生成和消费事件。
现在我们已经了解了什么是事件驱动架构,让我们来构建一个吧!
事件总线
我们将构建一个利用 Amazon S3 作为事件总线的示例架构。那么,事件总线到底是什么呢?
一般来说,事件总线就像是事件驱动架构的中心枢纽。所有生产者服务都会在这里写入事件,而消费者服务则会从中获取事件。在事件总线如何发布消息方面,有几种设计选择。
方法一:向所有人发布所有内容
这是事件总线最简单的实现。它接收一个事件,并将该事件发布给所有订阅者。
与其他方法相比,这有一些独特的优点和缺点。其中一个优点是,它使事件总线非常易于理解。任何事件生产者都可以向总线发布消息,它会通知总线的所有订阅者。总线的职责和逻辑清晰简洁。
缺点是所有订阅者都会收到该事件,即使他们无法操作或关心该事件。消费者必须构建一些过滤逻辑来过滤掉他们收到但不关心的事件。
方法 2:过滤事件给消费者
生产者继续将所有事件发布到总线。但是,这些事件会被过滤后发送给消费者。这样,生产者可以继续发布所有事件,而消费者只会收到他们关心的事件。
这消除了的缺点。现在消费者可以只Approach #1
接收他们关心的事件。
这种方法有一个缺点。使用过滤器方法,消费者除了订阅事件总线之外,还必须订阅过滤器。他们订阅来自总线的事件,并仅为特定事件注册过滤器。
将所有内容发布给所有人的架构
现在我们对事件总线及其发布事件的方式有了更多的了解,接下来我们来看一下 AWS 中的事件总线。本文将重点介绍 Amazon S3 作为事件总线。我们先来看一下第一种方法——从 S3 的角度将所有内容发布给所有人。
S3 通常被视为通用对象存储服务。但实际上,它除了存储海量数据之外,还拥有许多其他功能。我们将利用其中一项功能——事件通知,将 S3 存储桶转变为事件总线。
我们要将所有内容发布给所有人,因此可以添加一个事件通知,将新事件推送给所有消费者。第一种方法的高级架构图如下所示:
生产者会将其事件写入 S3 存储桶。我们会在存储桶上配置事件通知,用于存储桶中创建的任何新对象。当生产者将新事件写入存储桶时,系统会向所有消费者推送通知。
但推送究竟是如何发生的呢?当存储桶中创建对象时,会触发事件通知。但我们必须告诉存储桶将通知发送到哪里。Amazon S3 开箱即用,允许我们将事件发送到以下任意目标。
- Amazon Simple Notification Service (Amazon SNS) 主题
- Amazon Simple Queue Service (Amazon SQS) 队列(非 FIFO)
- AWS Lambda
所以我们需要稍微改进一下我们提出的架构。按照我们目前的方法,消费者无法接收事件,因为他们没有订阅任何内容。我们需要配置存储桶,以便将事件通知发送到某个地方。现在,让我们将 S3 事件通知配置为发送到 SNS 主题。
现在,生产者会将其事件写入 S3 存储桶。这些事件会将通知推送到 SNS 主题。这些事件的消费者可以订阅该 SNS 主题来接收通知。消费者收到通知后,可以从 S3 获取触发该通知的密钥。该密钥存储在 S3 上,其中包含生产者写入的事件。
过滤事件到消费者架构
在上述架构下,所有生产者事件都会发送给所有订阅者。这意味着任何将事件写入 S3 存储桶的事件生产者都会通知 SNS 主题,从而通知所有订阅该主题的消费者。
如果所有消费者都需要查看所有事件,那么这样做当然没问题。但实际情况可能并非如此。更有可能的是,某个消费者只关心一种类型的事件,而不是所有事件。因此,在上述架构下,消费者服务必须有某种过滤器来忽略它们不关心的事件。
这是一种有效的方法。但是,如果我们可以将过滤工作从特定服务转移到事件总线上,情况会怎样呢?这样,每个消费者服务就只会收到它们真正关心的事件了。
事实证明,我们可以通过多种方式实现这一目标。
每个制作人的事件通知和 SNS 主题
一种方法是让事件生产者写入 S3 存储桶中各自的键前缀。然后,我们可以为存储桶中的每个键前缀设置一个事件通知,并将其推送到各自的 SNS 主题。
这种架构解决了我们的过滤问题。每种类型的事件生产者都可以写入其自己的前缀,FooProducer
并将其写入/foo/
S3 存储桶。然后,我们可以为存储桶中的每个前缀配置一个事件通知,并将其发送到单独的 SNS 主题。这样,当FooProducer
事件写入到/foo/
该前缀时,事件通知就会被推送到该特定前缀的 SNS 主题。
每个键前缀只有一个事件通知和 SNS 主题。关注这些事件的消费者可以订阅此类生产者的 SNS 主题。
这是可行的,但它确实有一些复杂性和缺点。
首先,基础设施和移动部件的数量要多得多。每种类型的事件生产者都必须在存储桶中拥有一个键前缀、一个 S3 事件通知和一个 SNS 主题。
其次,单个 S3 存储桶中可以存储的事件通知数量是有限制的。假设这个限制是 100,那么就只能有 100 个不同的事件生产者向该存储桶写入数据。这或许是个问题,或许不是,但至少是个限制。
最后,它将责任从消费者转移到了生产者。现在,消费者不再过滤收到的事件,而是生产者必须记住写入一个特殊的前缀。这并不复杂,但可能会写入错误的前缀,从而将错误的事件推送给错误的消费者。
事件通知和 SNS 主题过滤
我们还有另一种方法可以过滤事件给消费者。如果我们扩展原有架构的功能会怎么样呢Publish Everything to Everyone Architecture
?
当前架构会将每个事件推送给每个消费者。这意味着消费者必须具备内部逻辑来跳过它们不关心的事件。
如果我们能让 SNS 主题替我们进行过滤会怎么样?这样一来,我们就无需在消费者内部进行过滤,也无需再使用多个 SNS 主题了。
那么我们如何才能得到这个模型呢?
S3 目前不支持的一种方法是在 S3 事件通知上定义 SNS 主题消息属性。
消息属性是您可以添加到 SNS 通知中的有效负载。这些属性仅仅是作为通知元数据的键值对。借助SNS 消息过滤功能,您的订阅者可以订阅仅接收包含他们关注的消息属性的消息。
结合 S3 事件通知,我们希望定义一个带有消息属性的 S3 事件通知。当事件触发时,该属性会传递到 SNS 主题。以下是此想法的示例:
这仍然对事件通知有所限制,生产者仍然会写入自己的前缀。但现在这些通知可以推送到一个 SNS 主题。
按照这种思路,所有生产者都会像以前一样继续将事件写入 S3。存储桶已配置事件通知,用于将事件推送到单个 SNS 主题。但是,**每个通知配置都设置为将一个消息属性传递给 SNS 主题。此消息属性可能类似于service: FooProducer
。
允许FooConsumer
仅FooProducer
通过 SNS 消息过滤接收来自的事件。
听起来很棒吧?不过,目前 S3 事件还无法实现这一点。您无法配置推送到 SNS 主题的 S3 事件来传递任何消息属性。
那么,如果这个想法被否定了,我们还能做什么呢?
事实证明,我们可以通过在生产者中添加一个额外步骤来实现类似的行为。目前,生产者将其事件写入事件总线(在我们的例子中是 S3)。然后,生产者依靠事件总线将该事件传递给消费者。我们在之前的示例中看到了这一点,即事件通知推送到我们的 SNS 主题。
如果我们为生产者添加额外的步骤,就可以实现 SNS 消息过滤。生产者将事件写入 S3 存储桶。然后,它会向以下形式的 SNS 主题发送一条消息:
{
"eventStore": "<s3-bucket-name>",
"eventPath": "/foo/my-event-xyz"
}
生产者向 SNS 发送消息时,还会设置一个消息属性,例如service: FooProducer
。这样,消费者就可以订阅并声明他们只想要具有该属性的消息。
对于这种方法,我们的架构如下:
我们看到,生产者仍然将事件写入 S3(bubble 1
)。但是现在,写入之后,他们会向消费者正在监听的通用 SNS 主题(bubble 2
)发送一条消息。
通过这种方法,我们彻底移除了 S3 事件通知。S3 不再是我们的事件总线,而是我们的事件存储。在这种方法中,SNS 主题才是真正向消费者传递事件的事件总线。
消费者现在只能接收他们关注的生产者事件。一旦他们收到他们关注的 SNS 主题通知,他们就可以抓取生产者写入 S3 的事件。有关在哪里查找事件的所有信息都在 SNS 消息中。
我们学到了什么?
我们了解了事件驱动架构的一些入门概念。在探讨 Amazon S3 如何充当事件总线之前,我们需要先了解事件总线的概念。
我们研究了在事件驱动架构中将事件从生产者传递到消费者的两种不同方法。具体来说,我们研究了事件总线如何选择将每个事件发布给每个消费者,以及如何筛选事件发送给消费者。
我们深入研究了如何在 AWS 内部使用这两个概念。我们探索了如何利用带有事件通知功能的 S3 将所有事件发布到消费者订阅的单个 SNS 主题。
基于这个想法,我们研究了如何过滤发布到 S3 的事件并发送给消费者。具体来说,我们可以在 S3 存储桶中为每种类型的事件生产者设置前缀。每个前缀都可以拥有自己的 SNS 主题。消费者可以选择自己想要订阅的 SNS 主题,以便只接收来自他们关注的生产者的事件。
这是一种可行的方法,但需要相当多的额外基础设施。因此,我们探索了如何将一个事件通知发送到一个主题,并将 SNS 消息属性附加到该事件中。如果 S3 事件通知能够做到这一点,我们就可以通过这些消息属性将事件筛选给消费者。遗憾的是,S3 事件通知目前尚不支持此功能。
因此,我们找到了一种类似的方法。我们不再依赖 S3 事件通知,而是让生产者向中心 SNS 主题发送消息。这些生产者仍然将事件写入 S3,但会将消息发布到 SNS 主题,并设置消息属性,例如service
。这样一来,消费者就可以订阅主题并选择他们想要接收消息的消息属性。这改变了 S3 的定义,从事件总线变成了事件存储,但这确实是一个可行的解决方案。
结论
Amazon S3 是 AWS 生态系统中一项强大的服务。除了高耐用性和高可用性之外,它还提供了许多适用于各种架构的核心功能。
在本文中,我们重点介绍了 S3 事件通知以及如何利用它们将存储桶转变为事件总线。我们还探讨了这个想法的局限性。
希望您能将我们这里介绍的概念应用到您自己的项目中。或者至少,您可以认识到这些概念可能带来的局限性,并确定它们行不通。
想看看我的其他项目吗?
我是 DEV 社区的忠实粉丝。如果您有任何疑问,或者想讨论关于重构的不同想法,请在 Twitter 上联系我,或在下方留言。
除了写博客之外,我还创建了一门“通过使用 AWS 学习 AWS”课程。课程将重点讲解如何实际使用 Amazon Web Services 来托管、保护和交付静态网站。这是一个简单的问题,有很多解决方案,但它非常适合加深你对 AWS 的理解。我最近为该课程添加了两个新的附加章节,分别侧重于“基础设施即代码”和“持续部署”。
文章来源:https://dev.to/kylegalbraith/how-to-use-amazon-s3-as-an-event-bus-in-your-next-architecture-4oe9