提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 前言
- [1. MQ的作⽤及应⽤场景](#1. MQ的作⽤及应⽤场景)
- [2. 了解过哪些MQ,以及他们的区别](#2. 了解过哪些MQ,以及他们的区别)
- [3. 介绍下RabbitMQ的核⼼概念及⼯作流程](#3. 介绍下RabbitMQ的核⼼概念及⼯作流程)
- [4. RabbitMQ如何保证消息的可靠性](#4. RabbitMQ如何保证消息的可靠性)
- [5. RabbitMQ如何保证消息的顺序性](#5. RabbitMQ如何保证消息的顺序性)
- [6. 如何保证消息消费时的幂等性](#6. 如何保证消息消费时的幂等性)
- [7. RabbitMQ有哪些特性](#7. RabbitMQ有哪些特性)
- [8. 介绍下RabbitMQ的死信队列](#8. 介绍下RabbitMQ的死信队列)
- [9. 介绍下RabbitMQ的延迟队列](#9. 介绍下RabbitMQ的延迟队列)
- [10. 介绍下RabbitMQ的⼯作模式](#10. 介绍下RabbitMQ的⼯作模式)
- [11. 消息积压的原因, 如何处理](#11. 消息积压的原因, 如何处理)
- [12. RabbitMQ是推模式还是拉模式](#12. RabbitMQ是推模式还是拉模式)
-
- [12.1 spingboot实现推拉模式](#12.1 spingboot实现推拉模式)
- [12.2 使用Rabbitmq的SDK实现推拉模式](#12.2 使用Rabbitmq的SDK实现推拉模式)
- [12.3 总结](#12.3 总结)
- 总结
前言
其中通⽤问题占: 53%, RabbitMQ : 24.32% RocketMQ: 19.25%, Kafka:3.38%, 其他
0.05%
问题 频率
可靠性保证 74
如何保证消息消费的幂等性 43
MQ的作⽤及应⽤场景 39
Kafka, RabbitMQ, RocketMQ区别 35
MQ如何保证消息的顺序 27
死信队列 23
延迟队列 20
MQ积压的原因, 如何处理 9
RabbitMQ的基本架构, 核⼼流程简单介绍 7
RabbitMQ⼯作模式 5
Rabbitmq的特性 3
RabbitMQ是推送式还是拉取式 1
1. MQ的作⽤及应⽤场景
类似问题:
项⽬什么场景下使⽤到了MQ, 为什么需要MQ
RabbitMQ 的作⽤?使⽤场景有哪些
RabbitMQ的主要应⽤场景
消息队列解耦应⽤程序的例⼦
消息队列的应⽤场景
为什么说消息队列可以削峰
消息队列就是接收消息并转发消息
消息队列(MQ)是⼀种应⽤程序间的通信⽅法,它允许系统组件以异步的⽅式进⾏交互, 在不同的应⽤场景下可以展现不同的作⽤, 常⻅的应⽤场景如下:
• 异步解耦: 在业务流程中, ⼀些操作可能⾮常耗时, 但并不需要即时返回结果. 可以借助MQ把这些操
作异步化, ⽐如 ⽤⼾注册后发送注册短信或邮件通知, 可以作为异步任务处理, ⽽不必等待这些操作
完成后才告知⽤⼾注册成功.

• 流量削峰: 在访问量剧增的情况下, 应⽤仍然需要继续发挥作⽤, 但是是这样的突发流量并不常⻅. 如果以能处理这类峰值为标准⽽投⼊资源,⽆疑是巨⼤的浪费. 使⽤MQ能够使关键组件⽀撑突发访问压⼒, 不会因为突发流量⽽崩溃. ⽐如秒杀或者促销活动, 可以使⽤MQ来控制流量, 将请求排队, 然后系统根据⾃⼰的处理能⼒逐步处理这些请求.

• 异步通信: 在很多时候应⽤不需要⽴即处理消息, MQ提供了异步处理机制, 允许应⽤把⼀些消息放⼊MQ中, 但并不⽴即处理它,在需要的时候再慢慢处理.
• 消息分发: 当多个系统需要对同⼀数据做出响应时, 可以使⽤MQ进⾏消息分发. ⽐如⽀付成功后, ⽀
付系统可以向MQ发送消息, 其他系统订阅该消息, ⽽⽆需轮询数据库.
• 延迟通知: 在需要在特定时间后发送通知的场景中, 可以使⽤MQ的延迟消息功能, ⽐如在电⼦商务平台中,如果⽤⼾下单后⼀定时间内未⽀付,可以使⽤延迟队列在超时后⾃动取消订单
理解说明就可以了
2. 了解过哪些MQ,以及他们的区别
类似问题:
了解过哪些MQ, 与其他同类产品的对⽐
kafka 和 RabbitMQ的对⽐
对⽐其他消息队列,不同mq分别⽤在什么场景
kafka和rocketmq⽐较
消息队列除了使⽤RabbitMQ,可以⽤RocketMQ吗?
⽬前业界有很多的MQ产品, 例如RabbitMQ, RocketMQ, ActiveMQ, Kafka, ZeroMQ等,
简单介绍其中3种
- Kafaka
Kafka⼀开始的⽬的就是⽤于**⽇志收集和传输,功能简单** ,追求⾼吞吐量, 性能卓越, 单机吞吐达到⼗万级, 在⽇志领域⽐较成熟, 功能较为简单, 主要⽀持简单的 MQ 功能. 适合⼤数据处理, ⽇志聚合, 实时分析等场景 - RabbitMQ
采⽤Erlang语⾔开发, MQ 功能⽐较完备, 且⼏乎⽀持所有主流语⾔, 开源提供的界⾯也⾮常友好, 性能较好, 吞吐量能达到万级 , 社区活跃度较⾼,⽂档更新频繁 , ⽐较适合中⼩型公司, 数据量没那么⼤, 且并发没那么⾼的场景. - RocketMQ
采⽤Java语⾔开发, 由阿⾥巴巴开源, 后捐赠给了Apache. 在可⽤性, 可靠性以及稳定性等⽅⾯都⾮常出⾊ , 吞吐量能达到⼗万级 , 在Alibaba集团内部⼴泛使⽤, 但⽀持的客⼾端语⾔不多, 产品较新⽂档较少,且社区活跃度⼀般. 适合于⼤规模分布式系统, 可靠性要求⾼, 且并发⼤的场景, ⽐如互联⽹⾦融.这些消息队列, 各有侧重, 没有好坏, 只有适合不适合, 在实际选型时, 需要结合⾃⾝需求以及MQ产品特征, 综合考虑
3. 介绍下RabbitMQ的核⼼概念及⼯作流程
相关⾯试题:
RabbitMQ的核⼼流程简单介绍⼀下
讲下RabbitMQ的结构
我们先来看RabbitMQ的⼯作流程


RabbitMQ是⼀个消息中间件, 也是⼀个⽣产者消费者模型. 它负责接收, 存储并转发消息. 根据⼯作流程图, 来介绍它的核⼼概念:
• Producer: ⽣产者, 向RabbitMQ发送消息
• Consumer: 消费者, 从RabbitMQ接收消息
• Broker: 消息队列服务器或服务实例, 也就是RabbitMQ Server,接收消息和转发消息
• Connection: ⽹络连接, 它允许客⼾端与 RabbitMQ通信
• Channel: 连接⾥的⼀个虚拟通道, 发送或者接收消息都是通过通道进⾏的.
• Exchange: 交换机. 负责接收⽣产者发送的消息, 并根据路由算法和规则将消息路由到⼀个或多个队
列,与队列是有绑定关系的
• Queue: 消息队列, 存储消息直到它们被消费者消费
虚拟机virtual host就相当于一个数据库了
生产者和消费者都是客户端
Rabbitmq就是服务端
⼯作流程:
- 创建连接: Producer 连接到RabbitMQBroker, 建⽴⼀个连接(Connection), 开启⼀个信道(Channel)
- 声明交换机和队列,以及绑定规则: Producer 声明⼀个交换机(Exchange)和队列, 并绑定Queue到
Exchange - 发布消息: Producer 发送消息⾄RabbitMQ Broker
- 消息存储: RabbitMQ Broker 接收消息, 并存⼊相应的队列(Queue)中, 如果未找到相应的队列, 则根据⽣产者的配置, 选择丢弃或者退回给⽣产者.
- 消费消息: 消费者监听Queue, 当消息到达时, 从Queue中获取消息, 处理后, 向RabbitMQ发送消息确认
- 消息删除: 消息被确认后, RabbitMQ 会把消息从Queue中删除
4. RabbitMQ如何保证消息的可靠性
相关⾯试题:
RabbitMQ消息丢失原因及其解决⽅案
如何保证消息不丢失
消息写⼊失败怎么办
消息消费失败如何处理
MQ的主动ack和被动ack有什么区别
RabbitMQ如何解决数据丢失问题, 如何保证⼀致性
消息队列怎么保证消费者的消息不丢失的?
从以下⼏个⽅⾯来回答
-
发送⽅投递可靠性
-
RabbitMQ可靠性
-
消费者可靠性

从这个图中, 可以看出, 消息可能丢失的场景以及解决⽅案:
-
⽣产者将消息发送到 RabbitMQ Broker失败,Producer-》Exchange
a. 可能原因: ⽹络问题,交换机不存在等
b. 解决办法: 参考[发送⽅确认-confirm确认模式]
-
消息在交换机中⽆法路由到指定队列:Exchange-》Queue
a. 可能原因: 代码或者配置层⾯错误, 导致消息路由失败,routingkey不匹配,队列不存在
b. 解决办法: 参考[发送⽅确认-return模式]
-
消息队列⾃⾝数据丢失
a. 可能原因: 消息到达RabbitMQ之后, RabbitMQ Server 宕机导致消息丢失.服务器挂了
b. 解决办法: 参考[持久性]. 开启 RabbitMQ持久化, 就是消息写⼊之后会持久化到磁盘, 如果
RabbitMQ 挂了, 恢复之后会⾃动读取之前存储的数据. (极端情况下, RabbitMQ还未持久化就挂
了, 可能导致少量数据丢失, 这个概率极低, 也可以通过集群的⽅式提⾼可靠性)
持久化:交换机持久化,队列持久化,消息持久化(必须基于队列持久化)
如果是搭建集群,主节点挂掉了--》可以使用仲裁队列
-
消费者异常, 导致消息丢失
a. 可能原因: 消息到达消费者, 还没来得及消费, 消费者宕机. 消费者逻辑有问题.
b. 解决办法: 参考[消息确认]. RabbitMQ 提供了 消费者应答机制 来使 RabbitMQ 能够感知
到消费者是否消费成功消息. 默认情况下消费者应答机制是⾃动应答的, 可以开启⼿动确认, 当消
费者确认消费成功后才会删除消息, 从⽽避免消息丢失. 除此之外, 也可以配置重试机制(参考下
⼀章节), 当消息消费异常时, 通过消息重试确保消息的可靠性
消息确认:自动确认(消息到达消费者之后,如果消费者逻辑有问题,处理失败,会确认,会导致消息丢失),手动确认
5. RabbitMQ如何保证消息的顺序性
相关⾯试题:
RabbitMQ怎么保证消息的顺序性?
如何保证消息能够有序消费
• 单队列单消费者
• 分区消费
• 事务和消息确认机制
• 业务逻辑控制, ⽐如消费端内部实现消息排序逻辑
• ...
消息顺序性保障分为: 局部顺序性保证和全局顺序性保证
局部顺序性通常指的是在单个队列内部保证消息的顺序. 全局顺序性是指在多个队列或多个消费者之间保证消息的顺序.
在实际应⽤中, 全局顺序性很难实现, 可以考虑使⽤业务逻辑来保证顺序性, ⽐如在消息中嵌⼊序列号,并在消费端进⾏排序处理. 相对⽽⾔, 局部顺序性更常⻅, 也更容易实现.
RabbitMQ作为⼀个分布式消息队列, 主要优化的是吞吐量和可⽤性, ⽽不是严格的顺序性保证. 如果业务场景确实需要严格的消息顺序, 可能需要在应⽤层⾯进⾏额外的设计和实现
接下来说⼀下消息的顺序性保证的常⻅策略
全局性顺序保证
1.一个字段或者属性,标识消息顺序,设置序号,这个序号可以进行排序,可以知道上一个消息是谁,谁消费我之后,我才可以消费,这个消息来的时候,可以去查一下上一条消息(比如数据库),看看这个消息消费完没有--》保证顺序
2.设置缓存,来排序---》TCP协议,参考TCP的顺序性的实现
局部顺序性:
-
单队列单消费者
最简单的⽅法是使⽤单个队列, 并由单个消费者进⾏处理. 同⼀个队列中的消息是先进先出的, 这是
RabbitMQ来帮助我们保证的.
-
分区消费
单个消费者的吞吐太低了, 当需要多个消费者以提⾼处理速度时, 可以使⽤分区消费. 把⼀个队列分割成多个分区, 每个分区由⼀个消费者处理, 以此来保持每个分区内消息的顺序性
⽐如⽤⼾修改资料后, 发送⼀条⽤⼾资料消息. 消费者在处理时, 需要保证消息发送的先后顺序
同一个用户id的很多消息,就在同一个分区,然后这个分区就由一个消费者处理
但这种场合并不需要保证全局顺序. 只需要保证同⼀个⽤⼾的消息顺序消费就可以. 这时候就可以采
⽤把消费按照⼀定的规则, 分为多个区, 每个分区由⼀个消费者处理
RabbitMQ本⾝并不⽀持分区消费, 需要业务逻辑去实现, 或者借助spring-cloud-stream来实现
参考: https://docs.spring.io/spring-cloud-stream/reference/rabbit/rabbit_partitions.html
我们可以自己来实现,发送消息的时候,路由到不同的队列中,一个用于id就路由到一个消息队列中就可以了
-
消息确认机制
使⽤⼿动消息确认机制, 消费者在处理完⼀条消息后, 显式地发送确认, 这样RabbitMQ才会移除并继续发送下⼀条消息.
-
业务逻辑控制
在某些情况下, 即使消息乱序到达, 也可以在业务逻辑层⾯实现顺序控制. ⽐如通过在消息中嵌⼊序列
号, 并在消费时根据这些信息来处理RabbitMQ本⾝并不保证全局的严格顺序性, 特别是在分布式系统中. 在实际应⽤开发中, 根据具体的业务需求, 可能需要结合多种策略来实现所需要的顺序保证
---》嵌入序列号
6. 如何保证消息消费时的幂等性
相关问题:
RabbitMQ怎么保证消息不重复消费
消息或请求存在重复消费问题吗? 是怎么解决的?
怎么解决MQ重复消费的问题
• 全局唯⼀ID
• 业务逻辑判断
• ...
幂等性是数学和计算机科学中某些运算的性质, 它们可以被多次应⽤, ⽽不会改变初始应⽤的结果
在应⽤程序中, 幂等性就是指对⼀个系统进⾏重复调⽤(相同参数), 不论请求多少次, 这些请求对系统的影响都是相同的效果.
⽐如数据库的 select 操作. 不同时间两次查询的结果可能不同, 但是这个操作是符合幂等性的. 幂等性指的是对资源的影响, ⽽不是返回结果. 查询操作对数据资源本⾝不会产⽣影响, 之所以结果不同, 可能是因为两次查询之间有其他操作对资源进⾏了修改
⽐如 i++ 这个操作, 就是⾮幂等性的. 如果调⽤⽅没有控制好逻辑, ⼀次流程重复调⽤好⼏次, 结果
就会不同.
MQ的幂等性介绍:发送网络故障,服务器异常--》没有可靠性的确认--》可能会重发消息
对于MQ⽽⾔, 幂等性是指同⼀条消息, 多次消费, 对系统的影响是相同的.
⼀般消息中间件的消息传输保障分为三个层级.
- At most once:最多⼀次. 消息可能会丢失,但绝不会重复传输.
- At least once:最少⼀次. 消息绝不会丢失,但可能会重复传输.
- Exactly once:恰好⼀次. 每条消息肯定会被传输⼀次且仅传输⼀次
RabbitMQ⽀持"最多⼀次"和"最少⼀次".
对于"恰好⼀次", ⽬前RabbitMQ还做不到, 不仅是RabbitMQ, ⽬前市⾯上主流的消息中间件, 都做不到这⼀点.
在业务使⽤中, 对于可靠性要求⽐较⾼的场景, 建议使⽤"最少⼀次", 以防⽌消息丢失. "最多⼀次" 会因为消息发送过程中, ⽹络问题, 消费出现异常等种种原因, 导致消息丢失
MQ消费者的幂等性的解决⽅法, ⼀般有以下⼏种:
一:全局唯⼀ID
- 为每条消息分配⼀个唯⼀标识符, ⽐如UUID或者MQ消息中的唯⼀ID,但是⼀定要保证唯⼀性.
- 消费者收到消息后, 先⽤该id判断该消息是否已经消费过, 如果已经消费过, 则放弃处理.
- 如果未消费过, 消费者开始消费消息, 业务处理成功后, 把唯⼀ID保存起来(数据库或Redis等)
可以使⽤Redis 的原⼦性操作setnx来保证幂等性, 将唯⼀ID作为key放到redis中 (SETNX
messageID 1) . 返回1, 说明之前没有消费过, 正常消费. 返回0, 说明这条消息之前已消费过, 抛
弃.
把id存在redis中,那么就通过redis有没有这个id来判断这个消息有没有消费了
二:业务逻辑判断
在业务逻辑层⾯实现消息处理的幂等性.
例如: 通过检查数据库中是否已存在相关数据记录, 或者使⽤乐观锁机制来避免更新已被其他事务更改的数据, 再或者在处理消息之前, 先检查相关业务的状态, 确保消息对应的操作尚未执⾏, 然后才进⾏处理, 具体根据业务场景来处理
不用解释什么是幂等性
7. RabbitMQ有哪些特性
发送⽅消息确认
• 持久化
• 消费端消息确认
• 重试机制
• TTL
• 死信队列
• 仲裁队列
• 延迟队列
8. 介绍下RabbitMQ的死信队列
类似问题:
RabbitMQ的死信队列以及应⽤场景
从以下⽅⾯来回答:
• 死信队列的概念
• 死信的来源
• 死信队列的应⽤场景
- 死信队列的概念
死信(Dead Letter)是消息队列中的⼀种特殊消息, 它指的是那些⽆法被正常消费或处理的消息. 在消息队列系统中, 如RabbitMQ, 死信队列⽤于存储这些死信消息 - 死信的来源
- 消息过期: 消息在队列中存活的时间超过了设定的TTL
- 消息被拒绝: 消费者在处理消息时, 可能因为消息内容错误, 处理逻辑异常等原因拒绝处理该消息. 如果拒绝时指定不重新⼊队(requeue=false), 消息也会成为死信.
- 队列满了: 当队列达到最⼤⻓度, ⽆法再容纳新的消息时, 新来的消息会被处理为死信
- 死信队列的应⽤场景
对于RabbitMQ来说, 死信队列是⼀个⾮常有⽤的特性. 它可以处理异常情况下,消息不能够被消费者正确消费⽽被置⼊死信队列中的情况, 应⽤程序可以通过消费这个死信队列中的内容来分析当时所遇到的异常情况, 进⽽可以改善和优化系统.
⽐如: ⽤⼾⽀付订单之后, ⽀付系统会给订单系统返回当前订单的⽀付状态
为了保证⽀付信息不丢失, 需要使⽤到死信队列机制. 当消息消费异常时, 将消息投⼊到死信队列中, 由订单系统的其他消费者来监听这个队列, 并对数据进⾏处理(⽐如发送⼯单等,进⾏⼈⼯确认).
场景的应⽤场景还有:
• 消息重试:将死信消息重新发送到原队列或另⼀个队列进⾏重试处理.
• 消息丢弃:直接丢弃这些⽆法处理的消息,以避免它们占⽤系统资源.
• ⽇志收集:将死信消息作为⽇志收集起来,⽤于后续分析和问题定位
• ttl+死信队列:实现延迟队列
9. 介绍下RabbitMQ的延迟队列
类似问题:
rabbitmq延迟队列的实现
从以下三个⽅⾯来回答
- 概念
- 应⽤场景
- 实现⽅式
介绍下RabbitMQ的延迟队列
延迟队列是⼀个特殊的队列, 消息发送之后, 并不⽴即给消费者, ⽽是等待特定的时间, 才发送给消费者.
延迟队列的应⽤场景有很多, ⽐如:
-
订单在⼗分钟内未⽀付⾃动取消
-
⽤⼾注册成功后, 3天后发调查问卷
-
⽤⼾发起退款, 24⼩时后商家未处理, 则默认同意, ⾃动退款
-
...
但RabbitMQ本⾝并没直接实现延迟队列, 通常有两种⽅法:
-
TTL+死信队列组合的⽅式
-
使⽤官⽅提供的延迟插件实现延迟功能
⼆者对⽐:
- 基于死信实现的延迟队列
a. 优点: 1) 灵活不需要额外的插件⽀持
b. 缺点: 1) 存在消息顺序问题 :就是先去扫描队列的对头是否过期,对头过期了,才会去判断下一个消息是否过期,不会去轮询整个队列消息,所以死信队列的消息的第一个消息并不是一定是最早的过期的
- 需要额外的逻辑来处理死信队列的消息, 增加了系统的复杂性:死信队列,死信交换机
- 基于插件实现的延迟队列
a. 优点: 1) 通过插件可以直接创建延迟队列, 简化延迟消息的实现. 2) 避免了DLX的时序问题
b. 缺点: 1) 需要依赖特定的插件, 有运维⼯作 2) 只适⽤特定版本
10. 介绍下RabbitMQ的⼯作模式
相关⾯试题:
RabbitMQ的⼏种模式, work模式怎么实现的能者多劳
-
Simple(简单模式):一个生产者,一个消费者
-
Work Queue(⼯作队列):一个生产者,多个消费者,一个消息只能被一个消费者消费
-
Publish/Subscribe(发布/订阅):一个生产者,多个消费者,一个消息被所有消费者消费
-
Routing(路由模式):交换机根据routingkey来转发到特定的队列
-
Topics(通配符模式):routingkey是通配符
-
RPC(RPC通信):RPC请求,不常用
-
Publisher Confirms(发布确认):高级特性,可靠性保证的机制
-
Publish/Subscribe(发布/订阅):
-
Routing(路由模式):
-
Topics(通配符模式):这三个会取决于我们的交换机类型
11. 消息积压的原因, 如何处理
类似问题:
MQ消息堆积问题
如果解决MQ的数据囤积?
消息积压的原因:
• 消息⽣产过快:短期,偶尔
• 消费者能⼒不⾜:持续,经常。代码性能问题。逻辑问题异常一直重试,机器配置问题,业务复杂,代码性能差
• ⽹络问题
• RabbitMQ服务配置偏低
• ...
解决⽅案:
• 提⾼消费者效率, ⽐如增加机器, 优化业务逻辑
• 限制生产者⽣产速率
• 资源配置优化
12. RabbitMQ是推模式还是拉模式
概念
RabbitMQ⽀持两种消息传递模式: 推模式(push)和拉模式(pull)
推模式:消息中间件主动将消息推送给消费者.
拉模式:消费者主动从消息中间件拉取消息
12.1 spingboot实现推拉模式

接下来通过代码演⽰下推模式和拉模式
推模式是默认的,就不用演示了
拉模式的话,之前实现消费者,使用的是@RabbitListener,这个是推模式
但是拉模式的话,就不能用RabbitListener了,要使用rabbittemlate的方法,主动去拉


我们注释掉消费者代码,然后给work.queue这个队列生产十个消息,
然后重新启动

这样work.queue里面就有10条消息了
最后我们用拉模式的方式获取这个队列里面的消息
sql
@RestController
@RequestMapping("/consumer")
public class ConsumerController {
@Autowired
private RabbitTemplate rabbitTemplate;
@RequestMapping("/get")
public String consumer() {
Message message = rabbitTemplate.receive("work.queue");
System.out.println("获取消息"+message);
return "获取消息成功";
}
}
receive的参数就是队列的名称


拉取消息的话,每次都只能拉取一个消息
推的话,就是一直推了,直接就推完了
sql
@Component//交给spring管理
public class WorkListener {
@RabbitListener(queues = Constants.WORK_QUEUE)//监听work.queue队列
public void workListener1(Message message, Channel channel){//队列里面有消息就调用这个方法,然后消息放在message里面
System.out.println("workListener1"+Constants.WORK_QUEUE+":"+message+"channel"+channel);
}
@RabbitListener(queues = Constants.WORK_QUEUE)//监听work.queue队列
public void workListener2(String message){//队列里面有消息就调用这个方法,然后消息放在message里面
System.out.println("workListener2"+Constants.WORK_QUEUE+":"+message);
}
}
这个就是推模式

一股脑的就推完了
这个代码在

12.2 使用Rabbitmq的SDK实现推拉模式
RabbitMQ主要是基于推模式⼯作的, 它的核⼼设计是让消息队列中的消费者接收到由⽣产者发送的消息. 使⽤channel.basicConsume⽅法订阅队列, RabbitMQ就会把消息推送到订阅该队列的消费者,
如果只想从队列中获取单条消息⽽不是持续订阅,则可以使⽤channel.basicGet⽅法来进⾏消费消息.这样就是拉取的方式了,就是拉模式了



sql
public class Producer {
public static void main(String[] args) throws IOException, TimeoutException {
//建立连接
ConnectionFactory factory = new ConnectionFactory();
factory.setHost(Constants.HOST);
factory.setPort(Constants.PORT);
factory.setUsername(Constants.USER_NAME);
factory.setPassword(Constants.PASSWORD);
factory.setVirtualHost(Constants.VIRTUAL_HOST);
Connection connection = factory.newConnection();
//建立通道
Channel channel = connection.createChannel();
//声明交换机,使用内置交换机
//声明队列,队列名称,是否持久化,(就是RabbitMQ重启,消息是否还存在),是否独占,(就是队列是否只有一个消费者)是否自动删除,参数
channel.queueDeclare(Constants.WORK_QUEUE,true,false,false,null);
//发送消息,参数1:交换机名称,参数2:routingkey,参数3:传递消息的额外设置,参数4:消息的具体内容
for (int i = 0; i < 10; i++) {
channel.basicPublish("",Constants.WORK_QUEUE,null,("hello work queue"+i).getBytes());
}
//释放资源
channel.close();
connection.close();
}
}
sql
public class PullConsumer {
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
//建立连接
ConnectionFactory factory = new ConnectionFactory();
factory.setHost(Constants.HOST);
factory.setPort(Constants.PORT);
factory.setUsername(Constants.USER_NAME);
factory.setPassword(Constants.PASSWORD);
factory.setVirtualHost(Constants.VIRTUAL_HOST);
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(Constants.WORK_QUEUE,true,false,false,null);//不存在这个队列就会创建
//拉模式获取消息
GetResponse getResponse = channel.basicGet(Constants.WORK_QUEUE, true);
System.out.println("拉模式获取消息"+new String(getResponse.getBody(),"UTF-8"));
}
}
sql
public class PushConsumer {
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
//建立连接
ConnectionFactory factory = new ConnectionFactory();
factory.setHost(Constants.HOST);
factory.setPort(Constants.PORT);
factory.setUsername(Constants.USER_NAME);
factory.setPassword(Constants.PASSWORD);
factory.setVirtualHost(Constants.VIRTUAL_HOST);
Connection connection = factory.newConnection();
//建立通道
Channel channel = connection.createChannel();
//声明交换机,使用内置交换机
//声明队列,队列名称,是否持久化,(就是RabbitMQ重启,消息是否还存在),是否独占,(就是队列是否只有一个消费者)是否自动删除,参数
channel.queueDeclare(Constants.WORK_QUEUE,true,false,false,null);//不存在这个队列就会创建
//接收消息,参数1:队列名称,参数2:是否自动确认,参数3:消费者接收到消息后的执行逻辑
DefaultConsumer consumer = new DefaultConsumer(channel){
@Override
//参数1:消费者标签,参数2:消息 envelope,参数3:消息属性 properties,参数4:消息内容 body,这个方法就是接收消息的回调方法
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("消费者接收到消息:"+new String(body));
}
};
channel.basicConsume(Constants.WORK_QUEUE,true,consumer);//第二个参数是自动确认,false表示手动确认
Thread.sleep(2000);
//释放资源
// channel.close();
// connection.close();
}
}

先启动生产者
然后在启动拉模式
因为先启动推模式的话,直接就推完了,就没了


启动拉模式之后,还剩九条消息
在启动推模式

Rabbitmq主要是推模式
12.3 总结
RabbitMQ ⽀持两种消息传递模式
推模式:
对消息的获取更加实时, 适合对数据实时性要求⽐较⾼时, ⽐如实时数据处理, 如监控系统, 报表系统等.
拉模式:
消费端可以按照⾃⼰的处理速度来消费, 避免消息积压, 适合需要流量控制, 或者需要⼤量计算资源的任务, 拉取模式允许消费者在准备好后再请求消息, 避免资源浪费