zoukankan      html  css  js  c++  java
  • rabbitmq面试

    MQ的优缺点

    优点

    • 异步 - 不需要立即处理的消息可以之后慢慢处理。异步处理可以提高系统吞吐量。
    • 解耦 - 各个系统间通过消息通信,不用关心其他系统的处理。
    • 削锋 - 可以通过消息队列支撑突发访问压力;可以缓解短时间内的高并发请求,不会因为突发超负荷请求而完全崩溃。

    缺点

    系统复杂度提高:需要考虑很多问题,如一致性问题、如何保证消息不被重复消费、如何保证消息可靠性传输等。

    RabbitMQ选型

    可以支撑高并发、高吞吐、性能比较高,有非常完善便捷的后台管理界面。

    支持集群化、高可用部署、消息高可靠支持,功能较为完善。

    缺点:基于erlang语言开发的,分析里面的源码有一定门槛,较难进行深层次的源码定制和改造。

    Kafka的优势在于专为超高吞吐量的实时日志采集、实时数据同步、实时数据计算等场景来设计。

    什么是RabbitMQ

    RabbitMQ是一款开源的,Erlang编写的,基于AMQP协议的消息中间件,主要负责接收、存储、转发消息。

    rabbitmq 的使用场景

    (1)服务间异步通信

    (2)顺序消费

    (3)定时任务

    (4)请求削峰

    RabbitMQ基本概念

    Producer:生产者,投递消息的程序

    Consumer:消费者,接受消息的程序

    Broker:服务节点,消息队列服务器实体

    Virtual host:虚拟broker,用于逻辑隔离,最上层消息的路由。可以理解为虚拟 broker 。其内部均含有独立的 queue、exchange 和 binding 等,拥有独立的权限系统。

    Queue:队列,RabbitMQ的内部对象,用于存储信息。多个消费可以订阅同一个队列,但消息会被平均分摊,而不是每个消费者都会受到所有的消息。

    Exchange:交换器,生产者将消息发送到交换器,交换器将消息路由到一个或者多个队列中

    RoutingKey:路由键,生产者将消息发给交换器的时候会指定一个路由键,用来指定路由规则

    Binding:绑定,RabbitMQ通过绑定将交换器与队列关联起来,绑定时会指定BindingKey

    Connection:连接,生产者或消费者和Broker之间的一条TCP连接

    Channel:信道,建立在Connection上的虚拟连接,每条AMQP指令都通过信道完成交换器类型

    RabbitMQ的工作模式

    简单模式:它包含一个生产者、一个消费者和一个队列。生产者向队列里发送消息,消费者从队列中获取消息并消费。

    工作模式:向多个互相竞争的消费者发送消息的模式,它包含一个生产者、两个消费者和一个队列。两个消费者同时绑定到一个队列上去,当消费者获取消息处理耗时任务时,空闲的消费者从队列中获取并消费消息。

    发布/订阅模式:同时向多个消费者发送消息的模式(类似广播的形式),它包含一个生产者、两个消费者、两个队列和一个交换机。两个消费者监听各自队列,两个队列绑定到交换机上去,生产者通过发送消息到交换机,所有消费者接收并消费消息。

    路由模式:根据路由键选择性给多个消费者发送消息的模式,它包含一个生产者、两个消费者、两个队列和一个交换机。两个消费者监听各自队列,两个队列通过路由键绑定到交换机上去。生产者发送消息到交换机,交换机通过路由键转发到不同队列,队列绑定的消费者接收并消费消息。

    主题模式:根据路由键匹配规则选择性给多个消费者发送消息的模式,它包含一个生产者、两个消费者、两个队列和一个交换机。两个消费者监听各自队列,两个队列通过路由键匹配规则绑定到交换机上去。生产者发送消息到交换机,交换机通过路由键匹配规则转发到不同队列,队列绑定的消费者接收并消费消息。

    消息在什么时候会变成死信?

    • 消息拒绝并且没有设置重新入队
    • 消息过期
    • 消息堆积,并且队列达到最大长度,先入队的消息会变成DL

    如何保证RabbitMQ消息的顺序性?

    1、需要保证顺序性的消息使用一个 queue对应一个 consumer。

    2、消息在被创建时,都将被赋予一个全局唯一的、单调递增的、连续的序列号(SerialNumber,SN),可以通过一个全局计数器来实现这一点。可以在消费端实现前一条消息未消费,不处理下一条消息;也可以在生产端实现前一条消息未处理完毕,不发布下一条消息。

    如何保证消息不丢失?

    1. 生产者发送消息至MQ的数据丢失

    解决方法:在生产者端开启comfirm 确认模式,然后如果写入了 RabbitMQ 中,RabbitMQ 会给你回传一个 ack 消息,告诉你说这个消息 ok 了。

    1. MQ收到消息,暂存内存中,还没消费,自己挂掉,数据会都丢失

    解决方式:MQ设置为持久化。将内存数据持久化到磁盘中

    1. 消费者刚拿到消息,还没处理,挂掉了,MQ又以为消费者处理完

    解决方式:用 RabbitMQ 提供的 ack 机制,每次处理完的时候, ack 一把。

    如何保证消息不被重复消费?

    1、设置操作的幂等性。

    2、生产者发送每条数据的时候,里面加一个全局唯一的 id,消费者通过全局唯一ID检查是否消费过。

    3、基于数据库的唯一键来保证重复数据不会重复插入多条。

    消息如何分发?

    若该队列至少有一个消费者订阅,消息将以循环(round-robin)的方式发送给消费者。每条消息只会分发给一个订阅的消费者。通过路由可实现多消费的功能

    消息怎么路由?

    消息将拥有一个路由键(routing key),在消息创建时设定。通

    过队列路由键,可以把队列绑定到交换器上。

    消息到达交换器后,RabbitMQ 会将消息的路由键与队列的路由键进行匹配(针对不同的交换器有不同的路由规则);

    常用的交换器主要分为一下三种:

    fanout:如果交换器收到消息,将会广播到所有绑定的队列上

    direct:如果路由键完全匹配,消息就被投递到相应的队列

    topic:可以使来自不同源头的消息能够到达同一个队列。 使用 topic 交换器时,可以使用通配符

    消息基于什么传输?

    由于 TCP 连接的创建和销毁开销较大,且并发数受系统资源限制,会造成性能瓶颈。RabbitMQ 使用信道的方式来传输数据。信道是建立在真实的 TCP 连接内的虚拟连接,且每条 TCP 连接上的信道数量没有限制。

    什么情况下会出现blackholed问题?

    blackholed问题是指,向exchange投递了 message,而由于各种原因导 致该message丢失,但发送者却不知道。可导致blackholed的情况:

    向未绑定 queue 的 exchange 发送 message;

    exchange 以 binding_key key_A 绑定了 queue queue_A,但向该 exchange 发送 message 使用的 routing_key却是 key_B。

    如何防止出现blackholed问题?

    如果在执行Basic.Publish时设置mandatory=true,则在遇到 可能出现blackholed情况时,服务器会通过返回Basic.Return告之当前 message无法被正确投递。

    Basic.Reject的用法是什么?

    该信令可用于consumer对收到的message进行reject。

    若在该信令中设 置requeue=true,则当RabbitMQ server收到该拒绝信令后,会将该 message重新发送到下一个consumer处(理论上仍 可能将该消息发送给当前consumer)。

    若设置requeue=false,则 RabbitMQ server在收到拒绝信令后,将直接将该message从queue中移 除。

    如何保证消息不被重复消费?

    重复消费场景:因为网络传输等故障,消费确认信息没有传送到消息队列,导致消息队列不知道已经消费过该消息了,再次将消息分发给其他的消费者。

    解决思路:保证消息的唯一性,就算是多次传输,不要让消息的多次消费带来影响;保证消息等幂性;

    比如:在写入消息队列的数据做唯一标识,消费消息时,根据唯一标识判断是否消费过;

    如何确保消息正确地发送至 RabbitMQ? 如何确保消息接收方消费了消息?

    发送方确认模式

    将信道设置成 confirm 模式(发送方确认模式),则所有在信道上发布的消息都会被指派一个唯一的 ID。

    一旦消息被投递到目的队列后,或者消息被写入磁盘后(可持久化的消息),信道会发送一个确认给生产者(包含消息唯一 ID)。

    如果 RabbitMQ 发生内部错误从而导致消息丢失,会发送一条 nack(notacknowledged,未确认)消息。

    发送方确认模式是异步的,生产者应用程序在等待确认的同时,可以继续发送消息。当确认消息到达生产者应用程序,生产者应用程序的回调方法就会被触发来处理确认消息。

    接收方确认机制

    消费者接收每一条消息后都必须进行确认(消息接收和消息确认是两个不同操作)。只有消费者确认了消息,RabbitMQ 才能安全地把消息从队列中删除。

    这里并没有用到超时机制,RabbitMQ 仅通过 Consumer 的连接中断来确认是否需要重新发送消息。也就是说,只要连接不中断,RabbitMQ 给了 Consumer 足够长的时间来处理消息。保证数据的最终一致性;

    特殊情况

    如果消费者接收到消息,在确认之前断开了连接或取消订阅,RabbitMQ 会认为消息没有被分发,然后重新分发给下一个订阅的消费者。(可能存在消息重复消费的隐患,需要去重)
    如果消费者接收到消息却没有确认消息,连接也未断开,则 RabbitMQ 认为该消费者繁忙,将不会给该消费者分发更多的消息。

    如何保证RabbitMQ消息的可靠传输?

    丢失又分为:生产者丢失消息、消息列表丢失消息、消费者丢失消息;

    生产者丢失消息:RabbitMQ提供transaction和confirm模式来确保生产者不丢消息;

    transaction机制:发送消息前,开启事务(channel.txSelect()),然后发送消息,如果发送过程中出现什么异常,事务就会回滚(channel.txRollback()),如果发送成功则提交事务(channel.txCommit())。然而,这种方式有个缺点:吞吐量下降;

    confirm模式用的居多:一旦channel进入confirm模式,所有在该信道上发布的消息都将会被指派一个唯一的ID(从1开始),一旦消息被投递到所有匹配的队列之后;rabbitMQ就会发送一个ACK给生产者(包含消息的唯一ID),这就使得生产者知道消息已经正确到达目的队列了;如果rabbitMQ没能处理该消息,则会发送一个Nack消息给你,你可以进行重试操作。

    消息队列丢数据:消息持久化。开启持久化磁盘的配置。

    这个持久化配置可以和confirm机制配合使用,你可以在消息持久化磁盘后,再给生产者发送一个Ack信号。

    队列持久化+消息持久化。

    消费者在收到消息之后,处理消息之前,会自动回复RabbitMQ已收到消息;

    如果这时处理消息失败,就会丢失该消息;

    解决方案:处理消息成功后,手动回复确认消息。

    为什么不应该对所有的 message 都使用持久化机制?

    一般仅对关键消息作持久化处理,且应该保证关键消息的量不会导致性能瓶颈。否则会导致性能的下降,因为写磁盘比写 RAM 慢的多。

    其次,message 的持久化机制用在 RabbitMQ 的内置 cluster 方案时会出现“坑爹”问题。

    如何保证高可用的?RabbitMQ 的集群

    普通集群模式,在多台机器上启动多个 RabbitMQ 实例。 queue,只会放在一个 RabbitMQ 实例上,但是每个实例都同步 queue 的元数据。消费的时候,实际上如果连接到了另外一个实例,那么那个实例会从 queue 所在实例上拉取数据过来。这方案主要是提高吞吐量的,就是说让集群中多个节点来服务某个 queue 的读写操作。

    镜像集群模式: RabbitMQ 的高可用模式。,在镜像集群模式下,你创建的 queue,无论元数据还是 queue 里的消息都会存在于多个实例上,就是说,每个 RabbitMQ 节点都有这个 queue 的一个完整镜像,包含 queue 的全部数据的。然后每次你写消息到 queue 的时候,都会自动把消息同步到多个实例的 queue 上。

    好处在于,你任何一个机器宕机了,没事儿,其它机器(节点)还包含了这个 queue 的完整数据,别的 consumer 都可以到其它节点上去消费数据。

    坏处在于,这个性能开销大,消息需要同步到所有机器上,导致网络带宽压力和消耗很重。

    rabbitmq 对集群节点停止顺序有要求吗?

    RabbitMQ 对集群的停止的顺序是有要求的,应该先关闭内存节点,最后再关闭磁盘节点.如果顺序恰好相反的话,可能会造成消息的丢失

    rabbitmq 集群有什么用?

    • 高可用: 某个服务器出现问题,整个 RabbitMQ 还可以继续使用;
    • 高容量: 集群可以承载更多的消息量.

    RAM node 和 disk node 的区别?

    RAM node 仅将相关元数据保存到内存中,但disk node会在内存和磁盘中均进行存储。要求在RabbitMQ cluster中至少存在一个disk node。

    rabbitmq 每个节点是其他节点的完整拷贝吗?为什么?

    不是,原因有以下两个:

    1. 存储空间的考虑:如果每个节点都拥有所有队列的完全拷贝,这样新增节点不但没有新增存储空间,反而增加了更多的冗余数据;
    2. 性能的考虑:如果每条消息都需要完整拷贝到每一个集群节点,那新增节点并没有提升处理消息的能力,反而可能更糟。

    rabbitmq 集群中唯一一个磁盘节点崩溃了会发生什么情况?

    如果唯一磁盘的磁盘节点崩溃了,不能进行以下操作:

    不能创建队列

    不能创建交换器

    不能创建绑定

    不能添加用户

    不能更改权限

    不能添加和删除集群节点

    唯一磁盘节点崩溃了,集群是可以保持运行的,但你不能更改任何东西。

  • 相关阅读:
    Data Structure and Algorithm
    Data Structure and Algorithm
    Data Structure and Algorithm
    Data Structure and Algorithm
    Data Structure and Algorithm
    Data Structure and Algorithm
    Data Structure and Algorithm
    Data Structure and Algorithm
    Data Structure and Algorithm
    Data Structure and Algorithm
  • 原文地址:https://www.cnblogs.com/hainingwyx/p/14623875.html
Copyright © 2011-2022 走看看