你知道Redis能夠實現延遲隊列嗎?

最近,又從新學習了下Redis,深深被Redis的魅力所折服,我才知道Redis不只能快還能慢(我想也這麼優秀o(╥﹏╥)o),簡直是個利器呀。算法

image

咳咳咳,你們不要誤會,本文很正經的啦!數據庫

好了,接下來回到咱們的話題,咱們都知道Redis是一種基於內存的單進程單線程數據庫(Redis6.0開始以後支持多線程啦!),處理速度都很是快。那麼爲什麼Redis又能慢呢?原來,這裏說的慢是指Redis能夠設置一些參數達到慢處理的結果。(這就是爲何Redis既能快又能慢啦!)服務器

那接下來開始講講咱們的楷模Redis在隊列中如何實現延時的狀況:數據結構

在咱們平常生活中,咱們能夠發現,多線程

  • 在淘寶、京東等購物平臺上下單,超過必定時間未付款,訂單會自動取消。
  • 打車的時候,在規定時間沒有車主接單,平臺會取消你的單並提醒你暫時沒有車主接單。
  • 點外賣的時候,若是商家在10分鐘還沒接單,就會自動取消訂單。
  • 收快遞的時候,若是咱們沒有點確認收貨,在一段時間後程序會自動完成訂單。
  • 在平臺完成訂單後,若是咱們沒有在規定時間評論商品,會自動默認買家不評論。
  • .......還有不少這樣的場景。

這時,咱們能夠想一想爲何要這樣作?分佈式

由於這樣能夠保證商品的庫存能夠釋放給其餘人購買,你能夠不用一直等待打車卻得不到回覆,你能夠及時換一家店點到外賣。性能

那麼這些狀況都是如何實現的呢?學習

這時咱們能夠看看這個圖,來看看消息延遲是如何處理的:插件

image

當用戶發送一個消息請求給服務器後臺的時候,服務器會檢測這條消息是否須要進行延時處理,若是須要就放入到延時隊列中,由延時任務檢測器進行檢測和處理,對於不須要進行延時處理的任務,服務器會立馬對消息進行處理,並把處理後的結果相應返會給用戶。線程

image

對於在延時任務檢測器內部的話,有查詢延遲任務和執行延時任務兩個職能,任務檢測器會先去延時任務隊列進行隊列中信息讀取,判斷當前隊列中哪些任務已經時間到期並將已經到期的任務輸出執行(具備實時性,會存在必定的時間偏差,由於這個是定時任務)。

這時,咱們能夠想想在Redis的數據結構中有哪些能進行時間設置標誌的命令?

是否是想到的 zset 這個命令,具備去重有序(分數排序)的功能。沒錯,你想對了呀!

咱們可使用 zset(sortedset)這個命令,用設置好的時間戳做爲score進行排序,使用 zadd score1 value1 ....命令就能夠一直往內存中生產消息。再利用 zrangebysocre 查詢符合條件的全部待處理的任務,經過循環執行隊列任務便可。也能夠經過 zrangebyscore key min max withscores limit 0 1 查詢最先的一條任務,來進行消費。

image

總的來講,你能夠經過如下兩種方式來實現((^▽^)若是你想到其餘方法,也能夠告訴我下呀~):

(1)使用zrangebyscore來查詢當前延時隊列中全部任務,找出全部須要進行處理的延時任務,在依次進行操做。

(2)查找當前最先的一條任務,經過score值來判斷任務執行的時候是否大於了當前系統的時候,好比說:最先的任務執行時間在3點,系統時間在2點58分),表示這個應該須要立馬被執行啦,時間快到了(沖沖衝,他來了他來了,他帶着死神的步伐來了)。

咱們能夠想想Redis來實現延時隊列有何優點呢?

其實,Redis用來進行實現延時隊列是具備這些優點的:

(1)Redis zset支持高性能的 score 排序。

(2)Redis是在內存上進行操做的,速度很是快。

(3)Redis能夠搭建集羣,當消息不少時候,咱們能夠用集羣來提升消息處理的速度,提升可用性。

(4)Redis具備持久化機制,當出現故障的時候,能夠經過AOF和RDB方式來對數據進行恢復,保證了數據的可靠性

這時候,會有小夥伴問了還有沒有其餘實現延時隊列的方式呀!emmm....固然有的,只有想不到的沒有作不到(O(∩_∩)O哈哈~,開玩笑)

1、用消息中間件實現延時隊列

(1)經過 RabbitMQ 來實現延時隊列,也就是用消息中間件來實現延時隊列啦~

方法一:在MQ中咱們能夠對Queue設置 x-expires 過時時間或者對 Message設置超時時間x-message-ttl。

(這裏要注意下:延時相同的消息咱們要扔到同一個隊列中,對於每個延時要創建一個與之對應的隊列—這是因爲MQ的過時檢測是惰性檢測的。)

方法二:咱們能夠用RabbitMQ的插件rabbitmq-delayed-message-exchange插件來實現延時隊列。達到可投遞時間時並將其經過x-delayed-type類型標記的交換機類型投遞至目標隊列。

image

(2)RocketMQ實現延時隊列

rocketmq在發送延時消息時,是先把消息按照延遲時間段發送到指定的隊列中(把延時時間段相同的消息放到同一個隊列中,保證了消息處理的順序性,可讓同一個隊列中消息延時時間是相同的,整個RocketMQ中延時消息時按照遞增順序排序,保證信息處理的前後順序性。)。以後,經過一個定時器來輪詢處理這些隊列裏的信息,判斷是否到期。對於到期的消息會發送到相應的處理隊列中,進行處理。

哈哈,目前RocketMQ只支持特定的延時時間段,1s,5s,10s,...2h,不能支持任意時間段的延時設置。有興趣的小夥伴能夠去了解下它是相關知識呀~

2、Kafka實現延時隊

Kafka基於時間輪自定義了一個用於實現延遲功能的定時器(SystemTimer),Kafka中的時間輪(TimingWheel)是一個存儲定時任務的環形隊列,能夠進行相關的延時隊列設置。

image

3、Netty實現延時隊列

Netty也有基於時間輪算法來實現延時隊列。Netty在構建延時隊列主要用HashedWheelTimer,HashedWheelTimer底層數據結構是使用DelayedQueue,採用時間輪的算法來實現。

image

4、DelayQueue來實現延時隊列

Java中有自帶的DelayQueue數據類型,咱們能夠用這個來實現延時隊列。DelayQueue是封裝了一個PriorityQueue(優先隊列),在向DelayQueue隊列中添加元素時,會給元素一個Delay(延遲時間)做爲排序條件,隊列中最小的元素會優先放在隊首,對於隊列中的元素只有到了Delay時間才容許從隊列中取出。這種實現方式是數據保存在內存中,可能面臨數據丟失的狀況,同時它是沒法支持分佈式系統的。

image
相關文章
相關標籤/搜索