背景
上一篇(靈感來襲,基於Redis的分散式延遲佇列)講述了基於Java DelayQueue和Redis實現了分散式延遲佇列,這種方案實現比較簡單,應用於延遲小,訊息量不大的場景是沒問題的,畢竟Java DelayQueue是佔用記憶體的。針對現用方案的不足,於是利用Redis的Sorted Set資料結構簡單實現分散式延遲佇列。
Sorted Set
- Redis 有序集合和集合一樣也是string型別元素的集合,且不允許重複的成員。
- 不同的是每個元素都會關聯一個double型別的分數。redis正是通過分數來為集合中的成員進行從小到大的排序。
- 有序集合的成員是唯一的,但分數(score)卻可以重複。
設計思路
- 使用Redis的Sorted Set作為中轉佇列,為防止延遲訊息量過大,維護多個Sorted Set,將延遲訊息按照hash值平均分佈到不同的Sorted Set中。
- 由於Sorted Set本身具備有序性,將延遲時間作為score值和延遲訊息繫結到一起存入Sorted Set中。
- 另起Java定時任務,每隔一定時間掃描所有Sorted Set,並通過ZRANGEBYSCORE操作取出符合條件的延遲訊息,然後放入目標佇列等待消費者消費。
程式碼實現
延遲佇列建立
根據queueName分別建立中轉佇列(Sorted Set)和 目標佇列key值,其中queueSize是中轉佇列的大小。
延遲訊息投遞
根據延遲訊息的hash值,平均分配到不同的中轉佇列(Sorted Set)中去。
中轉定時任務
通過分散式鎖來鎖定唯一的執行緒來執行延遲訊息遷移到目標佇列的操作。遍歷全部的中轉佇列,因為延遲訊息是和延遲時間戳關聯的,使用ZRANGEBYSCORE命令,取出延遲時間小於當前時間的50條訊息並通過LPUSH命令放入目標佇列裡。
延遲訊息消費
通過RPOP命令不斷的從目標佇列獲取延遲訊息,執行相應的消費邏輯。
總結
本文描述的實現方案還有諸多異常情況尚未考慮,比如生產者傳送失敗、消費者消費失敗的情況,無法保證極端情況下生產者和消費者兩端的資料一致性。該方案可以滿足業務量不是很大、延遲時間較長、允許部分資料可能丟失的場景,比如使用者簽到提醒,可以根據使用者簽到的時間,第二天在相應的時間點推送訊息提醒使用者繼續簽到。