10.1 事件驱动架构
事件是一种发生的事情。它是系统中发生的相关事件,比如状态变更,事件可以来自多种来源。本章将重点关注应用程序,但事件也可以发生在 IoT 设备、传感器或网络中。当事件发生时,相关方可以被通知。事件通知通常通过消息完成,消息是事件的数据表示。
在事件驱动架构中,我们识别事件生产者(event producers)和事件消费者(event consumers)。生产者是检测事件并发送通知的组件。消费者是当特定事件发生时被通知的组件。生产者和消费者彼此不知道对方,独立工作。生产者通过向由事件代理(event broker)运营的频道发布消息来发送事件通知,事件代理负责收集和路由消息给消费者。当事件发生时,消费者会被代理通知并可以对其采取行动。
当使用代理来承担事件的处理和分发时,生产者和消费者之间的耦合最小。特别是,它们在时间上是解耦的,因为交互是异步的。消费者可以在任何时候获取和处理消息,而不会影响生产者。
在本节中,您将学习事件驱动模型的基础知识,以及它们如何帮助在云中构建更具弹性和松散耦合的应用程序。
10.1.1 理解事件驱动模型
事件驱动架构可以基于两种主要模型:
发布/订阅(pub/sub) — 此模型基于订阅。生产者发布事件,这些事件被发送给所有订阅者进行消费。事件被接收后无法重放,因此新加入的消费者将无法获取过去的事件。
事件流(event streaming) — 在此模型中,事件被写入日志。生产者在事件发生时发布事件,所有事件按顺序存储。消费者不订阅它们,但可以从事件流的任何部分读取。在此模型中,事件可以重放。客户端可以随时加入并接收所有过去的事件。
在基本场景中,消费者在事件到达时接收和处理事件。对于特定用例(如模式匹配),它们还可以处理时间窗口内的一系列事件。在事件流模型中,消费者还有处理事件流的额外可能性。
事件驱动架构的核心是能够处理和路由事件的平台。例如,RabbitMQ 是与 pub/sub 模型一起使用的常见选择。Apache Kafka 是一个强大的事件流处理平台。
事件流模型非常吸引人,并且由于过去几年开发的许多技术而日益普及,这些技术允许您构建实时数据管道。然而,这是一个复杂的模型,值得用一本专门的书来有效地讲解。在本章中,我将介绍 pub/sub 模型。
在更详细地分析此模型之前,我将为 Polar Bookshop 系统定义一些需求,并将它们作为探索使用 pub/sub 模型的事件驱动架构的手段。
10.1.2 使用 pub/sub 模型
在 Polar Bookshop 系统中,我们需要实现一个事件驱动解决方案,以允许不同应用程序之间异步通信,同时减少它们之间的耦合。以下是需求:
当订单被接受时:
- Order Service 应通知感兴趣的消费者该事件。
- Dispatcher Service 应执行一些逻辑来调度订单。
当订单被调度时:
- Dispatcher Service 应通知对此事件感兴趣的消费者。
- Order Service 应在数据库中更新订单状态。
如果您注意到了,您可能注意到需求没有指定 Order Service 在创建订单时应通知哪些应用程序。在我们的示例中,只有新的 Dispatcher Service 应用程序会对这些事件感兴趣。但是,未来可能会有更多应用程序订阅订单创建事件。这种设计的优点在于,您可以演进软件系统并添加更多应用程序,而完全不影响现有应用程序。例如,您可以添加一个 Mail Service,在用户接受订单时向他们发送电子邮件,而 Order Service 甚至不会意识到这一点。
这种类型的交互应该是异步的,可以使用 pub/sub 模型建模。图 10.1 说明了这种交互,并描述了接受、调度和更新订单的三个流程。它们在时间上是解耦的,并异步执行。您可能会注意到,将数据持久化到数据库的操作与产生事件的操作具有相同的编号步骤。这是因为它们属于同一个工作单元(事务),正如我将在本章后面解释的那样。
图 10.1 Order Service 和 Dispatcher Service 通过产生和消费事件进行异步和间接通信,这些事件由事件代理(RabbitMQ)收集和分发
在本章的其余部分,您将学习一些可用于为 Polar Bookshop 实现此事件驱动设计的技术和模式。RabbitMQ 将是负责收集、路由和分发消息给消费者的事件处理平台。图 10.2 突出了引入 Dispatcher Service 应用程序和 RabbitMQ 后 Polar Bookshop 系统的事件驱动部分。
图 10.2 在 Polar Bookshop 系统中,Order Service 和 Dispatcher Service 基于 RabbitMQ 分发的事件进行异步通信
下一节将介绍 RabbitMQ 的基本概念、其协议以及如何在本地环境中运行它。