1 rabbitMQ消息丢失
消息发送过程中由于rabbitMQ服务异常或者节点宕机等情况下,导致消息丢失。
2 如何解决消息丢失
方案1:rabbitMQ持久化
方案2:rabbitMQ消息确认:消息发送确认&消息消费确认
2.1 rabbitMQ持久化
rabbitMQ默认建立的是临时queue和exchange,如果不声明持久化,一旦rabbitMQ服务挂掉,queue、exchange、消息都会全部丢失,所以一般创建queue或exchange的时候都会做持久化操作。
# 1.队列持久化
# 创建队列,如果队列不存在则创建队列
channel.queue_declare(queue='test', durable=True)
# durable=True 代表消息队列持久化存储,False的话是非持久化存储
# 2.交换机持久化,如果交换机不存在则创建交换机
channel.exchange_declare(exchange='test_exchange', durable=True)
# durable=True 代表交换机持久化存储,False的话是非持久化存储
# 说明:
# 1.如果存在相同命名非持久化的队列或者交换机,不能通过以上方法改变其属性
# 2.交换机或队列只要有一方的持久化状态不同则无法进行binding
# 3.消息持久化
# 因为exchange与queue虽然进行了持久化,但是消息存在于内存中,如果rabbitMQ服务挂掉,消息就会丢失,因为需要对消息进行持久化操作
channel.basic_publish(
exchange='test_exchange', # 交换机
routing_key='test', # 消息路由
body=b'xx', # 消息体
properties=pika.BasicProperties(delivery_mode=2), # delivery_mode=2代表消息进行持久化,delivery_mode=1代表不进行持久化
mandatory=True # 告诉消息无法路由到队列时如何处理,False则丢掉该消息,True则通过basic.return方法将消息返回给生产者
)
2.2 消息发送确认
消息发送确认:生产者是否将消息发送到交换机,交换机是否将消息传递到队列,rabbitMQ生产者确认机制:
1.通过事务实现;
2.通过发送方确认机制(publisher confirm)实现;
# 1.通过事务实现:
# 事务机制:只有消息被rabbitMQ服务器成功接收后,事务才能提交成功
try:
channel.tx_select() # 将当前信道设置为事务模式
channel.basic_publish(exchange=exchange_key, routing_key='key.q1.value', body=b'q1:test_msg.') # 发送消息
channel.tx_commit() # 提交事务
except Exception as ex:
# 消息发送过程有异常
channel.tx_rollback() # 捕获异常,并事务回滚
finally:
channel.close()
# 事务比较消耗性能,事务机制是阻塞式的过程,在实际开发中使用不多;
# 2.发送方confirm机制:
# 生产者将信道设置成confirm模式
channel.confirm_delivery()
# 1.在该信道发布的消息都会被指派一个唯一ID,一旦消息被投递到匹配的队列之后,rabbitMQ会发送一个(Basic.Ack)给生产者,发布者确认成功
# 2.如果消息与队列是持久化的,那么确认消息会在消息写入磁盘后发出
try:
rsp = channel.basic_publish(exchange='test_exchange',
routing_key='hello1',
body=b'q1:test_msg.',
properties=pika.BasicProperties(delivery_mode=2),
mandatory=True)
except Exception as ex:
print(ex)
# 发送方confirm确认的好处在于:
# 1.消息的处理是异步的,一旦发布一条消息,生产者程序会在channel等待确认的同时并继续发送下一条消息;
# 2.等消息最终得到确认之后,生产着再通过回调函数处理该条消息;
# 3.如果是因为rabbitMQ本身错误导致消息丢失,就会发送一条nack命令,生产者程序进行处理
2.3 消息接受确认
消息接受确认:消费者是否成功消费了队列中的消息
import time
import pika
# 创建连接
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost', port=xxxx))
# 创建channel
channel = connection.channel()
# 消费者进行消费的回调函数
def callback(ch, method, properties, body):
time.sleep(1)
# 消费者消费消息=======
print(body)
# 进行ack确认
ch.basic_ack(delivery_tag=method.delivery_tag)
# 参数说明:delivery_tag,消息的UUID
# 或者拒绝消息
ch.basic_reject(delivery_tag=method.delivery_tag, requeue=True)
# 参数说明:delivery_tag,消息的UUID; requeue=False时拒绝的消息将被队列删除,如果requeue=True时拒绝的消息将被重新排队,发送给消费者(也可能发送到之前拒绝消息的消费者)
# 2.消费者接受消息
channel.basic_consume(queue='hello',
auto_ack=False, # 指定消息为自动确认
on_message_callback=callback)
# 解决消费者异常退出或其它原因导致的消费者未能收到消息造成的消息丢失问题
# auto_ack=True时队列会讲发出去的消息自动确认后并从磁盘(内存)中删除,消费者订阅指定队列后,队列会将所有消息推送给消费者并从队列中删除消息
# auto_ack=False时需要消费者调用basic.ack进行确认后,队列才确认消费后进行删除,消费者未进行ack,消费者断开连接之后,队列会重新Ready,并发送给下一次消费者
# 3.消费
channel.start_consuming()