在實際專案開發中,我們遇到了如下需求:
1,機票訂單下單後,如果30分鐘內未支付,則自動取消
2,產品上架後,24小時都沒有人買,則自動下架
3,一定時間後自動評價或者自動收貨
4,其他類似需求...
類似這樣的需求,我把它叫做延時任務,叫做定時任務不大合適,因為這類任務沒有固定的觸發時間,下面我們來分析總結下這類任務的解決方案:
a,定時輪詢資料庫
啟動乙個job(job排程與業務邏輯處理分離),定時掃瞄資料庫,執行的操作類似select * from *** where status='online' and created_at<=current_time-30分鐘,獲取到資料後,進行訂單取消操作。
優點:簡單,且可以支援集群擴充套件
缺點:(1)存在延遲,如果job一分鐘執行一次,那麼最大延遲就是1分鐘
(2)資料可能存在分布不均的情況,有可能超時的訂單都集中在某個小範圍時間段,其他時間段job相當於都在空跑
(3)如果訂單量巨大,每隔一分鐘掃瞄一次,效能較差
該解決方案,我們實際中也有使用,定時任務大約5分鐘跑一次,主要用於對時間要求不那麼敏感且訂單量較少(比如金融產品要求百萬或者幾百萬的起投額,這樣的業務單個訂單的金額巨大,但是總體的訂單量少)的場景。
b,jdk提供的延遲佇列
jdk提供了乙個現成的延遲佇列實現,delayqueue,delayqueuequeue = new delayqueue(),這個element必須要實現delayed介面,當佇列中的元素延遲到期後才能被取出來,可以通過在while迴圈中呼叫take方法獲取到期的元素。
優點:所有的操作都在記憶體中,效率高,延遲非常低
缺點:(1)伺服器如果重啟後,資料就全部丟失了
(2)如果訂單量巨大,可能出現記憶體不夠導致溢位,需要在初期充分預估記憶體大小
還有一種與這個類似的方法就是採用時間輪的方式,之前的文章中有介紹過,不過本質上也與方案b存在相同的問題。
c,jdk延遲佇列或者時間輪+定時補償任務
首先將訂單資訊(包含超時資訊)寫入到資料庫,然後再寫入必要的訂單有關資訊到jdk延遲佇列中。jdk中的延遲任務處理後更新資料庫中的訂單資訊。如果伺服器重啟資料丟失了,那麼由補償任務執行超時操作。
優點同b,缺點:還是可能出現記憶體不夠導致溢位,需要在初期充分預估記憶體大小
d,redis快取
根據方案b的缺點,害怕重啟,以及記憶體溢位,那我們把這些資料都存到redis(規劃專門的記憶體)中,這樣也不怕機器重啟和記憶體溢位了。可以利用redis提供的有序集合,具體可以看redisfans**上的文件介紹。
首先我們使用zadd命令,其member為訂單id,score為訂單超時時間。然後在while迴圈中命令zrangebyscore查詢超時時間小於等於當前時間的記錄(system.currenttime),執行超時操作,然後刪掉對應的member。
缺點:(1)redis需要進行額外的維護,redis還是有宕機的可能,宕機時主從切換的過程中可能丟失資料,需要解決這個問題(雖然可以通過配置保證資料不丟失,但會導致master拒絕客戶端的寫請求,另一種方法是也採用補償的方式處理這種情況)。
(2)如果同一時間超時的訂單數巨大,zrangebyscore直接獲取全部資料會有嚴重的效能問題,需要分頁獲取,這樣會增大延遲,此時可能需要進行分片,
(3)需要引入分布式鎖避免單個訂單被重複執行超時操作
e,使用訊息佇列延遲投遞功能
比如利用rabbitmq的特性(ttl和dead letter exchanges),我們建議兩個佇列queue1和queue2首先將訂單放入佇列queue1,設定這個訊息的時間為30分鐘,超過時間後被**到queue2,訊息者消費queue2即可。
優點:高效,利於橫向擴充套件,mq訊息的持久化可以保證可靠性
缺點:需要引入rabbitmq,需要對其進行額外的維護(如果本身的專案中就用到了rabbitmq,那麼可以採用這種方案)
分布式環境下實現延時任務的方案
1,某寶購買了一件商品時尚未付款,然後30分鐘後就會自動取消該訂單 2,某寶購買一件商品後,確認收貨後15天預設好評 類似這種功能我們叫延時任務,但是我們要怎麼去實現這樣的功能呢,下面就來說說我們的策略吧 啟動乙個job job排程與業務邏輯處理分離 定時掃瞄資料庫,執行的操作類似select fr...
利用Redisson實現分布式延時任務排程功能
定時任務是在編碼世界中經常遇到的問題,比如定時備份資料庫 定時重新整理快取等,可以通過linux定時任務完成,也可以通過框架如spring完成,但是在分布式場景中傳統單機可以完成功能就不太行了,所以需要借助其他工具來實現任務排程的功能 場景 在一些訂單場景中,使用者下單後會鎖定一些資源,然後使用者非...
分布式定時任務
在做springboot專案的時候,需要定時做對賬任務。但因為專案是集群部署,就存在多個pod例項的定時任務同時執行,存在重複性。怎麼保證集群中不重複地完成定時任務?下面給出本人總結的方案。對賬任務表 merchant idname is done 是否對賬,0沒有對賬 1商家102 商家20 3商...