延迟任务应用场景

场景一:物联网系统经常会遇到向终端下发命令,如果命令一段时间没有应答,就需要设置成超时。

场景二:订单下单之后30分钟后,如果用户没有付钱,则系统自动取消订单。

场景三:过1分钟给新注册会员的用户,发送注册邮件等。

php 使用rabbitmq-delayed-message-exchange插件实现延迟功能

1.安装

下载后解压,并将其拷贝至(使用Linux Debian/RPM部署)rabbitmq服务器目录:/usr/local/rabbitmq/plugins中( windows安装目录\rabbitmq_server-version\plugins ).

2.启用插件

使用命令rabbitmq-plugins enable rabbitmq_delayed_message_exchang启用插件

rabbitmq-plugins enable rabbitmq_delayed_message_exchang

输出如下:

The following plugins have been enabled:
rabbitmq_delayed_message_exchange

通过rabbitmq-plugins list查看已安装列表,如下:

...
[ ] rabbitmq_delayed_message_exchange 20171215-3.6.x
...

3.机制解释

安装插件后会生成新的Exchange类型x-delayed-message,该类型消息支持延迟投递机制,接收到消息后并未立即将消息投递至目标队列中,而是存储在mnesia(一个分布式数据系统)表中,检测消息延迟时间,如达到可投递时间时并将其通过x-delayed-type类型标记的交换机类型投递至目标队列。

4.php实现过程

消费者 delay_consumer2.php:

<?php

//header('Content-Type:text/html;charset=utf8;');

$params = array(
'exchangeName' => 'delayed_exchange_test',
'queueName' => 'delayed_queue_test',
'routeKey' => 'delayed_route_test',
);
$connectConfig = array(
'host' => 'localhost',
'port' => 5672,
'login' => 'guest',
'password' => 'guest',
'vhost' => '/'
); //var_dump(extension_loaded('amqp')); //exit(); try {
$conn = new AMQPConnection($connectConfig);
$conn->connect();
if (!$conn->isConnected()) {
//die('Conexiune esuata');
//TODO 记录日志
echo 'rabbit-mq 连接错误:', json_encode($connectConfig);
exit();
}
$channel = new AMQPChannel($conn);
if (!$channel->isConnected()) {
// die('Connection through channel failed');
//TODO 记录日志
echo 'rabbit-mq Connection through channel failed:', json_encode($connectConfig);
exit();
}
$exchange = new AMQPExchange($channel);
//$exchange->setFlags(AMQP_DURABLE);//声明一个已存在的交换器的,如果不存在将抛出异常,这个一般用在consume端
$exchange->setName($params['exchangeName']);
$exchange->setType('x-delayed-message'); //x-delayed-message类型
/*RabbitMQ常用的Exchange Type有三种:fanout、direct、topic。       fanout:把所有发送到该Exchange的消息投递到所有与它绑定的队列中。       direct:把消息投递到那些binding key与routing key完全匹配的队列中。       topic:将消息路由到binding key与routing key模式匹配的队列中。*/
$exchange->setArgument('x-delayed-type','direct');
$exchange->declareExchange(); //$channel->startTransaction(); $queue = new AMQPQueue($channel);
$queue->setName($params['queueName']);
$queue->setFlags(AMQP_DURABLE);
$queue->declareQueue(); //绑定
$queue->bind($params['exchangeName'], $params['routeKey']);
} catch(Exception $e) {
echo $e->getMessage();
exit();
} function callback(AMQPEnvelope $message) {
global $queue;
if ($message) {
$body = $message->getBody();
echo '接收时间:'.date("Y-m-d H:i:s", time()). PHP_EOL;
echo '接收内容:'.$body . PHP_EOL;
//为了防止接收端在处理消息时down掉,只有在消息处理完成后才发送ack消息
$queue->ack($message->getDeliveryTag());
} else {
echo 'no message' . PHP_EOL;
}
} //$queue->consume('callback'); 第一种消费方式,但是会阻塞,程序一直会卡在此处 //第二种消费方式,非阻塞
/*$start = time();
while(true)
{
$message = $queue->get();
if(!empty($message))
{
echo $message->getBody();
$queue->ack($message->getDeliveryTag()); //应答,代表该消息已经消费
$end = time();
echo '<br>' . ($end - $start);
exit();
}
else
{
//echo 'message not found' . PHP_EOL;
}
}*/ //注意:这里需要注意的是这个方法:$queue->consume,queue对象有两个方法可用于取消息:consume和get。前者是阻塞的,无消息时会被挂起,适合循环中使用;后者则是非阻塞的,取消息时有则取,无则返回false。
//就是说用了consume之后,会同步阻塞,该程序常驻内存,不能用nginx,apache调用。
$action = '2'; if($action == '1'){
$queue->consume('callback'); //第一种消费方式,但是会阻塞,程序一直会卡在此处
}else{
//第二种消费方式,非阻塞
$start = time();
while(true)
{
$message = $queue->get();
if(!empty($message))
{
echo '接收时间:'.date("Y-m-d H:i:s", time()). PHP_EOL;
echo '接收内容:'.$message->getBody().PHP_EOL;
$queue->ack($message->getDeliveryTag()); //应答,代表该消息已经消费
$end = time();
echo '运行时间:'.($end - $start).'秒'.PHP_EOL;
//exit();
}
else
{
//echo 'message not found' . PHP_EOL;
}
}
}

生产者delay_publisher2.php:

<?php

//header('Content-Type:text/html;charset=utf-8;');

$params = array(
'exchangeName' => 'delayed_exchange_test',
'queueName' => 'delayed_queue_test',
'routeKey' => 'delayed_route_test',
); $connectConfig = array(
'host' => 'localhost',
'port' => 5672,
'login' => 'guest',
'password' => 'guest',
'vhost' => '/'
); //var_dump(extension_loaded('amqp')); 判断是否加载amqp扩展
//exit();
try {
$conn = new AMQPConnection($connectConfig);
$conn->connect();
if (!$conn->isConnected()) {
//die('Conexiune esuata');
//TODO 记录日志
echo 'rabbit-mq 连接错误:', json_encode($connectConfig);
exit();
}
$channel = new AMQPChannel($conn);
if (!$channel->isConnected()) {
// die('Connection through channel failed');
//TODO 记录日志
echo 'rabbit-mq Connection through channel failed:', json_encode($connectConfig);
exit();
}
$exchange = new AMQPExchange($channel);
$exchange->setName($params['exchangeName']);
$exchange->setType('x-delayed-message'); //x-delayed-message类型
/*RabbitMQ常用的Exchange Type有三种:fanout、direct、topic。       fanout:把所有发送到该Exchange的消息投递到所有与它绑定的队列中。       direct:把消息投递到那些binding key与routing key完全匹配的队列中。       topic:将消息路由到binding key与routing key模式匹配的队列中。*/
$exchange->setArgument('x-delayed-type','direct');
$exchange->declareExchange(); //$channel->startTransaction();
//RabbitMQ不容许声明2个相同名称、配置不同的Queue,否则报错
$queue = new AMQPQueue($channel);
$queue->setName($params['queueName']);
$queue->setFlags(AMQP_DURABLE);
$queue->declareQueue(); //绑定队列和交换机
$queue->bind($params['exchangeName'], $params['routeKey']);
//$channel->commitTransaction();
} catch(Exception $e) { } for($i=5;$i>0;$i--){
//生成消息
echo '发送时间:'.date("Y-m-d H:i:s", time()).PHP_EOL;
echo 'i='.$i.',延迟'.$i.'秒'.PHP_EOL;
$message = json_encode(['order_id'=>time(),'i'=>$i]);
$exchange->publish($message, $params['routeKey'], AMQP_NOPARAM, ['headers'=>['x-delay'=> 1000*$i]]);
sleep(2);
}
$conn->disconnect();

对于代码来讲,首先对于消费者核心代码

$exchange->setType('x-delayed-message'); //x-delayed-message类型
$exchange->setArgument('x-delayed-type','direct');

生产者核心代码

$exchange = new AMQPExchange($channel);
$exchange->setName($params['exchangeName']);
$exchange->setType('x-delayed-message'); //x-delayed-message类型
$exchange->setArgument('x-delayed-type','direct');
$exchange->declareExchange();

使用方法:先运行delay_consumer1.php,再运行delay_publisher1.php

运行效果:

RabbitMQ 入门教程(PHP版) 使用rabbitmq-delayed-message-exchange插件实现延迟功能的更多相关文章

  1. RabbitMQ入门教程(十六):RabbitMQ与Spring集成

    原文:RabbitMQ入门教程(十六):RabbitMQ与Spring集成 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https: ...

  2. RabbitMQ入门教程(十四):RabbitMQ单机集群搭建

    原文:RabbitMQ入门教程(十四):RabbitMQ单机集群搭建 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://b ...

  3. RabbitMQ 入门教程(PHP版) 延迟队列,延迟任务

    延迟任务应用场景 场景一:物联网系统经常会遇到向终端下发命令,如果命令一段时间没有应答,就需要设置成超时. 场景二:订单下单之后30分钟后,如果用户没有付钱,则系统自动取消订单. 场景三:过1分钟给新 ...

  4. 转 RabbitMQ 入门教程(PHP版) 使用rabbitmq-delayed-message-exchange插件实现延迟功能

    延迟任务应用场景 场景一:物联网系统经常会遇到向终端下发命令,如果命令一段时间没有应答,就需要设置成超时. 场景二:订单下单之后30分钟后,如果用户没有付钱,则系统自动取消订单. 场景三:过1分钟给新 ...

  5. RabbitMQ 入门教程(PHP版) 第三部分:发布/订阅(Publish/Subscribe)

    发布/订阅 在上篇第二部分教程中,我们搭建了一个工作队列.每个任务之分发给一个工作者(worker).在本篇教程中,我们要做的之前完全不一样——分发一个消息给多个消费者(consumers).这种模式 ...

  6. RabbitMQ 入门教程(PHP版) 第二部分:工作队列(Work queues)

    工作队列 在第一篇教程中,我们已经写了一个从已知队列中发送和获取消息的程序.在这篇教程中,我们将创建一个工作队列(Work Queue),它会发送一些耗时的任务给多个工作者(Works ). 工作队列 ...

  7. RabbitMQ 入门教程(PHP版) 第一部分:Hello World

    abbitMQ是一个消息代理.它的核心原理非常简单:接收和发送消息.你可以把它想像成一个邮局:你把信件放入邮箱,邮递员就会把信件投递到你的收件人处.在这个比喻中,RabbitMQ是一个邮箱.邮局.邮递 ...

  8. RabbitMQ 入门教程(PHP版) 简单Demo

    RabbitMQ的关键字说明 (1)Broker:经纪人.提供一种传输服务,维护一条从生产者到消费者的传输线路,保证消息数据能按照指定的方式传输.粗略的可以将图中的RabbitMQ Server当作B ...

  9. RabbitMQ 入门教程(PHP版) 第六部分:远程调用(RPC)

    在云计算环境中,很多时候需要用它其他机器的计算资源,把一部分计算任务分配到其他节点来完成.RabbitMQ 如何使用 RPC 呢?下面将会通过其它节点完成斐波纳契示例. 流程图  当客户端启动时,它 ...

随机推荐

  1. keras模块学习之层(layer)的使用-笔记

    本笔记由博客园-圆柱模板 博主整理笔记发布,转载需注明,谢谢合作! keras的层主要包括: 常用层(Core).卷积层(Convolutional).池化层(Pooling).局部连接层.递归层(R ...

  2. 阿里云 Windows Server 2012 密码过期设置

    不加入域的情况下: 1.服务器管理器>工具>本地安全策略>账户策略>密码策略>密码最长使用期限(修改为0天)或者禁用密码复杂度要求 参考:https://blog.csd ...

  3. CTSC2010 珠宝商

    珠宝商 题目描述 Louis.PS 是一名精明的珠宝商,他出售的项链构造独特,很大程度上是因为他的制作方法与众不同.每次 Louis.PS 到达某个国家后,他会选择一条路径去遍历该国的城市.在到达一个 ...

  4. sizeof的注意点

    sizeof('a')的值为4.因为此处‘a’是独立存在的一个字符(没有赋值给其它变量),实际上就是一个整型数,占4个字节,即此处‘a’对应的ascii码的十进制为整数97.(貌似解释得有些牵强,但事 ...

  5. win10 安装python模块objgraph+PyCharm环境配置

    1. 打开win10的命令行窗口 2.在命令行中输入python -m pip install objgraph,系统会自动帮忙安装 3.安装完成后,可以用命令python -m pip list查看 ...

  6. logstash-output-jdbc遇到connection is not available,request time out after 10000ms的问题解决

    上一篇logstash-output-jdbc使用中提到“运行bin/logstash -f test.conf时可能提示注册插件失败”,通过分析详细的错误日志,发现其赫然写着“connection ...

  7. js原型和原型链的问题

    <script> //js原型和原型链的概念 functionperson(name){ this.name=name; } person.prototype.age=18; person ...

  8. 自用ajax的json请求

    function singlePriceSubmit(){ var json={}; json["area"]=areaStr1; json["goodCateIdStr ...

  9. 97: cf 983E 倍增+树套树

    $des$一棵 $n$ 个点的树,树上有 $m$ 条双向的公交线路,每条公交线路都在两个节点之间沿最短路径往返.$q$ 次询问从一个点要到达另一个点,在只坐公交的情况下,至少需要坐几辆公交车:或者判断 ...

  10. 关灯问题II 状压DP

    关灯问题II 状压DP \(n\)个灯,\(m\)个按钮,每个按钮都会对每个灯有不同影响,问最少多少次使灯熄完. \(n\le 10,m\le 100\) 状压DP的好题,体现了状压的基本套路与二进制 ...