前言
共14
题。
你的项目中哪里用到了RabbitMQ?
微服务间异步通信
之前做过一个项目,它有网页端和配套的app
端,有一个新闻类的页面,用于展示一些行业相关新闻。这个项目的服务间通信使用了两种技术:
- 通过
Feign
实现服务的同步调用(需要返回数据); - 通过
MQ
实现服务的异步通信(不需要返回数据)。
当网页端或app
端上线一篇新闻,或下线一篇新闻的时候,就通过MQ
异步通信给另一端令其也上线或下线该新闻。
流量削峰
比如你现在去开发哔哩哔哩,每一个视频都会有点赞功能,全站有几千万的视频,每个视频都可能有不知道多少人去点赞,这就是一个高并发操作。
但是点赞又没有那么重要,不需要太强的实时性,此时就可以把点赞或点踩操作扔进mq
里,让消费者慢慢消费,从而降低流量。
为什么会选择使用RabbitMQ?
RabbitMQ
的优点是:耦合极低,通过异步让响应更快,并发量高还可以做流量削峰。
但是也有缺点:架构复杂,不好管理,需要依赖于Broker
。
RabbitMQ的持久化
RabbitMQ
的持久化分为三个部分:交换机 的持久化、队列 的持久化和消息的持久化。
- 交换机 的持久化,将
durable
的参数设置为true
交换机的持久化是我们在声明交换机的时候,将
durable
的参数设置为true
实现的。也就是将交换机的内部属性在服务器的内部保存,当MQ
服务器发生重启之后,不需要去重新建立交换机,交换机会根据服务器中保存的交换机的属性来自动创建。
- 队列 的持久化,设置
durable
属性为true
队列的持久化也是我们在声明队列的时候设置
durable
的参数来实现的。队列的持久化能保证队列的元数据不会因异常情况而丢失,但是并不能保证内部所存储的消息不会丢失,要确保消息不会丢失,还需要设置消息为持久化。
- 消息的持久化
实现消息持久化,需要把消息的投递模式设置为
2
,也就是MessageDeliveryMode.PERSISTENT
。
使用RabbitMQ如何保证消息不丢失?
消息丢失是无法找回的,所以只能避免:
-
生产者 :开启确认机制,出现错误就会开启
Spring
的重试机制,重试次数到达限制后(3-5
次)将消息放入到Mysql
,用xxl-job
定时任务读取数据库中的数据,再次发送,发送成功移除该消息; -
MQ本身:做持久化,参考上一条,
-
消费者 :默认开启自动确认机制,用MQ自带的重试机制,到达重试次数后把消息存到一个
error
队列中,人工处理手动删除消息(项目中用的是这个)。
如何解决消息的重复消费问题?
-
给每条消息设置一个唯一的标识
id
,然后消费者检查该id
是否存在(token+redis
); -
幂等方案:分布式锁、数据库锁(悲观锁、乐观锁),性能低不推荐。
如何解决消息堆积在MQ的问题?
增加使用效率,到达一定效率就不容易堆积了。
-
添加消费者;
-
扩大容积;
-
使用惰性队列,直接把数据放到磁盘。
RabbitMQ如何保证消费的顺序性?
在MQ
中,消息是有序的,但是多个消费者无法保证有序,因此一个队列只设置一个消费者消费就可以保证顺序。
RabbitMQ如何实现延迟队列?
延迟队列,即消息进入队列后不会立即被消费,只有到达指定时间后,才会被消费。RabbitMQ
本身没有提供延迟队列功能,有两种实现方式。
TTL
+死信交换机
将消息发送到TTL
(Time-To-Live
,生存时间)队列,消息的TTL
值就是延迟时间。当消息在队列中的存活时间超过TTL
值时,自动变成死信(如果配置了死信交换机和队列),随后这些死信就可以被发送到死信队列,从而实现了延迟队列的效果。只需要监听这个死信队列,消费这个死信队列的消息就可以了。
- 引入延迟消息插件
RabbitMQ如何设置消息过期?
存在以下两种方式:
-
消息所在队列设置了超时时间;
-
消息本身设置了超时时间。
什么是死信交换机?
死信交换机用来处理无法被正确路由、消费的消息,死信指的是消息被消费者拒绝或返回nack
/消息超时未消费/队列满了。如果队列绑定了死信交换机,死信就会投递过去交由人工处理,进一步提高消息队列的可靠性。
RabbitMQ的集群有哪些?
RabbitMQ
天然支持集群模式,它的集群有两种模式:
-
普通集群:是一种分布式集群,将队列分散到集群的各个节点,从而提高整个集群的并发能力;
-
镜像集群:是一种主从集群,普通集群的基础上,添加了主从备份功能,提高集群的数据可用性。
RabbitMQ的工作模式?
RabbitMQ
共有六种工作模式,分别是:
- 简单模式 :一个⽣产者
P
,⼀个消费者C
,消息只能被消费⼀次,也称为点对点(Point-to-Point
)模式。 - ⼯作队列模式 :一个生产者(
P
), 多个消费者(C
), 消息队列会将消息平均分配给消费者。 - 发布/订阅模式 :使用
fanout
类型交换机,将消息交给所有绑定到交换机的队列(发布/订阅模式)。交换机将消息复制多份,并且发送多个消费者,并且每个消费者收到相同的信息。 - 路由模式 在发布订阅模式的基础上增加了路由
key
。发布订阅模式是⽆条件的将所有消息分发给所有消费者,路由模式是Exchange
(交换机)根据RoutingKey
的规则,将数据筛选后发给对应的消费者队列。⽐如系统打印⽇志, ⽇志等级分为error
、warning
、info
、debug
,就可以通过这种模式,把不同的⽇志发送到不同的队列,最终输出到不同的⽂件。 - 通配符模式 :路由模式的升级版, 在
routingKey
的基础上,增加了通配符的功能,使之更加灵活。 - RPC通信模式 :在
RPC
通信的过程中,没有⽣产者和消费者,类似于RPC
远程调⽤, ⼤概就是通过两个队列实现了⼀个可回调的过程。
RabbitMQ的交换机都有哪些类型?
RabbitMQ
支持四种类型的交换机:fanout 、direct 、topic 和headers。每种交换机类型有不同的路由策略:
- fanout:广播消息给所有绑定的队列。
- direct :根据
RoutingKey
将消息路由到特定队列。 - topic :使用通配符匹配
RoutingKey
。 - headers :根据消息的
headers
属性进行路由,但性能较差且不常用。
RabbitMq的message(消息)大小和数量边界值?
RabbitMQ
默认情况下,单个消息最大为128MB
,如果超出,mq
会拒绝接收消息并抛出异常。对于总消息大小和消息数量,没有特别限制。
在rabbitmq.conf
文件中,可通过下述配置修改:
conf
# 设置单个消息的最大大小为 256 MiB
vm_memory_high_watermark.absolute = 268435456
在声明队列时,可以通过 x-max-length
和 x-max-length-bytes
参数分别设置队列中消息的数量上限和总大小上限。
java
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.QueueBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitMQConfig {
// 定义队列名称
public static final String MY_QUEUE = "my_queue";
@Bean
public Queue myQueue() {
return QueueBuilder.durable(MY_QUEUE)
.withArgument("x-max-length", 10000) // 消息数量上限
.withArgument("x-max-length-bytes", 104857600) // 消息总大小上限(100 MiB)
.withArgument("x-message-ttl", 60000) // 消息过期时间(60 秒)
.build();
}
}