本文建立在读者对RabbitMQ的基础了解上
本文延迟队列实现参照 https://blog.csdn.net/u012119576/article/details/74677835
对相关概念的理解参照 https://blog.csdn.net/samxx8/article/details/47417133
作为phper在实现诸如“课程开启后十分钟推送消息”,"订单生成后多少分钟自动取消"这类问题上会有一些问题,目前我能想到的三种解决方案有:
1. swoole有settimeout
2.exec设置linux系统一次性定时器 at命令(atd包) ,windows服务器上好像直接可以设置,但是最低间隔是一分钟还是30s,具体忘了。。。
3.crontab设置定时器检查(不建议。。。)
最近在弄RabbitMQ时,发现可以使用延迟队列实现这类需求。具体原理是新建两条队列绑定对应的交换机,其中一条设置消息延迟执行,在到期后使用交换机丢到与客户端连接的队列中,发送给客户端,具体参见代码。
channel();
//给cache发送 使其过期然后定向到另一个
//声明两个队列
$channel->exchange_declare('delay_exchange', 'direct',false,false,false);
$channel->exchange_declare('cache_exchange', 'direct',false,false,false);
$tale = new AMQPTable();
$tale->set('x-dead-letter-exchange', 'delay_exchange');//****很关键 表示过期后由哪个exchange处理
$tale->set('x-dead-letter-routing-key','delay_exchange');//****很关键 表示过期后由哪个exchange处理
//$tale->set('x-message-ttl',15000); //存活时长 下面的过期时间不能超过
$channel->queue_declare('cache_queue',false,true,false,false,false,$tale);
$channel->queue_bind('cache_queue', 'cache_exchange','cache_exchange');
$channel->queue_declare('delay_queue',false,true,false,false,false);
$channel->queue_bind('delay_queue', 'delay_exchange','delay_exchange');
$msg = new AMQPMessage('Hello World'.'3000',array(
'expiration' => intval(18000),
'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT
));
$channel->basic_publish($msg,'cache_exchange','cache_exchange');
echo date('Y-m-d H:i:s')." [x] Sent 'Hello World!' ".PHP_EOL;
$channel->close();
$connection->close();
channel();
$channel->exchange_declare('delay_exchange', 'direct',false,false,false);
$channel->exchange_declare('cache_exchange', 'direct',false,false,false);
$channel->queue_declare('delay_queue',false,true,false,false,false);
$channel->queue_bind('delay_queue', 'delay_exchange','delay_exchange');
echo ' [*] Waiting for message. To exit press CTRL+C '.PHP_EOL;
$callback = function ($msg){
echo date('Y-m-d H:i:s')." [x] Received",$msg->body,PHP_EOL;
$msg->delivery_info['channel']->basic_ack($msg->delivery_info['delivery_tag']);
};
//只有consumer已经处理并确认了上一条message时queue才分派新的message给它
$channel->basic_qos(null, 1, null);
$channel->basic_consume('delay_queue','',false,false,false,false,$callback);
while (count($channel->callbacks)) {
$channel->wait();
}
$channel->close();
$connection->close();