作者:_BKing
地址:www.cnblogs.com/xiaowei123/p/13222710.html
最近,又重新學習了下Redis,深深被Redis的魅力所折服,Redis不僅能快還能慢(我想也這么優秀o(╥﹏╥)o),簡直利器呀

咳咳咳,大家不要誤會,本文很正經的啦!伙伴們跟我一起沖呀,我們一起去爬爬這座延時佇列的山峰,探一探它究竟到底有高,

那接下來開始我們的旅行啦~,我們都知道Redis是一種基于記憶體的單行程單執行緒資料庫(Redis6.0開始之后支持多執行緒啦!),處理速度都非常快,
那么為何Redis又能慢呢?原來,這里說的慢是指Redis可以設定一些引數達到慢處理的結果,(這就是為什么Redis既能快又能慢啦!)
那接下來開始講講我們的Redis在佇列中如何實作延時的情況:
在我們日常生活中,我們可以發現:
-
在淘寶、京東等購物平臺上下單,超過一定時間未付款,訂單會自動取消,
-
打車的時候,在規定時間沒有車主接單,平臺會取消你的單并提醒你暫時沒有車主接單,
-
點外賣的時候,如果商家在10分鐘還沒接單,就會自動取消訂單,
-
收快遞的時候,如果我們沒有點確認識訓,在一段時間后程式會自動完成訂單,
-
在平臺完成訂單后,如果我們沒有在規定時間評論商品,會自動默認買家不評論,
-
.......
這時,我們可以想想為什么要這樣做?
因為這樣可以保證商品的庫存可以釋放給其他人購買,你可以不用一直等待打車卻得不到回復,你可以及時換一家店點到外賣,
那么這些情況都是如何實作的呢?
這時我們可以看看這個圖,來看看訊息延遲是如何處理的:

當用戶發送一個訊息請求給服務器后臺的時候,服務器會檢測這條訊息是否需要進行延時處理,如果需要就放入到延時佇列中,由延時任務檢測器進行檢測和處理,對于不需要進行延時處理的任務,服務器會立馬對訊息進行處理,并把處理后的結果返會給用戶,

對于在延時任務檢測器內部的話,有查詢延遲任務和執行延時任務兩個職能,任務檢測器會先去延時任務佇列進行佇列中資訊讀取,判斷當前佇列中哪些任務已經時間到期并將已經到期的任務輸出執行(設定一個定時任務),
這時,我們可以想一想在Redis的資料結構中有哪些能進行時間設定標志的命令?
是不是想到的 zset 這個命令,具有去重有序(分數排序)的功能,沒錯,你想對了呀!
我們可以使用 zset(sortedset)這個命令,用設定好的時間戳作為score進行排序,使用 zadd score1 value1 ....命令就可以一直往記憶體中生產訊息,再利用 zrangebysocre 查詢符合條件的所有待處理的任務,通過回圈執行佇列任務即可,也可以通過 zrangebyscore key min max withscores limit 0 1 查詢最早的一條任務,來進行消費,

總的來說,你可以通過以下兩種方式來實作((▽)如果你想到其他方法,也可以告訴我下呀~):
(1)使用zrangebyscore來查詢當前延時佇列中所有任務,找出所有需要進行處理的延時任務,在依次進行操作,
(2)查找當前最早的一條任務,通過score值來判斷任務執行的時候是否大于了當前系統的時候,比如說:最早的任務執行時間在3點,系統時間在2點58分),表示這個應該需要立馬被執行啦,時間快到了(沖沖沖,他來了他來了,他帶著死神的步伐來了),
我們可以想一想Redis來實作延時佇列有何優勢呢?
其實,Redis用來進行實作延時佇列是具有這些優勢的:
-
Redis zset支持高性能的 score 排序,
-
Redis是在記憶體上進行操作的,速度非常快,
-
Redis可以搭建集群,當訊息很多時候,我們可以用集群來提高訊息處理的速度,提高可用性,
-
Redis具有持久化機制,當出現故障的時候,可以通過AOF和RDB方式來對資料進行恢復,保證了資料的可靠性
這時候,會有小伙伴問了還有沒有其他實作延時佇列的方式呀!emmm....當然有的,只有想不到的沒有做不到,關注公眾號Java技術堆疊回復福利可以獲取一份最新面試題資料,
一、用訊息中間件實作延時佇列
(1)通過 RabbitMQ 來實作延時佇列
方法一:在MQ中我們可以對Queue設定 x-expires 過期時間或者對 Message設定超時時間x-message-ttl,
(這里要注意下:延時相同的訊息我們要扔到同一個佇列中,對于每一個延時要建立一個與之對應的佇列—這是由于MQ的過期檢測是惰性檢測的,)
方法二:我們可以用RabbitMQ的插件rabbitmq-delayed-message-exchange插件來實作延時佇列,達到可投遞時間時并將其通過 x-delayed-type 型別標記的交換機型別投遞至目標佇列,

(2)RocketMQ實作延時佇列
rocketmq在發送延時訊息時,是先把訊息按照延遲時間段發送到指定的佇列中(把延時時間段相同的訊息放到同一個佇列中,保證了訊息處理的順序性,可以讓同一個佇列中訊息延時時間是相同的,整個RocketMQ中延時訊息時按照遞增順序排序,保證資訊處理的先后順序性,),之后,通過一個定時器來輪詢處理這些佇列里的資訊,判斷是否到期,對于到期的訊息會發送到相應的處理佇列中,進行處理,
注意 :目前RocketMQ只支持特定的延時時間段,1s,5s,10s,...2h,不能支持任意時間段的延時設定,有興趣的小伙伴可以去了解下它是相關知識呀~
二、Kafka實作延時隊
Kafka基于時間輪自定義了一個用于實作延遲功能的定時器(SystemTimer),Kafka中的時間輪(TimingWheel)是一個存盤定時任務的環形佇列,可以進行相關的延時佇列設定,

三、Netty實作延時佇列
Netty也有基于時間輪演算法來實作延時佇列,Netty在構建延時佇列主要用HashedWheelTimer,HashedWheelTimer底層資料結構是使用DelayedQueue,采用時間輪的演算法來實作,

四、DelayQueue來實作延時佇列
Java中有自帶的DelayQueue資料型別,我們可以用這個來實作延時佇列,DelayQueue是封裝了一個PriorityQueue(優先佇列),在向DelayQueue佇列中添加元素時,會給元素一個Delay(延遲時間)作為排序條件,佇列中最小的元素會優先放在隊首,對于佇列中的元素只有到了Delay時間才允許從佇列中取出,這種實作方式是資料保存在記憶體中,可能面臨資料丟失的情況,同時它是無法支持分布式系統的,

沖沖沖,小伙伴們!又是美好的一天,
推薦去我的博客閱讀更多:
1.Java JVM、集合、多執行緒、新特性系列教程
2.Spring MVC、Spring Boot、Spring Cloud 系列教程
3.Maven、Git、Eclipse、Intellij IDEA 系列工具教程
4.Java、后端、架構、阿里巴巴等大廠最新面試題
覺得不錯,別忘了點贊+轉發哦!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/49432.html
標籤:Java
上一篇:JAVA JSP 無法決議為型別
下一篇:Mybatis 連接資料庫出錯
