redis_异步队列&延迟队列delay quene

异步消息队列

blpop | brpop

redis 127.0.0.1:6379> BLPOP LIST1 LIST2 .. LISTN TIMEOUT

如果列表为空,返回一个 nil 。 否则,返回一个含有两个元素的列表,第一个元素是被弹出元素所属的 key ,第二个元素是被弹出元素的值。

redis 127.0.0.1:6379> BLPOP list1 100

在以上实例中,操作会被阻塞,如果指定的列表 key list1 存在数据则会返回第一个元素,否则在等待100秒后会返回 nil 。
在这里插入图片描述

异步队列

消息中间件 RabbitMQ,kafka、RocketMQ等消息中间件

redis 轻量级消息队列

redis 通过list数据结构实现消息队列,主要命令

  • lpush和rpush入队列
  • lpop和rpop出队列
  • blpop和brpop阻塞式出队列

在这里插入图片描述

$redis = new Redis();
$redis->connect('127.0.0.1', 6379);

//发送消息
$redis->lPush($list, $value);

//消费消息
while (true) {
    try {
        $msg = $redis->rPop($list);
        if (!$msg) {
            sleep(1);
        }
        //业务处理
     
    } catch (Exception $e) {
        echo $e->getMessage();
    }
}

如果队列长时间是空的,那么pop就不会不断的循环,这样会导致redis的QPS升高,影响性能,所以用sleep来解决,当没有消息的时候阻塞一段时间,同时带来问题,sleep会增加消息处理延迟增加,这个时候可以通过blpop、brpop来阻塞读取队列

blpop/brpop在队列没有数据的时候,会立即进入休眠状态,一旦数据到来,则立刻醒过来。消息的延迟几乎为零。用blpop/brpop替代前面的lpop/rpop,就完美解决了上面的问题。

还有一个需要注意的点是我们需要是用try/catch来进行异常捕获,如果一直阻塞在那里,Redis服务器一般会主动断开掉空链接,来减少闲置资源的占用。

延时队列

zRangeByScore

redis 127.0.0.1:6379> ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]

在这里插入图片描述
你是否在做电商项目的时候会遇到如下场景:

  • 订单下单后超过一小时用户未支付,需要关闭订单
  • 订单的评论如果7天未评价,系统需要自动产生一条评论

这个时候我们就需要用到延时队列了,顾名思义就是需要延迟一段时间后执行。Redis可通过zset来实现。我们可以将有序集合的value设置为我们的消息任务把value的score设置为消息的到期时间,然后轮询获取有序集合的中的到期消息进行处理。

$redis = new Redis();
$redis->connect('127.0.0.1', 6379);

$redis->zAdd($delayQueue,$tts, $value);

while(true) {
    try{
        $msg = $redis->zRangeByScore($delayQueue,0,time(),0,1);
        if($msg){
            continue;
        }
        //删除消息
        $ok = $redis.zrem($delayQueue,$msg);
        if($ok){
            //业务处理
        }
    } catch(\Exception $e) {

    }
}

这里又产生了一个问题,同一个任务可能会被多个进程取到之后再使用 zrem 进行争抢,那些没抢到的进程都是白取了一次任务,这是浪费。解决办法:将 zrangebyscorezrem使用lua脚本进行原子化操作,这样多个进程之间争抢任务时就不会出现这种浪费了。

变成
先获取在删除====》业务操作,操作失败,重新加入队列,怕入队失败,做个日志监控,重发
发布了53 篇原创文章 · 获赞 36 · 访问量 5563

猜你喜欢

转载自blog.csdn.net/qq_39787367/article/details/103978080