天天看點

訂單逾時自動關閉的實作方案總結

轉載:http://www.architecy.com/archives/373

統一來說,業務有“在一段時間之後,完成一個工作任務”的需求。

實作這種定時任務有哪些方法呢,來總結一下想到的方法。

一、定時輪詢

這是一個比較直接的思路,啟動一個計劃任務,每隔一定時間處理一次,這種處理方式隻是适用比較小而簡單的項目。

假設訂單表的結構為:t_order(oid, finish_time, stars, status, …),更具體的,定時任務每隔一個小時會這麼做一次:

select oid from t_order where finish_time > 30minite and status=0;

update t_order set stars=5 and status=1 where oid in[…];

如果資料量很大,需要分頁查詢,分頁update,這将會是一個for循環。

定時輪詢的不足:

1、時效性差,會有一定的延遲,這個延遲時間最大就是每隔一定時間的大小,如果你設定每分鐘定時輪詢一次,那麼理論上訂單取消時間的最大誤差就有一分鐘,當然也可能更大,比如一分鐘之内有大量資料,但是一分鐘沒處理完,那麼下一分鐘的就會順延。

2、效率低。

二、被動取消

被動取消的方式很簡單:隻有當使用者查詢訂單資訊時,我們再判斷該訂單是否逾時,如果逾時再進行逾時邏輯的處理。

但是這種方式依賴于使用者的查詢操作觸發,這也就是說如果使用者不進行查詢訂單的操作,該訂單就永遠不會被取消。

不足:

1、會産生額外影響

比如統計,訂單數量等産生影響

2、影響使用者體驗

使用者打開訂單清單可能要處理大量資料,影響顯示的實時性。

三、延時消息

延時消息設計與實作

高效延時消息,包含兩個重要的資料結構:

(1)環形隊列,例如可以建立一個包含3600個slot的環形隊列(本質是個數組)

(2)任務集合,環上每一個slot是一個Set

同時,啟動一個timer,這個timer每隔1s,在上述環形隊列中移動一格,有一個Current Index指針來辨別正在檢測的slot。

Task結構中有兩個很重要的屬性:

(1)Cycle-Num:當Current Index第幾圈掃描到這個Slot時,執行任務

(2)Task-Function:需要執行的任務指針

訂單逾時自動關閉的實作方案總結

假設目前Current Index指向第一格,當有延時消息到達之後,例如希望3610秒之後,觸發一個延時消息任務,隻需:

(1)計算這個Task應該放在哪一個slot,現在指向1,3610秒之後,應該是第11格,是以這個Task應該放在第11個slot的Set中

(2)計算這個Task的Cycle-Num,由于環形隊列是3600格(每秒移動一格,正好1小時),這個任務是3610秒後執行,是以應該繞3610/3600=1圈之後再執行,于是Cycle-Num=1

Current Index不停的移動,每秒移動到一個新slot,這個slot中對應的Set,每個Task看Cycle-Num是不是0:

(1)如果不是0,說明還需要多移動幾圈,将Cycle-Num減1

(2)如果是0,說明馬上要執行這個Task了,取出Task-Funciton執行(可以用單獨的線程來執行Task),并把這個Task從Set中删除

使用了“延時消息”方案之後,“訂單48小時後關閉評價”的需求,隻需将在訂單關閉時,觸發一個48小時之後的延時消息即可:

(1)無需再輪詢全部訂單,效率高

(2)一個訂單,任務隻執行一次

(3)時效性好,精确到秒(控制timer移動頻率可以控制精度)

四、總結

環形隊列是一個實作“延時消息”的好方法,開源的MQ好像都不支援延遲消息,不妨自己實作一個簡易的“延時消息隊列”,能解決很多業務問題,并減少很多低效掃庫的cron任務。

另外,關于MQ的可達性、幂等性未來撰文另述。