[精选] php+redis是如何实现超时取消订单功能的呢?

业务场景:秒杀案例

秒杀业务逻辑:多个用户同时抢单,通过 mysql 行锁抢到的用户进入待支付页面(倒计时)。当用户没有支付订单超时时则取消该订单并归还库存。

应用

thinkphp+redis+workerman(可以自定义命令常驻)

1、thinkphp 安装 workerman。这里就过了,thinkphp 手册去找。

2、安装好 redis 及扩展。用宝塔的直接搞就完了,过。

3、生产者:用户创建订单向 redis 插入一条订单数据。

$redis = new \Redis();
$redis->connect('127.0.0.1',6379);
//$redis->auth('密码');//redis有密码就加
/**
*seckill_time为列队名称
*time() + $seckill['pay_time']为到期时间戳
*$newSeckillOrder->id为订单id,可以json字符串存储
*/
$redis->zAdd('seckill_time', time() + $seckill['pay_time'], $newSeckillOrder->id);

4、消费者:这里我们需要一个常驻内存一直来查询这个列队是否有消息,如果有就消费掉。

workerman


/**
 * 每个进程启动
 * @param $worker
 */
public function onWorkerStart($worker)
{
    //防止时间出问题
    date_default_timezone_set('PRC');

    $redis = new Redis();
    $redis->connect('127.0.0.1', 6379);
    //redis密码
    //$redis->auth('密码');//redis有密码就加此句

    if($worker->id === 0){
            //其他任务
    }
        //秒杀处理进程
    if($worker->id === 1){
        echo "启动秒杀任务!\n";
        //workerman定时器,每秒执行一次。
        Timer::add(1, function() use($redis) {
        //通过zRangeByScore查询seckill_time列队中0到当前时间戳的数据。
        $res = $redis->zRangeByScore('seckill_time', 0, time());
        //存在数据
        if (count($res) > 0) {
            foreach ($res as $k=>$v){
            //处理订单,$res[$k]为生产者存的订单id或数据。
            //.....
            //消费掉列队中的行数据
             $redis->zRem('seckill_time', $res[$k]);
             }
            //这步不用说都懂的吧!毕竟常驻内存,我们要管理好内存哦~
            unset($res);
         }
    });
   }
}

测试结果:

编辑

我这边是延迟 20 秒消费

如下:

编辑

消费掉后列队就空了哦~

使用该方案的原因:

可能有些人会问,workerman 有定时器,可以直接延迟操作呀?为什么如此多此一举呢?

原因很简单,如果 workerman 突然出问题掉了,那定时器延迟的一些数据是不是就终止了呢?这样会导致丢掉的数据无法去关闭订单啦!所以采用 redis 延迟消息列队,将数据存入 redis 中,即使 workerman 出问题掉了,重新启动 workerman 也可以将没消费的数据消费掉!

关于 redis 重启丢数据问题需要修改配置如下。

找到appendonly no 改为 appendonly yes

解决 redis 重启了就丢数据的问题。

当然也可以使用其他的消息中间件来解决,比如 mq,不过对于宝塔用户安装简介还是 redis 吧!

记住!不要定时器循环查数据库判断!不要定时器循环查数据库判断!不要定时器循环查数据库判断!

说三遍,数据库压力很大的~

当然该方案也适用拼团到期自动关闭该团等,可以自行脑补。

本作品采用《CC 协议》,转载必须注明作者和本文链接
最美的不是下雨天,而是和你一起躲过的屋檐!
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 16

加个订单补偿机制是不是更好一点呢?

2年前 评论
抄你码科技有限公司
2年前 评论
Weiwen (楼主) 2年前
黑将军 2年前

利用有序集合时间排序,超时取消订单

2年前 评论
黑将军

虽然 redis 很能扛,但是 1 秒钟刷一次,鸭梨也是很大 :joy:

2年前 评论
Weiwen (楼主) 2年前
GDDD

你这不行啊,既然是秒杀,并发肯定大,不应该通过 mysql 去控制。建议把库存写入 redis 队列,抢到的就可以去下单,然后做延迟队列去取消没下单支付的,库存重新入列

2年前 评论
Weiwen (楼主) 2年前
GDDD (作者) 2年前
Weiwen (楼主) 2年前
Weiwen (楼主) 2年前
GDDD (作者) 2年前
Weiwen (楼主) 2年前
GDDD (作者) 2年前