前菜
死信消息:
- 消息被拒绝(Basic.Reject或Basic.Nack)并且设置 requeue 参数的值为 false
- 消息过期了
- 队列达到最大的长度
过期消息:
在 rabbitmq 中存在2种方可设置消息的过期时间,第一种通过对队列进行设置,这种设置后,该队列中所有的消息都存在相同的过期时间,第二种通过对消息本身进行设置,那么每条消息的过期时间都不一样。如果同时使用这2种方法,那么以过期时间小的那个数值为准。当消息达到过期时间还没有被消费,那么那个消息就成为了一个 死信 消息。
- 队列设置:在队列申明的时候使用 x-message-ttl 参数,单位为 毫秒
- 单个消息设置:是设置消息属性的 expiration 参数的值,单位为 毫秒
延时队列:
在rabbitmq中不存在延时队列,但是我们可以通过设置消息的过期时间和死信队列来模拟出延时队列。消费者监听死信交换器绑定的队列,而不要监听消息发送的队列。
需求
有了以上的基础知识,我们完成以下需求:
需求:
用户在系统中创建一个订单,如果超过时间用户没有进行支付,那么自动取消订单。
分析:
1、上面这个情况,我们就适合使用延时队列来实现,那么延时队列如何创建
2、延时队列可以由 过期消息+死信队列 来时间
3、过期消息通过队列中设置 x-message-ttl 参数实现
4、死信队列通过在队列申明时,给队列设置 x-dead-letter-exchange 参数,然后另外申明一个队列绑定x-dead-letter-exchange对应的交换器。
实战
一.生产者
# utf-8
import time
import pika
host = 'localhost'
port = 5672
user = 'admin'
password = '123456'
connection = pika.BlockingConnection(
pika.ConnectionParameters(host=host, port=port, credentials=pika.PlainCredentials(user, password),
virtual_host="admin"))
channel = connection.channel()
# 过时消息接收交换机
delay_exchange = 'delay_exchange'
# 过时消息接收队列
delay_queue = 'delay_queue'
# 正常交换机
exchange = 'test_exchange'
# 正常队列
queue = 'test_queue'
# 设置过时队列参数
arguments = {
'x-message-ttl': 1000 * 3, # 过时时间 (毫秒)
'x-dead-letter-exchange': delay_exchange, # 过时结束后指向交换机(死信收容交换机)
'x-dead-letter-routing-key': delay_queue, # 过时结束后指向队列(死信收容队列)
}
# 声明收容交换机
channel.exchange_declare(exchange=exchange, exchange_type='fanout') # 声明收容队列
channel.queue_declare(queue=queue, durable=True, arguments=arguments)
# 收容队列和收容交换机绑定
channel.queue_bind(exchange=exchange, queue=queue)
for i in range(1, 10):
severity = 'info'
message = {'order':'{}'.format(i)}
channel.basic_publish(exchange=exchange,
routing_key=severity,
body=message)
print(" [x] Sent %r:%r" % (severity, message))
time.sleep(2)
二.消费者
正常消费者
# utf-8
import pika
host = 'localhost'
port = 5672
user = 'admin'
password = '123456'
connection = pika.BlockingConnection(
pika.ConnectionParameters(host=host, port=port, credentials=pika.PlainCredentials(user, password),
virtual_host="admin"))
channel = connection.channel()
# 正常交换机
delay_exchange = 'test_exchange'
# 正常队列
delay_queue = 'test__queue'
channel.exchange_declare(exchange=delay_exchange,
exchange_type='fanout')
result = channel.queue_declare(delay_queue,exclusive=True)
queue_name = result.method.queue
channel.queue_bind(exchange=delay_exchange,queue=queue_name,
routing_key='info') # 只处理routing_key为info的消息
print(' [*] Waiting for logs. To exit press CTRL+C')
def callback(ch, method, properties, body):
print(" [x] %r:%r" % (method.routing_key, body,))
channel.basic_consume(queue_name,callback,True)
channel.start_consuming()
异常消费者
# utf-8
import pika
host = 'localhost'
port = 5672
user = 'admin'
password = '123456'
connection = pika.BlockingConnection(
pika.ConnectionParameters(host=host, port=port, credentials=pika.PlainCredentials(user, password),
virtual_host="admin"))
channel = connection.channel()
# 过时消息接收交换机
delay_exchange = 'delay_exchange'
# 过时消息接收队列
delay_queue = 'delay__queue'
channel.exchange_declare(exchange=delay_exchange,
exchange_type='fanout')
result = channel.queue_declare(delay_queue,exclusive=True)
queue_name = result.method.queue
channel.queue_bind(exchange=delay_exchange,queue=queue_name,
routing_key='info') # 只处理routing_key为info的消息
print(' [*] Waiting for logs. To exit press CTRL+C')
def callback(ch, method, properties, body):
print(" [x] %r:%r" % (method.routing_key, body,))
channel.basic_consume(queue_name,callback,True)
channel.start_consuming()
如果消费者在30分钟内完成了订单支付也就是走test_queue队列消费消息,如果30分钟内未支付订单则走delay_queue队列异常处理(通常该脚本一直跑就行,更新订单状态).
总结
关键点设置过时消息接收交换机及队列
arguments = {
'x-message-ttl': 1000 * 3, # 过时时间 (毫秒)
'x-dead-letter-exchange': delay_exchange, # 过时结束后指向交换机(死信收容交换机)
'x-dead-letter-routing-key': delay_queue, # 过时结束后指向队列(死信收容队列)
}