在业务开发中,我们可能会遇到如下场景
例如 订单创建10分钟后,如果未付款,则关闭订单
依次类推的延迟操作

我们可以使用redis 有序集合 做一个延迟队列
下单时 存入集合 值为订单号 score为创建订单的时间戳
利用zrangebyscore查出 一小时之前的订单

生产者

<?php
//生产者
//连接redis
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);

//模拟生产环境 随机插入前一天到后一天的订单
$min = time() - 86400;
$max = time() + 86400;
//开始随机插入  50ms插入一次
while (true) {
    $rand_int = mt_rand($min,$max);
    $order_sn = 'SN'.date('YmdHis',$rand_int).mt_rand(100,999);
    $redis->zAdd('test_zset',$rand_int,$order_sn);
    usleep(1000000/20);
}

消费者

<?php
//消费者
//连接redis
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);

while (true) {
    //取出一小时之前的订单
    $time = time();
    $data = $redis->zrangebyscore('test_zset',0,$time - 3600);
    //没有取到数据 则休眠一秒
    if(empty($data)){
        echo "无过期订单\n";
        sleep(1);
        continue;
    }
    //从有序集合中删除取出的值
    foreach ($data as $k => $v) {
        $res = $redis->zrem('test_zset',$v);
    }
    //进行关闭未支付订单操作
    foreach ($data as $k => $v) {
        echo '订单号:'.$v."\n";
        usleep(1000000/20);//模拟业务操作耗时
    }
}

如果只是单消费者模式 这段代码没有问题
如果说,数据积攒过多,处理较慢,我们需要开启多个消费者来处理时
由于zrangebyscore取出数据时,没有删除,我们需要手动删除,所以取出以后,还没删除时,如果有其他消费者取数据,就会造成一笔订单多个消费者处理的问题
所以可以这样去做
用一个进程,专门用zrangebyscore取出需要操作的数据,存入一个队列,然后多个消费者从队列中取,就不会出现一个订单,被多个消费者处理的问题

多消费者新方案代码

<?php
//消费者
//连接redis
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);

while (true) {
    //取出一小时之前的订单
    $time = time();
    $lock = $redis->setnx('lock:test_zset',time() + 3600);
    if(empty($lock)){
        echo "有其他进程占用\n";
        sleep(1);
        continue;
    }
    $data = $redis->zrangebyscore('test_zset',0,$time - 3600);

    //没有取到数据 则休眠一秒
    if(empty($data)){
        echo "无过期订单\n";
        $redis->del('lock:test_zset');
        sleep(1);
        continue;
    }
    //从有序集合中删除取出的值
    foreach ($data as $k => $v) {
        $res = $redis->zrem('test_zset',$v);
    }
    $redis->del('lock:test_zset');
    //进行关闭未支付订单操作
    foreach ($data as $k => $v) {
        echo '订单号:'.$v."\n";
        usleep(1000000/20);//模拟业务操作耗时
    }
}

利用redis分布式锁 在利用zrangebyscore取数据之前 上一个锁 删除完数据 再释放锁
有其他消费者来取的时候没有成功上锁 则等待锁释放
这样就不会出现多消费者时 会重复消费数据的问题

最后修改:2020 年 11 月 05 日 05 : 19 PM
如果觉得我的文章对你有用,请随意赞赏