RabbitMQ - 延迟队列

一、延迟队列

1-1 简单介绍

​ 延迟队列:存储延迟消息的队列

​ 延迟消息:消息存入队列,等待一定时间后才交于消费者进行消费

1-2 应用场景

场景一 :订单系统

​ 用户下单的30分钟确认支付,若超过30分钟,则该订单被列为超时订单处理。可以使用延时队列,在30分的时候处理这个订单的状态。关闭订单,并且退还库存。

场景二:定时推送命令

​ 手机远程遥控家里的智能设备在指定的时间进行工作。可以使用延迟队列,将指令定时推送到智能设备进行处理。

二、RabbitMQ 实现延迟队列

RabbitMQ 本身不支持延迟队列,但是可以通过 死信队列(DLX)和 消息有效时间 (TTL)来实现延迟队列。

  • TTL - Time To Live
    • 设置消息有效时间,若超时,则消息变为 dead letter(死信)
    • Queue 的 x-expires 参数,Message 的 x-message-ttl 参数,都可以配置消息的有效时间
    • x-expires, x-message-ttl 同时存在,取最小的为准
    • 设置 Queue 的 x-expires,队列中所有消息都有相同的过期时间
    • 设置 Msg 的 x-message-ttl,则该过期时间仅对该消息有效。
  • DLX - Dead Letter Exchanges
    • 配置 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发送
    • 出现 DL 的情况
      • 消息或者队列的TTL过期
      • 队列达到最大长度
      • 消息被消费端拒绝(basic.reject or basic.nack)并且 requeue=false

RabbitMQ - 延迟队列_第1张图片

三、Python + Pika 实现

import datetime
import pika


def conn():
    credentials = pika.PlainCredentials('test', '123')
    connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost', credentials=credentials))
    if not connection.is_open:
        return
    channel = connection.channel()
    if channel.is_open is False:
        return
    return channel


def declare(channel):
    # 延迟交换机
    delay_exchange = 'delay.15min.ttl.exchange'
    # 延迟队列
    delay_queue = 'delay.15min.ttl.queue'

    # 延迟交换机死信收容交换机
    exchange_dlx = 'dlx.exchange'
    # 延迟队列死信收容队列
    queue_dlx = 'dlx.queue'

    # 声明收容交换机
    channel.exchange_declare(exchange=exchange_dlx, exchange_type='fanout', durable=True, auto_delete=True, )
    # 声明收容队列
    channel.queue_declare(queue=queue_dlx, durable=True, auto_delete=True, )
    # 收容队列和收容交换机绑定
    channel.queue_bind(exchange=exchange_dlx, queue=queue_dlx)

    # 设置延迟队列参数
    arguments = {
        # 'x-message-ttl': 1000 * 60 * 15,  # 延迟时间 (毫秒),15min
        'x-message-ttl': 1000 * 60 * 3,
        'x-dead-letter-exchange': exchange_dlx,  # 延迟结束后指向交换机(死信收容交换机)
        'x-dead-letter-routing-key': queue_dlx,  # 延迟结束后指向队列(死信收容队列)
    }

    # 声明延迟交换机、队列,并相互绑定;设置延迟队列的 ttl 和 xdl 跳转
    channel.exchange_declare(exchange=delay_exchange, exchange_type='fanout', durable=True, auto_delete=True, )
    channel.queue_declare(queue=delay_queue, exclusive=True, arguments=arguments, auto_delete=True, )
    channel.queue_bind(exchange=delay_exchange, queue=delay_queue)
    return queue_dlx, delay_exchange


def publish(channel, delay_exchange, msg, routing_key=''):
    channel.basic_publish(exchange=delay_exchange, body=msg, routing_key=routing_key)


def subscribe(channel, callback, queue):
    channel.basic_consume(callback, queue=queue, no_ack=True)


def sub_func(channel, body, envelope, properties):
    print(datetime.datetime.now())
    print(body)


if __name__ == '__main__':
    channel = conn()
    queue_dlx, exchange_dlx = declare(channel)
    subscribe(channel, sub_func, queue_dlx)
    # publish(channel, delay_exchange, 'hello')
    channel.start_consuming()

参考阅读

RabbitMQ 延迟队列 - Java 实现

你可能感兴趣的:(消息中间件)