在业务开发中,我们可能会遇到如下场景
例如 订单创建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取数据之前 上一个锁 删除完数据 再释放锁
有其他消费者来取的时候没有成功上锁 则等待锁释放
这样就不会出现多消费者时 会重复消费数据的问题
版权属于:本文是原创文章,版权归 吾梦小站 所有。
本文链接:https://www.nikm.cn/archives/54.html
本站所有原创文章采用 知识共享署名-非商业性使用 4.0 国际许可协议 进行许可。
您可以自由地转载和修改,但请务必注明文章来源并且不可用于商业目的。