应用场景
- 订单超过30分钟未支付,自动关闭
- 订单完成后, 如果用户一直未评价, 5天后自动好评
- 会员到期前15天, 到期前3天分别发送短信提醒
Redis Zset
原理
1.向zset中插入数据
score保存订单超时时间戳,订单如果30s后超时,将当时时间戳+30即可
value保存订单ID
2.轮询zset
根据score值范围查询【0,当前时间戳】即为要处理的订单
使用zrem删除此订单ID,若删除成功就开始处理订单超时逻辑
源码
<?php
Class DelayQueue
{
public $redis;
/**
* 连接mysql
* DelayQueue constructor.
* @throws Exception
*/
public function __construct()
{
$redis = new \Redis();
$res = $redis->connect('127.0.0.1', 6379, 5);
if ($res === false) {
throw new Exception('连接失败');
}
$this->redis = $redis;
}
/**
* 插入延时队列
* @param $key
* @param $value
* @param $score
* @throws Exception
*/
public function set($key, $value, $score)
{
$res = $this->redis->zAdd($key, ['NX'], $score, $value);
if ($res == 0) {
throw new Exception('入队列失败');
}
}
/**
* 处理延时队列
* @param $key
*/
public function deal($key)
{
while (true) {
$res = $this->redis->zRangeByScore($key, 0, time(), ['limit' => [0, 1]]);
echo '正常处理' . PHP_EOL;
if (empty($res)) {
sleep(1);
continue;
}
$value = $res[0];
$res = $this->redis->zRem($key, $value);
//在多线程处理时,只有删除成功的才有订单处理权
if ($res) {
//处理订单处理逻辑,更新订单状态,给用户发送提醒消息
var_dump(sprintf("订单【%s】30分钟未支付,已自动取消", $value));
//如果这里的任务处理失败,需要重新加入延时队列
}
}
}
}
$model = new DelayQueue();
//zset key名
$key = "order:delayqueue";
// 订单ID
$ordId = "S000001";
$model->set($key, $ordId, time() + 10);//10s之后处理
$ordId = "S000002";
$model->set($key, $ordId, time() + 30);//30s之后处理
$ordId = "S000003";
$model->set($key, $ordId, time() + 60);//60s之后处理
$model->deal($key);