MQ 实现信息延迟投递

Posted Json2011315

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MQ 实现信息延迟投递相关的知识,希望对你有一定的参考价值。

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 实现信息延迟投递的主要内容,如果未能解决你的问题,请参考以下文章

MQ 实现信息延迟投递

消息队列(MQ)消息延迟及过滤设计方案

如何在MQ中实现支持任意延迟的消息?

如果我是核酸系统架构师,我会这么用MQ。。。

聊聊业务系统中投递消息到mq的几种方式

RabbitMQ RabbitMQ高级特性