Rabbitmq八股文

为什么要用MQ

解耦系统组件

  • 系统间依赖关系简化:在没有使用 MQ 的情况下,不同的系统组件之间往往是直接调用,比如模块 A 调用模块 B,模块 B 再调用模块 C,这样会形成紧密的依赖关系。一旦某个模块的接口发生变化或者出现故障,很容易影响到其他与之关联的模块。而引入 MQ 后,各个模块只需与 MQ 进行交互,发送或接收消息,模块之间不再直接依赖,例如电商系统中,订单模块、库存模块、物流模块等可以通过 MQ 传递消息来协作,订单模块生成订单后将消息发送到 MQ,库存模块和物流模块从 MQ 中获取相关消息并各自处理,它们之间的耦合度大大降低。
  • 便于系统的扩展和维护:当业务发展需要新增或修改某个功能模块时,只需关注该模块与 MQ 的交互逻辑,不用过多考虑对其他模块的影响,降低了系统维护和扩展的复杂性,提升了整体的灵活性。

异步处理提高性能

  • 任务并行化:一些耗时的操作如果采用同步方式执行,会让用户长时间等待,影响用户体验。例如在用户注册时,除了完成基本的注册信息写入数据库操作外,可能还需要发送欢迎邮件、短信通知等操作。若采用同步方式,要等这些额外操作都完成后才返回注册成功结果给用户,耗时较长。通过 MQ 可以将这些操作变成异步,注册模块把相关任务消息发送到 MQ 后立即返回注册成功提示给用户,而发送邮件、短信的服务从 MQ 中获取消息后再异步执行,多个任务并行处理,提高了系统的整体响应速度。
  • 提升系统吞吐量:系统可以快速处理一些核心业务逻辑并将其他非核心的、耗时的任务交给 MQ 去排队调度,让系统能够同时处理更多的请求,提升了单位时间内能够处理的业务量,增强了系统的并发处理能力。

流量削峰填谷

  • 应对突发高流量:在电商促销活动(如 "双十一""618" 等)期间,短时间内会有大量的用户请求涌入系统,比如大量的下单操作,如果系统直接按照这个峰值流量去处理,很可能会导致服务器资源耗尽、系统崩溃等情况。通过 MQ,可以将这些突发的大量请求先缓冲到队列中,按照系统能够承受的处理速度从队列中慢慢取出消息进行处理,就像把洪水先引入水库,再通过合理的放水速度来调控一样,避免了系统被瞬间的高流量冲垮,保障了系统的稳定性。
  • 均衡资源利用:在低谷期,系统可以慢慢处理队列中积压的任务,使系统资源在不同时段都能得到较为均衡的利用,不至于在高峰期资源紧张,低谷期又闲置浪费。

保障消息可靠传递

  • 确保消息不丢失:MQ 本身具备多种机制来保障消息的可靠传递。例如 RabbitMQ 有消息确认机制,生产者发送消息后可以等待 MQ 的确认回复,知道消息确实被接收并存储了;消费者获取消息后也可以进行手动确认,只有确认后消息才会从队列中删除,这样可以防止消息因为网络故障、系统重启等原因丢失,在一些对数据准确性要求较高的业务场景(如金融交易记录传递等)中非常重要。
  • 支持消息持久化:MQ 可以将消息持久化存储到磁盘等介质上,即使遇到服务器意外断电等情况,恢复后依然能够读取之前存储的消息继续进行处理,进一步提升了消息传递的可靠性。

交换机的四种类型

Fanout(扇出)类型

  • 消息转发规则:当生产者将消息发送到 Fanout 类型的交换机时,该交换机会把接收到的消息广播式地转发到所有与之绑定的队列中,不考虑消息本身的任何内容属性,只要队列绑定了这个交换机,就会收到消息,就像广播电台广播节目,所有收听该电台频率的收音机都能接收到声音一样。
  • 应用场景:适用于需要将消息同时通知到多个不同的接收方,实现一对多的广播消息传递。例如在一个新闻发布系统中,当有新的新闻资讯产生时,通过 Fanout 交换机将消息发送到各个不同的客户端展示队列(如网页端展示队列、移动端展示队列等),让不同的客户端都能获取消息进行展示,快速实现消息的全面广播。

Direct(直连)类型

  • 消息转发规则:消息在发送到 Direct 类型交换机时会带有一个路由键(Routing Key),交换机根据这个路由键精准地将消息转发到与之绑定且路由键完全匹配的队列中。也就是说,只有队列绑定的路由键和消息携带的路由键一致时,该队列才能接收到消息,相当于一对一的精准投递。
  • 应用场景:常用于有明确的消息分类和对应接收者的场景,使得消息能够准确地到达指定的处理模块。比如在电商系统中,订单相关的消息可以根据不同的业务类型(如 "下单""支付""退款" 等作为不同的路由键)发送到 Direct 交换机,然后不同的业务模块对应的队列(下单处理队列、支付处理队列、退款处理队列等)通过绑定相应的路由键来获取自己关心的消息进行处理,确保消息准确无误地流向对应的业务逻辑。

Topic(主题)类型

  • 消息转发规则:消息同样带有路由键,不过路由键可以由多个单词组成,中间用 "." 分隔。队列在绑定交换机时可以使用通配符("*" 匹配一个单词,"#" 匹配零个或多个单词)来确定接收哪些路由键对应的消息。这样交换机就可以根据队列绑定的规则以及消息的路由键情况灵活地进行消息转发,相比 Direct 类型更具灵活性。
  • 应用场景:在一些业务场景中,消息需要按照一定的主题或规则进行分类和分发时比较适用。例如在物联网系统中,设备发送的消息路由键可以设置为类似 "device_type.location.status" 的格式,不同的监控模块对应的队列通过合适的通配符绑定(如 "*.livingroom.#" 可以接收客厅相关设备的各种状态消息)来获取消息进行分析处理,能满足更复杂的消息匹配和分发需求。

Headers(头信息)类型

  • 消息转发规则:该类型交换机不依赖路由键来转发消息,而是基于消息的头信息(Headers)进行匹配转发。生产者在发送消息时可以添加多个头信息键值对,队列在绑定交换机时也设定相应的头信息匹配规则(比如匹配所有头信息、部分头信息或者特定的头信息组合等),当消息的头信息满足队列绑定的匹配规则时,交换机就会将消息转发到该队列。不过这种类型相对比较复杂,使用频率比前三种要低一些。
  • 应用场景:在需要根据消息附带的多种属性、元数据等进行灵活匹配和转发的特殊场景中会用到。例如在一个复杂的企业级系统中,有多种不同来源、不同优先级、不同格式要求的消息,通过在消息头中设置诸如 "来源部门""优先级""消息格式" 等头信息,然后不同的业务模块根据自己关注的头信息组合来绑定交换机,从而获取符合要求的消息进行处理。

RabbitMQ 有以下几种常见的工作模式:

简单模式(Simple Mode)

  • 模式特点:由一个生产者(Producer)、一个队列(Queue)和一个消费者(Consumer)组成。生产者将消息发送到队列中,消费者从队列里获取消息并进行处理。是最基础、最简单的消息传递模式,适用于简单的一对一消息传递场景。
  • 示例应用场景:比如在一个小型的日志收集系统中,某个应用程序作为生产者将产生的日志消息发送到队列,而日志处理的服务作为消费者从队列获取日志并存储到数据库等操作。

工作队列模式(Work Queue Mode)

  • 模式特点:包含一个生产者和多个消费者,生产者将消息发送到同一个队列。多个消费者共同监听该队列,消息会被平均分配给这些消费者(或者按照能者多劳的方式分配,取决于具体配置),实现多个消费者并行处理消息来分担任务负载,常用于需要对任务进行并行处理以提高效率的场景。
  • 示例应用场景:在邮件发送系统中,有大量邮件需要发送,生产者将邮件发送任务消息放入队列,多个消费者(邮件发送服务实例)可以从队列获取任务来分别发送邮件,加快邮件发送的整体速度。

发布 / 订阅模式(Publish/Subscribe Mode)

  • 模式特点:有一个生产者以及多个消费者,不过这里的消息传递不是直接到队列,而是生产者将消息发送到一个交换机(Exchange),交换机的类型为 Fanout(扇出),它会把接收到的消息广播式地转发到所有与之绑定的队列,然后每个队列对应的消费者都能接收到消息,实现一对多的消息广播。
  • 示例应用场景:在一个新闻发布系统中,新闻内容生产者将新发布的新闻消息发送到交换机,多个不同的客户端(比如网页端、移动端等不同的展示应用作为消费者)对应的队列都绑定了该交换机,它们都能获取到新闻消息并展示给用户。

路由模式(Routing Mode)

  • 模式特点:同样是生产者将消息发送到交换机,但此时交换机类型为 Direct(直连)。消息在发送时会带有一个路由键(Routing Key),交换机根据这个路由键将消息精准地转发到与之绑定且路由键匹配的队列中,不同的队列可以绑定不同的路由键来有选择性地接收消息,实现更灵活的消息定向分发。
  • 示例应用场景:在电商系统中,订单处理相关的消息根据不同的业务类型(比如下单、支付、退款等作为不同路由键)发送到交换机,然后不同的业务模块对应的队列(下单处理队列、支付处理队列、退款处理队列等)通过绑定相应路由键来获取自己关心的消息进行处理。

主题模式(Topic Mode)

  • 模式特点:生产者发送消息到交换机(类型为 Topic),消息的路由键可以是由多个单词组成,中间用 "." 分隔,队列在绑定交换机时可以使用通配符(比如 "*" 匹配一个单词,"#" 匹配零个或多个单词)来确定接收哪些路由键对应的消息,这种模式比路由模式更加灵活,能根据一定规则匹配多种消息情况。
  • 示例应用场景:在一个物联网设备监控系统中,设备发送的消息路由键可以设置为类似 "device_type.location.status" 的格式,不同的监控模块对应的队列通过合适的通配符绑定(比如 "*.livingroom.#" 可以接收客厅相关设备的各种状态消息)来获取消息进行分析处理。

如何保证消息的可靠性

消息发送阶段可能出现的问题及应对措施

  • 发送消息时丢失的情况分析

    • 生产者发送消息时连接 MQ 失败:这可能由于网络故障、MQ 服务端不可用等原因导致,使得消息一开始就无法成功发往 MQ。
    • 生产者发送消息到达 MQ 后未找到 Exchange:说明在 MQ 中配置的 Exchange(交换机)存在问题,可能是 Exchange 名称有误或者没有正确创建,导致消息无法基于 Exchange 规则进一步流转。
    • 生产者发送消息到达 MQ 的 Exchange 后,未找到合适的 Queue:意味着 Exchange 与 Queue(队列)之间的绑定关系出现差错,消息不能被准确投递到对应的队列中去。
    • 消息到达 MQ 后,处理消息的进程发生异常:即使消息顺利到达了 MQ,但如果 MQ 内部处理该消息的相关进程出现意外情况,同样会影响消息的正常流转。

生产者端保障消息可靠性的相关机制

MQ 端保障消息可靠性的措施

复制代码
@RabbitListener(queuesToDeclare = @Queue(
        name = "lazy.queue",
        durable = "true",
        arguments = @Argument(name = "x-queue-mode", value = "lazy")
))
public void listenLazyQueue(String msg){
    log.info("接收到 lazy.queue的消息:{}", msg);
}

消费者端保障消息可靠性的措施

复制代码
spring:
  rabbitmq:
    listener:
      simple:
        acknowledge-mode: auto
  • 生产者重试机制

    可以在配置文件(如 yaml)中进行定义,设置重试次数以及下次重试的时间间隔。不过,需要留意的是,这种同步阻塞重试的方式不太推荐使用,因为它可能会阻塞后续流程,拖慢整体的消息发送效率,对系统的实时性产生不良影响。

  • 生产者确认机制

    当生产者将消息发送到 MQ 后,MQ 会向生产者发送回执,包含 ack(确认)、nack(否定确认)。但在实际应用中,大多只处理 nack 情况。总体而言,这种机制也并非尽善尽美,并且倘若生产者端出现问题,往往是编码方面的问题,所以通常不会将其作为重点来解决消息可靠性问题。

  • 数据持久化
    将 Exchange、Queue、Message 进行持久化操作,这样可以保证在 MQ 出现意外情况(比如突然宕机等)时,数据不会丢失,消息依然能够在系统恢复后按照原有的规则进行处理和流转。

  • 惰性队列的应用

    • 原理及优势:惰性队列在接收到消息后,会直接将消息存入磁盘而非内存,只有当消费者要消费消息时,才会从磁盘中读取并加载到内存,也就是采用懒加载的方式。这种方式的优势在于能够支持存储数百万条的消息,极大地提升了消息存储能力以及应对突发情况的能力。
    • 配置方法示例
  • 消费者确认机制
    在 Spring 框架下,可以通过如下配置来实现消费者确认机制。例如在配置文件中设置:

  • 消费者重传机制:和生产者的重传机制类似,不过同样需要合理设置相关参数,避免因不合理的重传造成资源浪费或者消息处理混乱等问题。

  • 业务幂等性判断(避免消息的重复消费)

    • 利用唯一消息 id :可以通过配置消息转换器来自动创建消息 id,用于区分不同的消息,并且在业务逻辑中基于该 ID 来判断是否为重复消息,从而避免重复消费带来的问题。示例配置如下:
    复制代码
      @Bean
      public MessageConverter messageConverter(){
          // 1.定义消息转换器
          Jackson2JsonMessageConverter jjmc = new Jackson2JsonMessageConverter();
          // 2.配置自动创建消息id,用于识别不同消息,也可以在业务中基于ID判断是否是重复消息
          jjmc.setCreateMessageIds(true);
          return jjmc;
      }
  • 根据实际业务判断:除了依赖消息 id 外,还需要结合具体的业务场景和业务规则,从更全面的角度来判断消息是否重复,进而保证消息消费的准确性和可靠性。

相关推荐
V+zmm101343 分钟前
基于微信小程序的短文写作竞赛管理系统
java·微信小程序·小程序·毕业设计·springboot
哟哟耶耶3 分钟前
knowledge-vscode中配置java环境(JDK-8下载,配置 Maven 并创建项目)
java·开发语言
IDRSolutions_CN6 分钟前
如何在 Java 中查找 PDF 页面大小(教程)
java·经验分享·pdf·软件工程·团队开发
helloworld_工程师10 分钟前
SpringBoot整合高德地图完成天气预报功能
java·前端·后端
tmacfrank16 分钟前
排序算法总结
java·算法·排序算法
最懒的菜鸟32 分钟前
spring boot jwt生成token
java·前端·spring boot
瑜舍44 分钟前
Apache Tomcat RCE漏洞(CVE-2025-24813)
java·tomcat·apache
un_fired1 小时前
【Spring AI】基于专属知识库的RAG智能问答小程序开发——功能优化:用户鉴权
java·人工智能·spring
martian6651 小时前
Java并发编程从入门到实战:同步、异步、多线程核心原理全解析
java·开发语言
计算机学姐1 小时前
基于SpringBoot的电影售票系统
java·vue.js·spring boot·后端·mysql·spring·intellij-idea