Blog

91APP Queue System 解密

steven-tsai.jpg
Steven Tsai2020/06/24


今天想跟大家分享 91APP Queue System 是如何做的,這次分享的內容會從幾個面向切入討論與分析

  1. 什麼是 Queue?
  2. Queue 適合的使用場景
  3. 挑戰
  4. 91APP 怎麼做?
  5. 總結

什麼是 Queue?

Queue 又稱為隊列,我們可以將它想像成水管,你可以從一側一直將資料放進去,從另一側將資料取出來,是一個先進先出的資料結構。詳細的定義也可以參考 Wiki 的介紹

1


Queue 適合的使用場景

1.非同步處理

如下圖,是以成立訂單為例,前台在訂單成立時,需要發送通知信告知消費者訂單成立的相關訊息,而我們通常有幾種策略可以採用。

2

策略一:直接將寄信的程序埋在前台

這種方式最為直接也幾乎不會有延遲,但缺點卻很明顯,如寄信服務異常或延遲時可能會間接拖垮前台服務

策略二:排程定時撈取還未寄發信件的訂單

此種方法可以讓寄信邏輯與服務與前台脫鉤,但無可避免的會有系統性的延遲,例如:排程設定 5 分鐘,那一封信最多可能會延遲至 5 分鐘才寄達消費者手中

策略三:透過 Queue 來處理

前台在成立訂單時將要發送信件的任務資訊塞進 Queue 中,寄信服務從 Queue 中取得要寄送信件的任務與相關資訊來執行寄信,這個不僅讓寄信服務與前台解耦,如果寄信服務處理速度夠快,也可以做到幾乎即時的效果。

2.應付瞬間巨量

3

第二種常常使用 Queue 的情境是為了解決瞬間巨量,例如我們公司常常會在雙 11 時搶購期間瞬間湧入大量使用進行搶購,這時候 Queue 就成為一個很好的緩衝工具,前台在成立訂單時一樣將寄信的任務資訊塞進 Queue,寄信服務可以依據自己能處理的速度穩定的消化,無須為了搶購同時擴展前台與寄信服務。

當然如果認為寄信服務處理速度應該跟上訂單成立的速度,我們也可以同時擴展寄信服務,有個 Queue 做為緩衝,可調配的空間也會大上許多

4

3.系統間解耦

5

Queue 的第三種常用情境是拿來做為 系統間解耦 的工具,以圖中的案例來說,會員中心會在會員升等時將相關資訊塞到 Queue 中,對會員升等有興趣的各個服務只要訂閱這條 Queue,就能在會員升等時即時收到相關訊息,並做後續各服務職責內的處理。而會員中心並不知道到底誰關心會員升等的訊息,它只是忠實的將資訊送出即可。

如果公司因為業務的發展多個一組優惠券服務,希望能在升等時同時發送優惠券給會員,只要將優惠券服務開發完成後上線,並訂閱會員升等的 Queue,相關功能就完成,系統之間彼此透過 Queue 來達成解耦,不用因為需求的增減頻繁的異動會員中心。

6

比較 Queue 與 Load Balancer 的差異

如下圖,應該可以感覺到 Load Balancer 跟 Queue 有點類似,都有點在做附載平衡的感覺,那我們什麼時候該選用 Load Balance 什麼時候該選用 Queue 呢?

7

以我們的經驗來說,如果是需要較為即時性的服務,例如:商用 API,則選用 Load Balancer 較為合適,因為 Queue 具有緩衝的功能,換言之當瞬間巨量進來時,來不及處理的會暫時放在 Queue 中,這對於要求低延遲來說並不是個很好的解決方式。

所以通常這類狀況都會選用 Load Balancer,並且對於服務的資源監控較為嚴格,為了避免被瞬間巨量衝垮,可能在服務 CPU 或是 Memory 到達 60 ~ 70% 左右時就開始擴展節點,以應付不知何時會來的巨量請求。

相反的如果我們對於這服務的延遲容忍度較高,那 Queue 就是一個很好的選擇,對於面對 Queue 的服務來說,它永遠是用自己能處理的 最快 速度在消化 Queue 中的訊息,前面的瞬間巨量不會直接衝進服務之中進而壓垮服務,我們也可以透過滯後指標像是 Queue Length 來判斷 Queue 是否已經開始堆積,需要擴充服務節點來加快消化速度。

挑戰

  1. 如何簡化團隊串接 Queue 的門檻
  2. 100+ Queue 與 worker 如何開發、維運、監控與部署

91APP 怎麼做?

MessageWorker SDK 製作

我們對於一些比較常用 Queue,例如:RabbitMQ、NATS、NATS Streaming 的 SDK 再做一層封裝,讓團隊在串接 Queue 時使用的 API 一致,轉換 Queue 時只要更改設定即可完成

8

我們也在 SDK 中埋入了以下功能讓維運門檻降低

  • Graceful shutdown
  • 效率指標
  • 串接 CloudWatch

讓團隊專注於商業邏輯開發

91APP 自製了一個 Queue System 的底層,團隊只要依據規定的介面與 API 接口進行開發,交付 Artifact 後透過設定的方式即可將新的 Worker 掛入底層,並接上設定的 Queue 開始運作

9

Graceful shutdown

前面有提到 SDK 內建 Graceful Shutdown,這讓我們的 Worker 在收到關機指令時,會暫停再跟 Queue 收新的任務來做事,並且確實的將手上的任務執行完畢並通知 Queue 後才將自己關機,有了這個機制使我們能更放心的更新、部署 Queue System,不再需要擔心會不會有任務做到一半掉了的問題

10

對 Queue 進行分群

如果每個 Worker 都要搭配一條專屬的 Queue 來進行工作,公司有數百個 Worker 管理維運成本將變得很高,所以我們對 Queue 定義了 SLO (服務等級目標) 後做了適度的分群,對於 SLO 相同的 Worker 讓它使用同一條 Queue,並在底層自製了 Router (Process Pool) 來分配任務,它會依據工程師的設定有限度的自行擴展 Worker 執行任務,同時對於閒置太久的 Worker 也會對它進行關機來節省資源,這邊其實開發的方式並不太容易,有興趣的朋友可以參考敝司的首席架構師 Andrew 部落格 - 微服務基礎建設: Process Pool 的設計與應用,裡面有詳盡的開發介紹。

11

維運與監控

在探討怎麼維運與監控時必須得先問自己一個問題,我們究竟關心什麼? 對於 91APP 來說,我們關心 效率,所以對 Queue 都進行的 SLO 的定義,我們並不只是期待服務開發完上線來能跑就好,我們更希望它依照定義的目標與效率來執行,當服務沒達到水平時我們應該要被通知到並進一步修正與改善。

所以我們開始定義關於效率的指標,以下圖為例,我們對每則進入 Queue 的 Message 都標上時間,當 Message 被取出時計算它延遲了多久,Router 將工作分配給 Worker 執行時也會計算總共花費了多少時間,透過這幾個時間點來了解 Message 整個生命週期耗費了多少時間

12

有了這些數據搭配前面提到的串接 CloudWatch,我們可以得到下面這張圖,並且透過這張圖來了解 Queue 的狀況,這分鐘每筆 Message 執行的效率如何,整體是否有延遲 13

同時也收集每條 Queue 每分鐘收到 / 消化的 Message 總量,有沒有哪些 Message 違反了我們所定義的 SLO

14

搭配監控機器的 CPU 、Memory、Disk I/O 狀況,當有問題發生時我們可以更快的分辨出究竟延遲是機器太忙,還是外部服務變慢導致的現象

15

每種任務的特性可能都不太一樣,有些量不大但對於時間的敏感度較高,例如:簡訊,它可能瞬間只會有幾千筆,卻必須要在 5 秒內送到客戶手中,有了前述這些監控指標與數據,使我們能更容易抓出系統的瓶頸點,進而有機會優化。

圖中紅點是該分鐘違反 SLO 的 Message 數量,搭配 CloudWatch Alarm 與 Slack API 串接,能更即時的讓 RD 知道目前發生了什麼狀況,並且做出即時的處理。

16

17


結語

Queue 是一個很好用且成熟的工具,它可以幫助我們應付 瞬間巨量系統間解耦,但隨著服務的擴展與規模的變大,往往維運成本就會漸漸變高,隨時問問自己究竟關心什麼?想辦法讓這些關注點數據化,透過一些成熟的工具,例如:CloudWatch、Slack、Container... 等等,將有機會大幅度的降低維運成本。

© 2021 91APP, Inc. 版權所有
* Version v0.9.6 * 2021/05/29 14:44