[精选] 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 协议》,转载必须注明作者和本文链接
最美的不是下雨天,而是和你一起躲过的屋檐!
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 16

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

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

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

1年前 评论
黑将军

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

1年前 评论
Weiwen (楼主) 1年前
陈先生

redis -> BLPOP

redis.io/commands/blpop/

1年前 评论
GDDD

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

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

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!