消息队列之取消会议和自动退款处理
2020 年,因為新冠疫情原因很多行業(yè)交流會議被取消,這就涉及到如何處理退款,理想的場景是當我們在系統(tǒng)管理界面點擊取消按鈕時,就會在后臺自動批量處理所有已付款用戶的退款,然后給對應用戶發(fā)送會議取消和退款成功通知。
編寫任務類
對于這樣的耗時操作,顯然可以借助消息隊列來異步處理。我們創(chuàng)建一個對應的會議取消任務類?CancelConference,在?handle?方法中,我們在?each?方法的匿名函數中為每一個參會者處理退款,并發(fā)送郵件通知:
處理隊列任務超時
這里面,如果使用了第三方支付服務的話,退款需要涉及到網絡請求,郵件發(fā)送也是個耗時操作,而 Laravel 任務類默認的超時時間是 60s,可能不夠,你可以指定?--timeout?將其延長:
這里我們將其設置為了 300 分鐘,也就是 5 個小時,之所以設置這么長,是因為?CancelConference?會在一次執(zhí)行期間為所有參會者退款并發(fā)送通知。
當然,和?tries、--backoff?等參數一樣,你也可以通過任務類的屬性設置超時時間:
推薦這樣做,因為只有這個任務需要設置很長的超時時間,其他任務不需要這么長。
避免隊列任務重復執(zhí)行
從隊列中獲取任務時,任務被標記為?reserved,這樣就不能被其他處理器獲取了,當任務執(zhí)行完成后,要么從隊列中移除,要么再次被推送到隊列重試。
但是如果隊列任務在執(zhí)行過程中隊列處理器進程崩潰導致任務執(zhí)行中斷,則永遠被標記為?reserved,不會再被執(zhí)行。
要避免這個問題,Laravel 為?reserved?狀態(tài)設置了超時時間,默認是 90s,可以在?queue.php?配置文件中配置這個值:
由于我們?yōu)?CancelConference?任務類設置了超時時間為 5 個小時,這會導致的一個問題是 90s 后,reserved?狀態(tài)失效,其他處理器可以獲取這個任務類執(zhí)行,出現同一個任務的重復執(zhí)行。
要解決這個問題,需要確保?retry_after?的值大于所有任務類的超時時間:
確保擁有足夠多的處理器
如果一個任務需要執(zhí)行 5 個小時,只有一個隊列處理器進程的話,其他隊列任務都會被堵塞,為此需要啟動多個處理器進程,但是也有可能所有處理器進程同時都在執(zhí)行這些耗時 5 小時的任務,一個解決辦法是將這些任務推送到獨立的隊列:
并為其分配足夠多的處理器進程:
使用獨立的連接處理耗時任務
按住葫蘆起了瓢,只有 1 個隊列任務耗時 5 小時,卻要連帶所有其他任務的?reversed?狀態(tài)需要 5 個多小時才能過期,這么做是不合適的,要解決這個問題,可以為該隊列設置獨立的連接:
然后在推送非常耗時的任務到隊列的時候指定連接:
或者在任務類中通過屬性值設置這個連接:
最后我們還可以在啟動處理器進程的時候指定隊列連接:
php artisan queue:work database-cancelations指定連接的隊列處理器進程只能消費該連接中的隊列任務。
將單任務拆分為多個子任務
我們的?CancelConference?任務類可能需要運行長達 5 個小時才能完成所有退款和郵件發(fā)送,在此期間這個隊列處理器進程只能處理這一個任務,如果在執(zhí)行過程中出現異常,又要從頭開始執(zhí)行,并且跳過已經處理過的退款,這里面存在的不可控和風險比較大。
為此,我們可以將這個耗時很長的單任務分發(fā)拆分為多個子任務的分發(fā),實現起來也不難,就是把原來一個任務處理所有退款和通知的實現代碼:
調整為一個任務只處理一個用戶的退款和通知,我們新建一個?RefundAttendee?任務類來處理這個工作,從外部傳入?RefundAttendee?的就不再是?Conference?實例,而是?Attendee?實例了:
這樣一來,我們就可以恢復到使用單個隊列連接,也不需要配置那些額外的超時字段了:
這里,我們實際上是在一個隊列任務(CancelConference)中將另一個隊列任務(RefundAttendee)推送到消息隊列進行處理。
總結
以上是生活随笔為你收集整理的消息队列之取消会议和自动退款处理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何制作趣味头像?分享几种制作头像的方法
- 下一篇: 隐藏app界面和桌面应用程序图标的方法