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腳本進行原子化操作,這樣多個進程之間爭搶任務時就不會出現這種浪費了。

變成
先獲取在刪除====》業務操作,操作失敗,重新加入隊列,怕入隊失敗,做個日誌監控,重發
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章