一、应用场景
- 对于商城系统而言,定时任务会非常多,比如优惠券过期、订单定时关闭、2小时订单未支付自动取消等等。常用做法是写数据库指定过期时间,定时循环读表,当数据量一大,这种会严重影响性能,产生巨大的io。
- 而另外一些基于redis实现的延迟队列,也是基于pull拉模式去实现的,也会产生io,只是变成了内存读取。
- 一种好的做法是 push 推模式,服务端主动推送,这里就是rabbitmq的死信队列去实现
二、延迟队列原理
延迟队列一般分为两种:
- 基于消息的延迟和基于队列的延迟。基于消息的延迟是指为每条消息设置不同的延迟时间,那么每当队列中有新消息进入的时候就会重新根据延迟时间排序,当然这也会对性能造成极大的影响。
- 实际应用中大多采用基于队列的延迟,设置不同延迟级别的队列,比如 5s、10s、30s、1min、5mins、10mins 等,每个队列中消息的延迟时间都是相同的,这样免去了延迟排序所要承受的性能之苦,通过一定的扫描策略(比如定时)即可投递超时的消息。
- 这里主要通过rabbitmq的死信队列实现,需要同时声明一个缓冲队列和一个延迟队列,给缓冲队列设置统一消息过期时间,当消息过期后,会自动被重新投递到死信队列(死信队列也是一个普通队列),只需要监听死信队列即可
三、api项目使用说明
- 首先需要定义延迟消费队列:即不管是订单关闭,还是优惠券过期的后续处理,都需要去监听这个消息
在 application/libraries/mq/MqDlxQueue 去定义
- 运行声明脚本
php mq/StartDlx.php DELAY_QUEUE_TEST - 定义缓冲区队列:即所有投递到这个队列的消息,设置统一的过期时间,比如固定2小时关闭订单,可以设置统一的队列过期时间为2小时。
这里需要指定 dlx_key:即过期后投递到的死信队列的路由
需要指定 message_ttl:即统一的消息过期时间,单位 毫秒注意:该 缓冲队列的 key,不要有任何消费者,不然被其他消费者消费后,就不会过期
在 application/libraries/mq/MqQueue 去定义
- 声明 缓冲队列
php mq/Start.php BUFFER_QUEUE_TEST - 设置 延迟队列消费者
- 发送 消息到 缓冲区
-
测试效果: