若 task 無法立即完成,為了提高 UX,通常不會讓使用者在線上等待 task 完成,而是告知使用者 task 已經在背景執行,完成後再通知使用者。
這個情境下,系統通常是這樣實作:建立一個 queue,將等待處理的 task 全部放進 queue 中,並讓 idle 的 consumer 自動從 queue 中取得一個 task 來執行 (當然也有可能是用 dispatch 的方式)。
假設有 A、B、C 三個使用者的 task 的放進 queue 來處理,一般情況下 queue 的狀態大概會向下面這個樣子:
=> A, C, B, C, A, A, C, A, B, C, B =>
但大多時候工作的分配通常不會這個平均,有時 A 使用者會需要大量處理資料,於是 queue 就會成下面這個樣子:
=> A, C, B, C, A, A, A .... (x1000), A, A, C, A, B, C, B =>
若這個情況下仍然使用相同的 consume 機制,會變成大多數的時間都在處理 A 的 tasks,導致 B、C 使用者的 task 被 (無限) 延後。
在一般狀態下這樣不是一件好事,就像去餐廳買飯,因為前面有個 50 人份的訂單排在你前面,就導致你買一個便當也要等一個多小時。在我自己碰到的狀況,這個時候餐廳通常會有二種作法:
- 直接關店不在讓其他客人點餐,專心處理大量訂單
- 餐廳仍然讓客人點餐,且會在客人與訂單有相同餐點時,一起處理餐點並同時出餐,所以客人會在大量訂單處理完之前,即可以收到餐點並離開
將上面提到的情況改用在 task queue 上,若遇到類似的問題,則可以考慮不同處理 queue 的方法。
例如為不同用途、類型的 task 分別建立不同的 queue,而每次都從個別的 queue 做一次 de-queue,這樣就不會因為 queue-A 大量的 task 卡住其他的 task:
queue for A: t999, t998, t997, ...., t3, t2, t1
queue for B: t3, t2, t1
queue for C: t6, t5, t4, t3, t2, t1
另外也可以考慮使用類似 Laravel API rate limit 的功能,分別記錄每個時段中,各類型的 task 共處理了多少次,若超過限制,則將原本 de-queue 出來準備處理的 task 在 in-queue 輪到較晚處理:
step 1: C, B, A, A, A, A, A, C, A
step 2: C, B, A, A, A, A, A, C
step 3: C, B, A, A, A, A, A
step 4: C, B, A, A, A, A
step 5: C, B, A, A, A // 連續處理二個 A task 了,到達上限
step 6: A, C, B, A, A
step 7: A, A, C, B, A
step 8: A, A, A, C, B
step 8: A, A, A, C
....
當然,用錢可以解決的問題都不是問題。把廚房加個 100 倍大,請一批廚師來炒菜,原本那 50 個訂單就根本沒在怕的啦 XD