RabbitMQ


1. RabbitMQ特点?——灵活易扩展、可靠、高可用(镜像集群模式)、多语言、多协议

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

2. 消息队列有什么优缺点?——解耦、异步、削峰 / 复杂度、可用性降低、数据一致性问题

优点:解耦、异步、削峰。

缺点

  • 系统可用性降低:系统引入的外部依赖越多,越容易挂掉。万一 MQ 挂了,MQ 一挂,整套系统崩 溃,你不就完了?
  • 系统复杂度提高:硬生生加个 MQ 进来,你怎么保证消息没有重复消费?怎么处理消息丢失的情况?怎么保证消息传递的顺序性?问题一大堆。
  • 一致性问题:多个消息队列传输存在一致性问题

3. 交换器4种类型?——fanout、direct、topic、headers

  • fanout(速度最快):简单地把所有发送到该交换器的消息,路由到所有与该交换器绑定的队列中。
  • direct:把消息路由到BindingKey和RoutingKey完全匹配的队列中。
  • topic:通配符模式,把消息路由到BindingKey和RoutingKey通配匹配的队列中
  • headers(性能差,几乎用不到):不处理路由键。而是根据发送的消息内容中的headers属性(键值对)进行匹配。

4. RabbitMQ如何保证按顺序执行?——多对1对1 / 1对1(内部内存队列)

4.1 为什么要保证顺序?——避免数据异常

消息队列中的若干消息如果是对同一个数据进行操作,这些操作具有前后的关系,必须要按前后的顺序执行,否则就会造成数据异常

4.2 如何保证?

  • 方式1:拆分成多个queue,每个queue对应一个consumer,缺点是造成吞吐量下降

  • 方式2:就一个 queue 对应一个 consumer,但这个 consumer 内部用内存队列做排队,然后利用哈希code分发给底层不同的 worker 来处理


5. 如何保证RabbitMQ不被重复消费(保证幂等性)?——设全局唯一标识

幂等性:就是用户对于同一操作发起的一次请求或者多次请求的结果是一致的,不会因为多次点击而产生了副作用。

5.1 为什么会重复消费?——消费者消费后信号未传输到MQ

正常情况下,消费者在消费消息的时候,消费完毕后,会发送一个确认消息给消息队列,消息队列就知道该消息被消费了,就会将该消息从消息队列中删除;

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

5.2 如何解决?——生产者避免重复进入MQ(查队列中消息的id)、消费者避免重复消费(查数据库/缓存中已消费消息id)

  • 在消息生产时避免重复进入MQ:MQ内部针对每条生产者发送的消息生成一个全局唯一ID,进入队列前首先查询队列中是否存在该id,避免重复进入队列;

  • 在消息消费时避免重复消费:消费者获取到消息后,先根据id去查询redis/db是否存在该消息,如果不存在,则正常消费,消费完毕后写入redis/db;如果存在,则证明消息被消费过,直接丢弃。


6. 消息基于什么传输?——信道(未使用TCP)

RabbitMQ是基于信道Channel的方式来传输数据排除了使用TCP链接来进行数据的传输,因为TCP链接创建和销毁对于系统性能的开销比较大,且并发能力受系统资源的限制,这样很容易造成rabbitMQ的性能瓶颈。

消费者链接RabbitMQ其实就是一个TCP链接,一旦链接创建成功之后,就会基于链接创建Channel,每个线程把持一个Channel,Channel复用TCP链接,减少了系统创建和销毁链接的消耗,提高了性能


7. 如何保证RabbitMQ消息的可靠传输?——生产者confirm / MQ持久化 / 消费者手动ack

消息不可靠的情况可能是消息丢失,劫持等原因丢失又分为:生产者丢失消息、消息列表丢失消息、消费者丢失消息

  • 生产者丢失消息confirm模式,MQ收到信息后返回ack给生产者

    从生产者弄丢数据这个角度来看,RabbitMQ提供transaction(交易)机制confirm模式来确保生产者不丢消息;

    • transaction机制:发送消息前开启事务,然后发送消息。如果发送过程中出现什么异常,事务就会回,如果发送成功则提交事务。
    • confirm模式(常用):所有在该信道上发布的消息都将会被指派一个唯一的ID(从1开始),一旦消息被投递到所有匹配的队列之后,RabbitMQ就会发送一个ACK给生产者(包含消息的唯一ID),这就使得生产者知道消息已经正确到达目的队列了; 如果RabbitMQ没能处理该消息,则会发送一个Nack消息给你,你可以进行重试操作。
  • 消息队列丢数据:开启消息持久化,保证数据重启不丢失

    开启持久化磁盘的配置,这样设置以后,即使RabbitMQ挂了,重启后也能恢复数据

  • 消费者丢失消息:手动ack模式(自动确认处理失败丢失了返回MQ的ack)

    原因:消费者丢数据一般是因为采用了自动确认消息模式,即且处理失败。消费者在收到消息之后,处理消息之前,会自动回复RabbitMQ已收到消息; 如果这时处理消息失败,就会丢失该消息;

    解决方法:改为手动确认消息。;解决方案就是处理消息成功后,手动回复确认消息


8. 如何确保消息接收方消费了消息? ——来回确认

  • 发送方确认模式:生产者收到ack确认送达,收到nack则重新发送

    生产者把信道设置为confirm确认模式后,所有再改信道发布的消息都会被指定一个唯一的ID,一旦消息被投递到所有匹配的队列之后,RabbitMQ就会发送一个确认(Basic.Ack)给生产者(包含消息的唯一ID),这样生产者就知道消息到达对应的目的地了。

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

  • 接收方确认机制:消费方收到就返回ack,RabbitMQ删除队列中消息(MQ会等消费者处理好为止)

    消费者接收每一条消息后都必须进行确认(消息接收和消息确认是两个不同操作)。只有消费者确认了消息,RabbitMQ 才能安全地把消息从队列中删除。 这里并没有用到超时机制,RabbitMQ 仅通过 Consumer 的连接中断来确认是否需要重新发送消息。也就是说,只要连接不中断,RabbitMQ 给了 Consumer 足够长的时间来处理消息。保证数据的最终一致性。

9. 如何保证RabbitMQ消息队列的高度可用性?——镜像集群模式(自动备份同步)

RabbitMQ 有三种模式:单机模式,普通集群模式,镜像集群模式

  • 单机模式:就是demo级别的,一般就是你本地启动了玩玩儿的,没人生产用单机模式

  • 普通集群模式:意思就是在多台机器上启动多个RabbitMQ实例,每个机器启动一个。创建queue,只会放在一个RabbitMQ实例上,但是每个实例都同步queue元数据(元数据可以认为是queue的一些配置信息,通过元数据,可以找到queue所在实例,可以理解为多个实例间相互连接)。消费时如果连接到了另外一个实例,那么那个实例会从queue所在实例上拉取数据过来。可提高吞吐量,但无高可用性

  • 镜像集群模式:这种模式才是真正的RabbitMQ的高可用性。跟普通集群模式不一样的是,在镜像集群模式下,创建queue无论元数据(RabbitMQ配置数据)还是queue里面的消息都会存在于多个实例上。就是说,每个RabbitMQ节点都有这个queue的一个完整镜像,包含queue的全部数据的意思,然后每次你写消息到queue的时候,都会自动把消息同步到多个实例的queue上


10. 死信队列和延迟队列的使用?

  • 死信队列:和普通的队列没啥大的区别,都需要创建自己的QueueExchange,然后通过RoutingKey绑定到Exchange上去,只不过死信队列的RoutingKeyExchange要作为参数,绑定到正常的队列上去

  • 发生以下几种情况时,消息会由正常队列q加入q绑定的死信队列:

    1. 正常队列里面的消息被basicNack或者reject

    2. MQ开启了自动签收,消费者消费消息时出现异常,超过了重试次数,那么这条消息也会进入死信队列

    3. 正常队列已满(达到了最大的长度)

    4. 消息过期了

      在 rabbitmq 中存在2种方法可设置消息的过期时间,当消息达到过期时间还没有被消费,那么那个消息就成为了一个死信消息。

      • 第一种通过对队列进行设置,这种设置后,该队列中所有的消息都存在相同的过期时间

        **队列设置:**在队列申明的时候使用 x-message-ttl 参数,单位为 毫秒

      • 第二种通过对消息本身进行设置,那么每条消息的过期时间都不一样。

        **单个消息设置:**是设置消息属性的 expiration 参数的值,单位为 毫秒

      • 如果同时使用这2种方法,那么以过期时间小的那个数值为准

  • 延时队列:发送者发出的消息要延时一段时间,消费者才能接受的到。在rabbitmq中不存在延时队列,但是我们可以通过设置消息的过期时间和死信队列来模拟出延时队列。消费者监听死信交换器绑定的队列,而不要监听消息发送的队列。

    典型的应用场景:订单30分钟内未支付就关闭订单。

    如何实现10s自动关闭订单?

    1、用 x-message-ttl 参数,设置正常队列q1过期时间为10s,并将q绑定到死信队列q2
    2、首先将生产者消息路由到q1,若消息10s内没有消费,则自动过期,从而被路由到q1绑定的q2
    3、从而触发q2的关闭订单方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值