限時訂單實現方案(DelayQueue、ActiveMq)
一、在各種電商網站下訂單後會保留一個時間段,時間段內未支付則自動將訂單狀態設定為已過期。
二、解決方案
1、輪詢資料庫:實現一個定時器,每隔一段時間去檢查一遍資料庫裡的所有訂單,檢視其狀態是否是未支付並且已經到期。並修改這些資料的狀態為已過期。
優點:方法簡單,容易實現
缺點:訂單狀態處理不及時,輪詢資料庫的次數中可能很多都並沒有修改訂單(做的無用功),資料庫頻繁多次被連線浪費資料庫資源開銷。
因此以上方式實際開發中基本不予採用。開發中真正實現限時訂單採用以下兩種方案:
2、Java本身的解決方案--DelayQueue,延時佇列
核心思想如圖:
(1)、使用者下單,儲存訂單到資料庫的同時,將該訂單以及訂單的過期時間推入DelayQueue
(2)、啟動一個檢查訂單到期的執行緒,該執行緒使用delayQueue的take()方法獲取到期訂單,該方法為阻塞方法,如果當前沒有到期訂單,該方法會一直阻塞等待,直到獲取到訂單後繼續往下執行。
(3)、當take()獲取到一個到期訂單後,該執行緒按獲取到的訂單的id去資料庫查詢訂單並去檢查訂單狀態,如果為未支付,則將狀態修改為已過期
2.1、SpringBoot框架下程式碼實現
延時佇列實體Bean
延時訂單業務處理介面
延時訂單業務處理實現類
如果我們只實現了以上的程式碼,會存在一個很嚴重的問題,因為延時訂單是存在DelayQueue中的,而DelayQueue是存在記憶體中的,那麼當系 統重啟後,DelayQueue中的資料就被清空了,因此當系統重新啟動的時候,需要在訂單的實現類中去做一個檢索資料庫訂單的操作,將已過期未支付的設定為已過期,將未過期未支付的重新推入DelayQueue佇列中。程式碼如下:
@PostConstruct 註解不重複解釋,上面程式碼中有註釋
2.2、JFinal框架下程式碼實現
由於專案中介面工程使用的是JFinal框架,SpringBoot框架下實現限時訂單很簡單,而JFinal框架下實現有一點小麻煩,特別在此進行分享
(1)ItemVo類和SpringBoot下的一模一樣。不再重複貼程式碼
(2)延時訂單業務介面
(3)延時訂單業務處理實現類
不是使用spring的註解,這兩個方法在JFinal框架下怎麼呼叫,見下圖
首先了解一下,在JFinal框架下會有一個初始化類JFinalConfig,繼承該類可以做專案的一些初始化操作
該類有兩個方法:
afterJFinalStart() :當JFinal框架初始化完成後執行,僅執行一次
beforeJFinalStop() :當JFinal框架關閉之前執行,僅執行一次
那麼我們的延時訂單執行緒初始化與系統重啟後檢測資料庫訂單的操作就可以放到afterJFinalStart() 裡面進行執行
系統關閉執行緒中斷執行程式碼:
以上就完整實現了限時訂單的功能。
3、ActiveMq訊息中介軟體實現方案
使用DelayQueue方案適合在單臺伺服器上,如果在分散式環境下,DelayQueue方案則還需要進行改進,存在的問題就是多個伺服器會搶奪同一個訂單,解決方案就是分割槽處理,每個伺服器只負責自己的訂單,不管其他伺服器上的訂單。
使用DelayQueue的方案在功能上比較好的實現了限時訂單的功能,但是可擴充套件性和伸縮性並不好,那麼接下來使用ActiveMq實現,既能實現功能,也能更好的擴充套件和伸縮(訊息中介軟體的特性就是實現系統的解耦)
實現步驟:
1、使用者下單儲存到資料庫的同時使用訊息生產者傳送一條訊息到ActiveMq訊息佇列,注意:並不是呼叫send就馬上傳送,而是根據過期時間進行延遲傳送。時間到期了才會傳送這條訊息到訊息佇列中。
2、當訊息佇列收到該訊息的時候,將訊息轉發給訂閱了該佇列的消費者,消費者收到訊息就去做訂單狀態檢查
(1)、介面實現類
(2)、訊息生產者--傳送訂單到期的訊息到訊息佇列(按過期時間延遲傳送)
(3)、訊息消費者--修改訂單過期狀態
以上三個類就是使用ActiveMq訊息中介軟體實現限時訂單的所有程式碼。呼叫程式碼很簡單就是儲存訂單的同時將訊息傳送到訊息佇列,不在贅述。