rabbitMQ~必须了解的技术点【面试高频】

1 重复消费问题

原因

​ 消费者消费消息后会发送给队列ack信息表示消费已完成,但是可能由于网络波动或者其它异常原因造成ack信息丢失,消费队列未收到ack信息,会重新对该条信息进行requeue后再次发送到消费者,因此造成重复消费;

解决方案

​ 1.通过redis记录每条消息的uuid,使用setnx命令,将uuid作为键进行存储;
​ 2.消费者受到消息后首先进行redis setnx操作,成功则正常消费后返回ack给消费队列,失败则代表该消息已经被消费过,直接返回ack消息给消费队列。

2 如何保持消息消费的顺序性

原因

​ 一个消费队列被多个消费者订阅,消息入队出队都遵循FIFO原则,但是不同消费者消费的顺序可能是不同的;
在这里插入图片描述

​ 当存在一个数据操作需要进行:增加 ==> 修改 ==> 删除 时,会因为消费顺序不一致导致数据不一致;

解决方案
在这里插入图片描述

​ 增加多个消息队列,一个消费者对应一个队列,将同一个数据CRUD的操作放在一个队列里面,让一个消费者订阅后进行消费,消费者单线程消费,这样可以保证顺序,但是可能会降低整个中间件的吞吐量。

3 海量消息堆积/积压在MQ

原因

​ 1.消息队列中的消费没有及时消费,生产者推送消息的速度远大于消费者消费消息的速度;

​ 2.消费者消费消息阻塞或者消费者异常挂掉,耗时长;

解决方案

​ 1.提供消费者的消费能力,内部采用多线程等方式去提升消费效率;

​ 2.增加消费者去消费信息;

​ 3.提供一个队列能够存储消息的上限(Lazy Queues):

1.接收到消息直接存储到磁盘而不是内存;
2.消费者要消费消息时再从磁盘中读取出来;
3.支持数百万条的消息存储。

优缺点:基于磁盘存储,消息容量上限高

消息积压过多可能会导致MQ内存溢出,最终服务宕机!

事故修复

​ 1.堆积的消息还需要使用

​ ①堆积消息量比较小且消费者耗时不算太久:优化消费者,增加消费者;

​ ②海量消息(数百万条)堆积:

1.修复优化consumer,确保和提升消费速度;
2.新建一个exchange, 创建多余之前很多倍的消费队列,然后创建一些consumer去消费积压的消息,消费过程只将积压的消息通过轮训的方式又发送到新的消费队列中去,不做任何逻辑处理;
3.新启多个消费者去消费新消费队列中转移后的消息。

​ 2.堆积的消息不需要使用

​ 直接通过一个消费者进行ack确认,不做任何逻辑或者直接删除队列;

4 延迟队列

概念

​ 延迟队列存储的是延迟消息,延迟消息是指消息入队之后,不想让消费者立刻拿到消息,而是等待指定时间后,消费者才能拿到对应的消息进行消费。

使用场景

​ 1.订单超时取消功能;

​ 2.定时系统等;

实现方式

TTL: 可以设置单个消息或队列的过期时间,如果都设置,则取两者最小值;

DLX: RabbitMQ的Queue可以配置x-dead-letter-exchange 和x-dead-letter-routing-key(可选)两个参数,如果队列内出现了dead letter,则按照这两个参数重新路由转发到指定的队列。

x-dead-letter-exchange:出现死信(dead letter)之后将dead letter重新发送到指定exchange

​ x-dead-letter-routing-key:出现死信(dead letter)之后将dead letter重新按照指定的routing-key发送

死信条件:消息被拒绝,TTL过期、队列已满

基于pika实现

在这里插入图片描述

生产者:

import pika
from pika.exchange_type import ExchangeType

# 创建连接
credentials = pika.PlainCredentials('guest', 'guest')
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost', port=5672, credentials=credentials))

# 创建channel
channel = connection.channel()

# 创建死信交换机与死信队列,并对死信队列进行绑定到死信交换机
channel.exchange_declare(exchange='DLX', exchange_type=ExchangeType.topic)
channel.queue_declare(queue='DLQ')
channel.queue_bind(queue='DLQ', exchange='DLX', routing_key='*.dead.*')

# 死信转发参数
dl_args = {
    'x-dead-letter-exchange': 'DLX',
    'x-dead-letter-routing-key': '*.dead.*',
    'x-message-ttl': 10000  # ms
}

# 辅助exchange、queue(TTL)
channel.exchange_declare(exchange='ttl_exchange', exchange_type=ExchangeType.topic)
channel.queue_declare(queue='ttl_queue', arguments=dl_args)
channel.queue_bind('ttl_queue', exchange='ttl_exchange', routing_key='*.ttl.*')

# 发布消息到TTL交换机
channel.basic_publish(exchange='ttl_exchange',
                      routing_key='test.ttl.test',
                      body=b'ttl test')
消费者:

import time
import pika

# 创建连接
connection = pika.BlockingConnection(pika.ConnectionParameters(host='7.220.225.46', port=5672))
channel = connection.channel()

def callback(ch, method, properties, body):
    """回调函数"""
    print(time.localtime())
    print(body)

# 消费死信队列中的消息
channel.basic_consume(queue='DLQ', on_message_callback=callback, auto_ack=True)
channel.start_consuming()
5 死信交换机与死信队列

死信:对消息设置的过期时间到了消息都没有被消费就会认为这个消息已经死了,死信会进入死信交换机;

​ 成为死信的3种条件:

​ (1) 消息被consumer拒收且requeue=false;
​ (2) 消息设置的TTL到了,消息过期;
​ (3) 队列的长度满了,排在前面的消息就会被丢弃或者扔到死信路由上;

死信队列:存储死信的队列

为队列绑定死信交换机(指定队列的死信参数)

# 死信转发参数
dl_args = {
    'x-dead-letter-exchange': 'DLX',
    'x-dead-letter-routing-key': '*.dead.*',
    'x-message-ttl': 10000  # ms
}
6 rabbitMQ消息过期

默认情况下消息没有有效期

TTL:消息的存活时间,过期的消息叫做死信,死信被存进死信队列

消息有效时间设置方式:

①声明队列消息的有效期,所有进队消息都有一个统一的有效时间;

②发送消息的时候设置消息的有效期;

两者都设置的情况下,取时间最短的消息

1.队列有效期设置:
dl_args = {
    'x-dead-letter-exchange': 'DLX',
    'x-dead-letter-routing-key': '*.dead.*',
    'x-message-ttl': 10000  # ms
}
channel.queue_declare(queue='ttl_queue', arguments=dl_args)

2.消息有效期设置:
channel.basic_publish(exchange='ttl_exchange',
                      properties=pika.BasicProperties(expiration=5000),
                      routing_key='test.ttl.test',
                      body=b'ttl test')

死信交换机:用来接受死信的交换机

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值