使用RabbitMQ的事件驱动微服务

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
云原生网关 MSE Higress,422元/月
注册配置 MSE Nacos/ZooKeeper,118元/月
简介: 本文讲的是使用RabbitMQ的事件驱动微服务【编者的话】从传统的HTTP调用迁移到基于事件驱动的微服务架构改变了我们以往的思维方式,其有助于服务的解耦和伸缩,该文介绍了使用RabbitMQ作为消息基础设施实现的事件驱动系统以及处理队列的一些模式。
本文讲的是使用RabbitMQ的事件驱动微服务【编者的话】从传统的HTTP调用迁移到基于事件驱动的微服务架构改变了我们以往的思维方式,其有助于服务的解耦和伸缩,该文介绍了使用RabbitMQ作为消息基础设施实现的事件驱动系统以及处理队列的一些模式。

在微服务之间使用正确的模式进行通信有助于应用程序的伸缩以及解决大多数分布式系统的问题。我们一开始是采用直接的HTTP调用来通信的,但后来决定迁移到事件驱动系统上了。该系统改变了我们对于服务之间交互的思维方式,迫使我们采用可伸缩的模式并且提高了我们的适应能力。

我们从传统的HTTP通信迁移到基于事件的通信有几个原因,第一是对于服务的强制解耦,从我们对于HTTP的使用经验来看,我们的服务将会对于需要的每个服务都发起调用请求,这意味着原来的服务对于与其通信的每个服务都需要一个客户端库,该客户端库需要确保错误不会停止或者阻止正常的功能,并且与每个服务保持一致。

当我们扩展到超过20个服务时,维护客户端库就成为了一个长期、艰巨的过程。取代旧功能的新服务需要更新所有依赖项。由于所有的这些变动的组件,使得开发和部署的过程更长并更容易出错。

使用事件的另一个好处是服务不再需要编排功能了,消除了对于客户端的直接调用。服务可以自由地进出那些已经存在的地方,而无需更新客户端库或者添加新的HTTP调用。我们可以迅速部署那些监听事件的原型应用程序,而不用担心它们降低整个系统的可用性。

第三点,这个变化允许我们来实现全局模式,我们增加了速率限制和每个工作节点的超时时间,而不用在我们的每一个不同的客户端库(GitHub、AWS、内部服务等)分别实现。我们也能够很容易地实现一个 熔断器 模式,方式是通过切断一个事件的监听器,直到它健康为止,并且这只针对需要改变的工作节点,而不是所有的服务调用者。

最后,我们并不仅限于为那些长时间运行的工作节点持有一个开放的HTTP连接(可以断开连接或限制打开的套接字等)。

事件和任务

组成事件驱动系统的有两种不同的模式:事件和任务。

事件是当有事发生时告知已订阅的应用程序的那些通知。应用程序订阅某些事件并通过为此创建任务来响应,事件不会直接修改状态。

任务是修改状态的动作,唯一可以为一个给定的应用程序创建一个任务的是应用程序本身。这样的话,应用程序不能直接修改对方的状态。

当遇到事件和任务的命名时,严格的命名约定有助于我们保持一致性和清晰度。任务的命名以应用程序名称开始这样可以确保它们只处理预期的应用程序,接下来是模型,其状态会被任务修改,最后是一个描述性的“现在时”的动词。一个任务的例子是 api.user.authorize ,根据约定我们知道这个任务是由 api 服务处理的,该任务在 user 对象上执行一个 authorize 动作。

事件没有应用程序名称,因为它们可以被多个应用程序订阅。它们以模型名字开始,并以描述发生了什么的过去式动词结尾,一个事件的例子就是 user.authorized

将我们的应用程序分解为任务和事件迫使我们改变了思维方式,以前,如果我们想在收到付款后发送一封电子邮件,我们会添加一个SendGrid的调用到我们的支付服务里,非常简单明了。

但是对于我们崭新的事件系统,我们的支付服务发出了一个事件 org.payment.processed 。我们的email服务Pheidi,就会收到事件并创建了一个任务: pheidi.email.send ,我们现在需要按照反应而不是命令来考虑,不过如果我们需要额外的并且事件没有提供的数据(比如信用卡注册名字),我们仍然使用HTTP调用我们的账单服务。

在基于事件的解决方案伴随着优势的同时,还是有一些缺点的,因为你没有显式地调用服务,所以不能确定你发出的事件的响应是什么,这使得调试非常困难,因为系统更为复杂和难以理解了。

实现

我们使用RabbitMQ作为我们的消息系统,负责分发事件到监听它们的服务。任务也会通过RabbitMQ分发,所以可以跨一个应用的多个实例来做负载均衡。我们选择RabbitMQ是因为它易于部署,并且里边有准备就绪的供我们使用的NPM客户端模块。
tasks-and-events.png

我们创建了 Ponos 作为我们的统一的工作节点服务器来与RabbitMQ互动,这里有一些我们用来处理队列的模式。

指数退避算法

从一开始我们就在每个job上添加了指数退避算法,如果一个job抛出了一个可重复的错误,那么将会在过一段时间后重试。每个job都从一个最小的时间延迟开始,并且直到达到一个预定义的最大极限值(或者如果没有定义极限的话,那么就是无穷大),就增加一倍。

最初,我们希望job可以永远重试,考虑如果有什么东西被“卡住”的话,我们的警报系统将发出警告,我们中的一员将会成为拯救世界的白马骑士。这一开始会工作得很好,但随着我们添加了更多的工作,在队列中被“卡住”的条目的数量将会由于各种原因增长地很快。

最大重试限制&恢复功能

为了应对日益增长的队列,我们给每个队列添加了一个最大重试限制,如果job重试达到了给定的次数,我们会阻止它重试并运行恢复功能。恢复功能会记录并更新带了一个错误的数据库。现在,我们的警报系统将触发恢复功能,使我们能够优先解决问题,而不是将我们的队列进行备份。我们发现,采用快速失败机制并给我们的用户显示错误相对于让用户为这些故障等待很长一段时间而言,是更好的选择。

预读取

预读取是RabbitMQ Channel上的一个很重要的设置选项,没有这个,你的工作节点将利用队列里的所有可用的job,举例来说,如果你的应用程序经历了一个尖峰负载并入列了10000个job,所有的这10000个job都会被发送到工作节点上并存入内存里,这通常会导致机器崩溃,预读取限制了你的工作节点可以载入内存的job的数量,这篇来自RabbitMQ的 博客文章 会帮助我们确定实现预读取的最佳方式。

交换机和队列

为了实现事件和任务,我们使用以下的RabbitMQ结构。任务通过 sendToQueue  API来使用一个队列,由于任务只使用一个应用程序,我们不用为它们创建一个交换机。事件是一个更复杂的设置。事件的发布者会创建一个fanout交换机,每个订阅者会创建并绑定一个队列到那台交换机,这样允许任何应用程序都可以接收任何事件,而不会影响其他应用程序。

事务IDs

有一样可以帮助我们调试和反躬自省事件系统的东西是事务ID(TID)。每一个发送到RabbitMQ的job都会带上一个TID前缀,如果该job是一个事件或者任务的结果,那么就使用相同的TID,如果job不是从事件或者任务创建而来的,我们就产生一个新的TID。这有助于我们追踪哪些事件引发了哪些要运行的任务。

我们的事件驱动系统加快了我们的开发速度,使我们对于失败更具有弹性,并为我们的用户提高了我们产品的响应能力,我们希望这些技术将会一如既往地有助于你们系统的可伸缩性。

原文链接:Event-driven Microservices Using RabbitMQ(翻译:胡震)

原文发布时间为:2016-09-20

本文作者:胡震

本文来自云栖社区合作伙伴Dockerone.io,了解相关信息可以关注Dockerone.io。

原文标题:使用RabbitMQ的事件驱动微服务

相关实践学习
消息队列RocketMQ版:基础消息收发功能体验
本实验场景介绍消息队列RocketMQ版的基础消息收发功能,涵盖实例创建、Topic、Group资源创建以及消息收发体验等基础功能模块。
消息队列 MNS 入门课程
1、消息队列MNS简介 本节课介绍消息队列的MNS的基础概念 2、消息队列MNS特性 本节课介绍消息队列的MNS的主要特性 3、MNS的最佳实践及场景应用 本节课介绍消息队列的MNS的最佳实践及场景应用案例 4、手把手系列:消息队列MNS实操讲 本节课介绍消息队列的MNS的实际操作演示 5、动手实验:基于MNS,0基础轻松构建 Web Client 本节课带您一起基于MNS,0基础轻松构建 Web Client
相关文章
|
3月前
|
消息中间件 存储 Java
RabbitMQ 在微服务架构中的高级应用
【8月更文第28天】在微服务架构中,服务之间需要通过轻量级的通信机制进行交互。其中一种流行的解决方案是使用消息队列,如 RabbitMQ,来实现异步通信和解耦。本文将探讨如何利用 RabbitMQ 作为服务间通信的核心组件,并构建高效的事件驱动架构。
134 2
|
3月前
|
消息中间件 Java RocketMQ
微服务架构师的福音:深度解析Spring Cloud RocketMQ,打造高可靠消息驱动系统的不二之选!
【8月更文挑战第29天】Spring Cloud RocketMQ结合了Spring Cloud生态与RocketMQ消息中间件的优势,简化了RocketMQ在微服务中的集成,使开发者能更专注业务逻辑。通过配置依赖和连接信息,可轻松搭建消息生产和消费流程,支持消息过滤、转换及分布式事务等功能,确保微服务间解耦的同时,提升了系统的稳定性和效率。掌握其应用,有助于构建复杂分布式系统。
66 0
|
4月前
|
消息中间件 Java Kafka
基于事件驱动的微服务架构设计与实现
基于事件驱动的微服务架构设计与实现
|
5月前
|
消息中间件 Java Kafka
基于事件驱动的微服务架构设计与实现
基于事件驱动的微服务架构设计与实现
|
4月前
|
消息中间件 存储 缓存
架构设计篇问题之消息队列(MQ)在微服务系统中问题如何解决
架构设计篇问题之消息队列(MQ)在微服务系统中问题如何解决
|
4月前
|
消息中间件 供应链 Java
实现基于Spring Cloud的事件驱动微服务
实现基于Spring Cloud的事件驱动微服务
|
4月前
|
消息中间件 监控 Cloud Native
阿里云云原生生态强调事件驱动架构(EDA),借助EventBridge和EventMesh实现微服务间的高效协作。
【7月更文挑战第3天】阿里云云原生生态强调事件驱动架构(EDA),借助EventBridge和EventMesh实现微服务间的高效协作。EDA提升系统弹性和可维护性,促进业务敏捷性。实施路径包括事件模型设计、集成阿里云服务、开发事件处理器和监控优化。通过阿里云服务,开发者能轻松构建响应式、可扩展的云原生应用,加速创新并驱动数字化转型。
95 0
|
4月前
|
消息中间件 监控 Java
如何在Java中实现事件驱动的微服务架构
如何在Java中实现事件驱动的微服务架构
|
6月前
|
消息中间件 Java API
【微服务系列笔记】MQ消息可靠性
消息可靠性涉及防止丢失,包括生产者发送时丢失、未到达队列以及消费者消费失败处理后丢失。 确保RabbitMQ消息可靠性的方法有:开启生产者确认机制,确保消息到达队列;启用消息持久化以防止未消费时丢失;使用消费者确认机制,如设置为auto,由Spring确认处理成功后ack。此外,可开启消费者失败重试机制,多次失败后将消息投递到异常交换机。
111 1
|
6月前
|
消息中间件 缓存 API