RabbitMQ面经


消息队列概念:消息队列可以看做是一个存放消息的容器,当需要使用消息时,直接从容器中取出进行消费即可,RabbitMQ消息队列是有序的,消息队列是分布式系统中的重要组件,其作用是通过异步处理提高系统性能和削峰,降低系统耦合性,队列是一种先进先出的数据结构,所以消费消息时也是按照顺序来消费的。
Queue(消息队列) 用来保存消息直到发送给消费者。它是消息的容器,也是消息的终点。一个消息可投入一个或多个队列。消息一直在队列里面,等待消费者连接到这个队列将其取走


消息队列的三个好处:
1、通过异步处理提高系统性能(减少响应所需时间)。
2、削峰/限流
3、降低系统耦合性。


在项目中使用消息队列,将订单请求信息写入消息队列后就立即返回给客户(订单秒杀中状态),然后对订单请求进行业务校验(如是否超卖、重复下单等),若符合业务校验则进行缓存更新和写数据库的操作(商品表中库存数量减1,写入订单信息),待操作执行成功后跳转到支付页面;若订单请求不通过业务校验,则下单失败。
使用消息队列进行异步下单的好处:将短时间高并发产生的事务消息存储在消息队列中,然后服务端在慢慢根据自己的能力去消费这些消息,避免短时间的高并发请求导致服务器宕机


消息队列带来的问题:
1、统可用性降低: 系统可用性在某种程度上降低,为什么这样说呢?在加入 MQ 之前,你不用考虑消息丢失或者说 MQ 挂掉等等的情况,但是,引入 MQ 之后你就需要去考虑了!
2、系统复杂性提高: 加入 MQ 之后,你需要保证消息没有被重复消费、处理消息丢失的情况、保证消息传递的顺序性等等问题!
3、一致性问题: 我上面讲了消息队列可以实现异步,消息队列带来的异步确实可以提高系统响应速度。但是,万一消息的真正消费者并没有正确消费消息怎么办?这样就会导致数据不一致的情况了


RabbitMQ简介:RabbitMQ 是采用 Erlang 语言实现 ,整体上是一个生产者与消费者模型,主要负责接收、存储和转发消息;
RabbitMQ中包含了生产者、交换器、消息队列和消费者。生产者是产生消息的一方,然后将消息传到交换机,交换器根据分配规则将消息分配到对应的消息队列中(Queue)

RabbitMQ面经_第1张图片

RabbitMQ的交换机有四种类型,不同类型对应不同的路由策略:Fanout模式(广播模式)、Direct模式、Topic模式、Headers模式。
生产者将消息发送给交换器的时候,一般会指定一个RoutingKey(路由键),用这个来指定消息的路由规则,RoutingKey 需要与交换器类型和绑定键(BindingKey)联合使用才能最终生效
RabbitMQ 中通过 Binding(绑定) 将 Exchange(交换器) 与 Queue(消息队列) 关联起来,在绑定的时候一般会指定一个 BindingKey(绑定建) ,这样 RabbitMQ 就知道如何正确将消息路由到队列了,如下图所示。

RabbitMQ面经_第2张图片

多个消费者可以订阅同一个队列,这时队列中的消息会被平均分摊(Round-Robin,即轮询)给多个消费者进行处理,而不是每个消费者都收到所有的消息并处理,这样避免消息被重复消费。


交换器类型之== fanout类型:== fanout类型的交换器路由规则非常简单,它会将交换器中的消息发送到与它绑定的消息队列中,不需要做任何判断,因此速度快。fanout类型常用来广播消息。

RabbitMQ面经_第3张图片

交换器类型之== direct类型:== direct类型涉及了RountingKey(路由键),将队列与路由键进行了绑定,消息传递时路由键必须去匹配才能够被队列接受,否则消息就会被抛弃

RabbitMQ面经_第4张图片

交换器类型之Topic类型:使用通配符*或#,*匹配一个词汇,#匹配0个或多个词汇。 如:*.orange.*表示只要是xx.orange.yy的消息都会传入该队列,xx、yy任意一个词;*.*.rabbit表示路由键为xx.yy.rabbit

RabbitMQ面经_第5张图片

交换器类型之Headers类型:,Headers类型的exchange使用的比较少,它是忽略routingKey的一种路由方式。是使用Headers来匹配的。Headers是一个键值对,可以定义成Hashtable。发送者在发送的时候定义一些键值对,接收者也可以再绑定时候传入一些键值对,两者匹配的话,则对应的队列就可以收到消息


RabbitMQ特点:
可靠性: RabbitMQ 使用一些机制来保证可靠性, 如持久化、传输确认及发布确认等。
灵活的路由 : 在消息进入队列之前,通过交换器来路由消息。对于典型的路由功能, RabbitMQ 己经提供了一些内置的交换器来实现。针对更复杂的路由功能,可以将多个 交换器绑定在一起, 也可以通过插件机制来实现自己的交换器。
扩展性: 多个 RabbitMQ 节点可以组成一个集群,也可以根据实际业务情况动态地扩展 集群中节点。
高可用性 : 队列可以在集群中的机器上设置镜像,使得在部分节点出现问题的情况下队 列仍然可用。
多种协议: RabbitMQ 除了原生支持 AMQP 协议,还支持 STOMP, MQTT 等多种消息 中间件协议。
多语言客户端 :RabbitMQ 几乎支持所有常用语言,比如 Java、 Python、 Ruby、 PHP、 C#、 JavaScript 等。
管理界面 : RabbitMQ 提供了一个易用的用户界面,使得用户可以监控和管理消息、集 群中的节点等。
插件机制 : RabbitMQ 提供了许多插件 , 以实现从多方面进行扩展,当然也可以编写自 己的插件。


生产者和消费者
生产者 :
消息生产者,就是投递消息的一方,就是把消息传入到交换机的一方。
消息一般包含两个部分:消息体(payload)和标签(Label)。
消费者 :
消费消息,接收消息的一方,也就是将消息从队列中取出消费的一方。
消费者连接到 RabbitMQ 服务器,并订阅到队列上。消费消息时只消费消息体,丢弃标签。


RabbitMQ是有序的吗?如何保证消息的顺序性?
RabbitMQ是有序的。

RabbitMQ面经_第6张图片

AMQP 是什么?
RabbitMQ就是AMQP协议的Erlang的实现(但RabbitMQ 还支持 STOMP2、 MQTT3 等协议);两者的架构模型是一样的(交换器、队列、绑定),生产者将消息发送给交换器,交换器和队列绑定。
AMQP 协议的三层 :
1、Module Layer:协议最高层,主要定义了一些客户端调用的命令,客户端可以用这些命令实现自己的业务逻辑。
2、Session Layer:中间层,主要负责客户端命令发送给服务器,再将服务端应答返回客户端,提供可靠性同步机制和错误处理。
3、 TransportLayer:最底层,主要传输二进制数据流,提供帧的处理、信道服用、错误检测和数据表示等。


Broker 服务节点、Queue 队列、Exchange 交换器
Broker 服务节点:可以看做是RabbitMQ的服务节点,一般情况下一个Broker可以看做一个RabbitMQ 服务器
Queue队列:RabbitMQ 的内部对象,用于存储消息。多个消费者可以订阅同一队列,这时队列中的消息会被平摊(轮询)给多个消费者进行处理
Exchange 交换器: 生产者将消息发送到交换器,由交换器将消息路由到一个或者多个队列中。当路由不到时,或返回给生产者或直接丢弃


什么是死信队列?如何导致的?
DLX,全称为 Dead-Letter-Exchange,死信交换器。当消息在一个队列中变成死信 (dead message) 之后,它能被重新被发送到另一个交换器中,这个交换器就是 DLX,绑定 DLX 的队列就称之为死信队列。
导致的死信的几种原因:
1、消息被拒(Basic.Reject /Basic.Nack) 且 requeue = false。
2、消息 TTL (消息的存活时间)过期。
3、队列满了,无法再添加


RabbitMQ的工作模式
简单模式
work 工作模式
pub/sub 发布订阅模式
Routing 路由模式
Topic 主题模式


如何保证消息的可靠性?
三种情况下丢失消息:消息到 MQ 的过程中搞丢,MQ 自己搞丢,MQ 到消费过程中搞丢
1、生产者到 RabbitMQ:事务机制和 Confirm 机制,注意:事务机制和 Confirm 机制是互斥的,两者不能共存,会导致 RabbitMQ 报错。
2、RabbitMQ 自身:持久化、集群、普通模式、镜像模式。
3、RabbitMQ 到消费者:basicAck 机制、死信队列、消息补偿机制
普通集群模式:意思就是在多台机器上启动多个 RabbitMQ 实例,每个机器启动一个。你创建的 queue,只会放在一个 RabbitMQ 实例上,但是每个实例都同步 queue 的元数据
镜像集群模式:这种模式,才是所谓的 RabbitMQ 的高可用模式。跟普通集群模式不一样的是,在镜像集群模式下,你创建的 queue,无论元数据还是 queue 里的消息都会存在于多个实例上,就是说,每个 RabbitMQ 节点都有这个 queue 的一个完整镜像,包含 queue 的全部数据的意思。然后每次你写消息到 queue 的时候,都会自动把消息同步到多个实例的 queue 上。RabbitMQ 有很好的管理控制台,就是在后台新增一个策略,这个策略是镜像集群模式的策略,指定的时候是可以要求数据同步到所有节点的,也可以要求同步到指定数量的节点,再次创建 queue 的时候,应用这个策略,就会自动将数据同步到其他的节点上去了。这样的好处在于,你任何一个机器宕机了,没事儿,其它机器(节点)还包含了这个 queue 的完整数据,别的 consumer 都可以到其它节点上去消费数据。坏处在于,第一,这个性能开销也太大了吧,消息需要同步到所有机器上,导致网络带宽压力和消耗很重!RabbitMQ 一个 queue 的数据都是放在一个节点里的,镜像集群下,也是每个节点都放这个 queue 的完整数据。


如何解决消息队列的延时以及过期失效问题?
RabbtiMQ 是可以设置过期时间的,也就是 TTL。如果消息在 queue 中积压超过一定的时间就会被 RabbitMQ 给清理掉,这个数据就没了。那这就是第二个坑了。这就不是说数据会大量积压在 mq 里,而是大量的数据会直接搞丢。
解决方法:批量重导。就是大量积压的时候,我们当时就直接丢弃数据了,然后等过了高峰期以后,开始写程序,将丢失的那一批数据一点一点查出来,然后重新灌入到MQ里面去,把高峰期丢失的数据补回来。

你可能感兴趣的:(面试整理,java-rabbitmq,rabbitmq,java)