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

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

最近的一个项目遇到了这种情况,如果运单30分钟还没有被接单,则状态自动变为已取消。实现延迟消息原理如下,借用一张图:


实现方案

  1. 定时任务轮询数据库,看是否有产生新任务,如果产生则消费任务
  2. pcntl_alarm为进程设置一个闹钟信号
  3. swoole的异步高精度定时器:swoole_time_tick(类似javascript的setInterval)和swoole_time_after(相当于javascript的setTimeout)
  4. rabbitmq延迟任务

以上四种方案,如果生产环境有使用到swoole建议使用第三种方案。此篇文章重点讲述第四种方案实现


rabbitmq延时队列_javascript



rabbitmq延时队列_javascript_02rabbitmq延时队列_生产环境_03

1 <?php
2 require_once __DIR__ . '/../vendor/autoload.php';
3 use PhpAmqpLib\Connection\AMQPStreamConnection;
4 use PhpAmqpLib\Message\AMQPMessage;
5
6
7 $queue = "test_ack_queue";
8 $exchange = "test_ack_queue";
9 //获取连接
10 $connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
11 //从连接中创建通道
12 $channel = $connection->channel();
13
14 $channel->exchange_declare('delay_exchange', 'direct',false,true,false);
15 $channel->exchange_declare('cache_exchange', 'direct',false,true,false);
16
17 $tale = new \PhpAmqpLib\Wire\AMQPTable();
18 $tale->set('x-dead-letter-exchange', 'delay_exchange');
19 $tale->set('x-dead-letter-routing-key','delay_exchange');
20 //$tale->set('x-message-ttl',10000);
21
22 $channel->queue_declare('cache_queue',false,true,false,false,false,$tale);
23 $channel->queue_bind('cache_queue', 'cache_exchange','cache_exchange');
24
25 $channel->queue_declare('delay_queue',false,true,false,false,false);
26 $channel->queue_bind('delay_queue', 'delay_exchange','delay_exchange');
27
28
29 $msg = new AMQPMessage('Hello World',array(
30 'expiration' => 10000,
31 'delivery_mode' => AMQPMessage::DELIVERY_MODE_PERSISTENT
32
33 ));
34
35 $channel->basic_publish($msg,'cache_exchange','cache_exchange');
36 echo date('Y-m-d H:i:s')." [x] Sent 'Hello World!' ".PHP_EOL;
37
38
39
40
41 //while ($wait) {
42 // $channel->wait();
43 //}
44
45 $channel->close();
46 $connection->close();

task



rabbitmq延时队列_javascript_02rabbitmq延时队列_生产环境_03

1 <?php
2 require_once __DIR__ . '/../vendor/autoload.php';
3 use PhpAmqpLib\Connection\AMQPStreamConnection;
4 use PhpAmqpLib\Message\AMQPMessage;
5
6
7 //获取连接
8 $connection = new AMQPStreamConnection('localhost', 5672, 'guest', 'guest');
9 //从连接中创建通道
10 $channel = $connection->channel();
11
12
13 //$channel->queue_declare($queue, false, true, false, false);
14 //$channel->exchange_declare($exchange, 'topic', false, true, false);
15 //$channel->queue_bind($queue, $exchange);
16
17
18
19 $channel->exchange_declare('delay_exchange', 'direct',false,false,false);
20 $channel->queue_declare('delay_queue',false,true,false,false,false);
21 $channel->queue_bind('delay_queue', 'delay_exchange','delay_exchange');
22
23
24
25 function process_message(AMQPMessage $message)
26 {
27 $headers = $message->get('application_headers');
28 $nativeData = $headers->getNativeData();
29 // var_dump($nativeData['x-delay']);
30 echo date('Y-m-d H:i:s')." [x] Received",$message->body,PHP_EOL;
31 $message->delivery_info['channel']->basic_ack($message->delivery_info['delivery_tag']);
32
33 }
34
35
36 $channel->basic_qos(null, 1, null);
37 $channel->basic_consume('delay_queue', '', false, false, false, false, 'process_message');
38
39 function shutdown($channel, $connection)
40 {
41 $channel->close();
42 $connection->close();
43 }
44 register_shutdown_function('shutdown', $channel, $connection);
45
46 while (count($channel->callbacks)) {
47 $channel->wait();
48 }

work


rabbitmq延时队列_php_06


rabbitmq延时队列_生产环境_07



 rabbitmq延时队列_生产环境_08