MQ 实现信息延迟投递

本文介绍如何通过rabbitmq_delayed_message_exchange插件实现消息延迟投递。该插件支持不同过期时间的消息按实际过期时间进行优先级投递,避免了设置多个交换机或队列的繁琐。文中提供了生产者和消费者的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.使用延迟插件 rabbitmq_delayed_message_exchange 实现队列信息延迟投递

安装步骤

1.1 下载插件

MQ插件地址:https://www.rabbitmq.com/community-plugins.html
下载延迟交换机插件地址:https://github.com/rabbitmq/rabbitmq-delayed-message-exchange/releases/tag/v3.8.0

说明选择适应的版本:
在这里插入图片描述

1.2 下载文件后执行

下载完成后是一个zip / ez 包,解压缩 将ez文件拷贝到rabbitmq安装目录下的plugins目录中

1.3 执行安装

执行安装 :

root@8a5497b5da92:/plugins#  rabbitmq-plugins enable rabbitmq_delayed_message_exchange

查看是否安装成功:

root@8a5497b5da92:/plugins#  rabbitmq-plugins list

在这里插入图片描述
1.4 重启mq 查看交换机类型

在这里插入图片描述

2.延迟交换机优点

1.第一条消息过期 TTL 是 30min,第二条消息 TTL 是 10min,延迟交换机不会堵塞,触发过期时间的数据会被交换机优先投递到队列中

2.当梯度非常多的情况下,比如 1 分钟,2 分钟,5 分钟,10 分钟,20 分钟,30 分钟,不需要设置过多的交换机或队列

3.代码实现

3.1 消费者代码实现 php consume.php

<?php
require_once __DIR__ . '/../vendor/autoload.php';
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Wire\AMQPTable;

try {
    $connection = new AMQPStreamConnection('192.168.88.130', 5672, 'guest', 'guest');
    $channel = $connection->channel();

    $exchange = 'order_delayed_exchange'; //交换机名
    $queue_name = 'order_delayed_queue'; //消息队列载体
    $exchange_type = 'x-delayed-message'; //交换机类型-延迟交换机
    $routing_key = 'order_delayed_key'; //路由关键字
    $durable = true; //是否持久化 true false


//第四个参数标识是否持久化【durable】
    $channel->exchange_declare(
        $exchange,
        //exchange类型为x-delayed-message
        $exchange_type,
        false,
        $durable,
        false,
        false,
        false,
        //此处是重点,$argument必须使用new AMQPTable()生成
        new AMQPTable([
            "x-delayed-type" => 'direct'
        ])
    );
//第三个参数标识是否持久化【durable】
    $channel->queue_declare($queue_name, false, $durable, false, false);
    $channel->queue_bind($queue_name, $exchange, $routing_key);

    echo ' [*] Waiting for logs. To exit press CTRL+C', "\n";

    $res = array();
    $callback = function ($msg) {
        #获取推送的信息
        echo ' [x] ', $msg->body, "\n";
        $result = $msg->body;

        //file_put_contents('aa.log',$res,FILE_APPEND);
        //信息标签
        echo $msg->delivery_info['delivery_tag'] . "\n";
        echo '接收时间:' . date("Y-m-d H:i:s", time()) . PHP_EOL;
        echo '接收内容:' . $result . PHP_EOL;

        //消息应答机制
        $result = mt_rand(0, 1); #模拟信息处理逻辑, 逻辑成功-true  逻辑处理失败-false
        if (!empty($result)) {
            #表示逻辑处理完成,并处理成功,使用ack机制应答信息(ack应答成功消息将从队列消失)
            echo 'success' . PHP_EOL;
            $msg->ack();
        } else {
            #表示逻辑处理失败,需要把信息重回队列,重新消费,使用nack机制
            echo 'fail' . PHP_EOL;
            echo "将消息打回,重回队列:";
            $msg->nack(true);
        }

        // $msg->delivery_info['channel']->basic_ack($msg->delivery_info['delivery_tag']); //手动发送ACK应答
        //exit();
    };

//在默认情况下,消息确认机制是关闭的。现在是时候开启消息确认机制,将basic_consumer的第四个参数设置为false(true表示不开启消息确认),并且工作进程处理完消息后发送确认消息。
    $channel->basic_consume($queue_name, '', false, false, false, false, $callback);

    while (count($channel->callbacks)) {
        $channel->wait();
    }
    $channel->close();
    $connection->close();
    return $res;
}catch (Exception $exception){
    print_r($exception->getMessage());
}

3.2 生产者代码 pushlist.php

<?php
require_once __DIR__ . '/../vendor/autoload.php';

//引入 php-amqplib 类库并且 使用必要的类:
use PhpAmqpLib\Connection\AMQPStreamConnection;
use PhpAmqpLib\Message\AMQPMessage;
use PhpAmqpLib\Wire\AMQPTable;


//创建一个 RabbitMQ 的连接:
$connection = new AMQPStreamConnection('192.168.88.130', 5672, 'guest', 'guest');
$channel = $connection->channel();


$exchange='order_delayed_exchange'; //交换机名
$exchange_type='x-delayed-message'; //交换机类型-延迟交换机
$durable = true; //标识是否持久化
$routing_key = 'order_delayed_key';//路由关键字
//声明一个队列给我们发送消息使用;然后我们就可以将消息发送到队列中;第四个参数标识是否持久化【durable】
$channel->exchange_declare(
    $exchange,
    //exchange类型为x-delayed-message
    $exchange_type,
    false,
    $durable,
    false,
    false,
    false,
    //此处是重点,$argument必须使用new AMQPTable()生成
    new AMQPTable([
        "x-delayed-type" => 'direct'
    ])
);
//设为confirm模式
$channel->confirm_select();

$tls_arr=[
    10000,
    30000,
    10000,
    20000
];
$tls=$tls_arr[rand(0,3)];
#$tls=20000;【可以自行测试,先推送过期时间是6s的数据,再推送2s过期的数据】
//推送的信息内容
$data = array(
    'eval_type' => 1,
    'ttl'=>'过期时间'.$tls,
    'content' => '提交的内容',
    'add_time' => date('Y-m-d H:i:s')
);

echo $data = json_encode($data);
$msg = new AMQPMessage(
    $data,
    [
        'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT,
        //此处是重点,设置延时时间,单位是毫秒 1s=1000ms,实例延迟20s
        'application_headers' => new AMQPTable([
            'x-delay' => $tls,
        ])
    ]
);
$channel->basic_publish($msg, $exchange, $routing_key);
//echo " send message :".$data." \n";

//消息发送状态回调(成功回调)
$channel->set_ack_handler(function (AMQPMessage $message) {
    echo "success:" . $message->body;
});
//失败回调
$channel->set_nack_handler(function (AMQPMessage $message) {
    echo "fail:" . $message->body;
});
$channel->wait_for_pending_acks();
$channel->close();
$connection->close();

4.执行效果

执行生成者:先推送一条6s的信息,再推送一条2s的信息:
在这里插入图片描述
执行消费者:
结果:2s过期的信息会优先被消费
在这里插入图片描述

### 使用 MQ 死信队列实现消息延迟消费的方法 #### 配置死信交换机和队列 为了实现消息的延迟消费,首先需要创建一个普通的交换机和队列,并指定该队列的消息在超过设定的时间存活 (TTL) 后转发至特定的死信交换机。这可以通过声明带有 `x-dead-letter-exchange` 和 `x-dead-letter-routing-key` 参数的队列来完成[^1]。 ```java @Bean public Queue delayQueue() { Map<String, Object> args = new HashMap<>(); args.put("x-message-ttl", 5000); // 设置消息过期时间为5秒 args.put("x-dead-letter-exchange", "dlx.exchange"); // 指定死信交换机名称 args.put("x-dead-letter-routing-key", "dlx.routingKey"); // 指定死信路由键名 return new Queue("delay.queue", true, false, false, args); } ``` #### 定义死信处理逻辑 当原队列中的消息达到其 TTL 并成为死信之后,这些消息将会按照预先定义好的规则被重新发布到另一个由开发者自定义的死信队列中等待进一步处理。此时可以在应用程序里监听这个新的队列并执行相应的业务操作[^2]。 ```java @RabbitListener(queues = "dead.real.queue") public void listenDeadLetterQueue(Message message) throws Exception { System.out.println("Received dead letter message: " + new String(message.getBody())); // 处理接收到的死信... } ``` #### 发送带有时效性的消息 发送方可以向正常的工作队列投递携带有效期限属性的消息对象;一旦这条记录超过了所给定的最大生命周期,则自动进入下一流程阶段——即变为一条“死信”,进而触发后续动作[^3]。 ```java rabbitTemplate.convertAndSend( exchangeName, routingKeyName, message, msg -> { msg.getMessageProperties().setExpiration(String.valueOf(ttlInMillis)); return msg; } ); ``` 通过上述方式能够有效地利用 RabbitMQ 提供的功能特性构建起一套简单而可靠的延时任务调度系统[^4]。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值