異步消息隊列
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 進行爭搶,那些沒搶到的進程都是白取了一次任務,這是浪費。解決辦法:將 zrangebyscore
和zrem
使用lua腳本進行原子化操作,這樣多個進程之間爭搶任務時就不會出現這種浪費了。
變成
先獲取在刪除====》業務操作,操作失敗,重新加入隊列,怕入隊失敗,做個日誌監控,重發