2014年1月15日

RabbitMQ Message Queue 使用心得

此篇是我們在開發系統過程中導入Rabbitmq的心得分享。

為何要使用Message Queue

我們可以將一個Job(例如將一則訊息存到資料庫裡、發一封email...等)以一個的訊息來描述並將訊息放到Queue裡面,再建立一個或多個Consumer來將Queue裡的訊息一一取出後做後續的處理。

這好比說將要出貨的貨物(Message)一直往倉庫(Queue)裡放,有一台或多台的貨車(Consumer)不斷的將倉庫裡的貨物取出後做出貨的動作,貨車出完貨後會再回去倉庫取貨物。如果貨車數量愈多,那麼倉庫裡的貨物愈能更快的取出處理。

假設我們的系統loading非常的大,我們可以一直增加伺服器執行Consumer來消化Queue裡面的Message

使用情境

想像一下像Line的App,假設我們要傳一則訊息到某一個群組內的所有人,當server收到這樣的request時需做下列四項事情

  1. 將此訊息log到db裡(方便之後查詢)。
  2. 從db查詢該群組下有哪些人,例如有5個人,其中有4個人已允許加入群組邀請,1個尚未回覆。
  3. 將訊息傳送給4個已經加入群組的人,假設1個人目前沒有開啟App
  4. 發送Notification(GCM or iOS notification)給這1位不在線上的人

如果我們只用一個執行緒來完成上述所有的事情,那麼使用者在App中按下送出訊息的按鈕後必需要等待server依序執行完這4件事情後才會收到回應,但這樣的回應時間會過長而不被現在的使用者可接受。

加入Message Queue設計

若搭配Message Queue的話可使用下列的設計方式來加快系統的回應時間,請參考下圖 sample

系統建立下列3個Queue(紅色方塊)與3個Consumer(灰色)。註:Consumer是獨立的執行緒在執行。

  • Log Queue負責暫存要存到db的群組訊息,Consumer A會從Log Queue取出群組訊息並做寫入db的動作
  • Chat Queue負責暫存要發送的群組訊息,Consumer B會從Chat Queue取出群組訊息後至db取出該群組的人員名單並做傳送訊息的動作,傳送過程中如發現收件人的App沒有打開的話則將訊息丟至Notification Queue
  • Notification Queue負責暫存要發送GCM或iOS的訊息,Consumer C會從Notification Queue取出群組訊息後做發送notification的動作
  • Exchange(type=fanout)自動將訊息送給所有bind到此exchange的queue(請參考此篇文章)

以下說明流程

  1. 甲送一則訊息至server,該訊息含有發訊人、收訊群組、訊息內容…等資訊。
  2. server收到這個request後會將訊息丟進Exchange,此時server就馬上就回response回去了,接下來工作都交由Exchange, Quene, Consumer處理。
  3. Type為fanout的Exchange會將訊息同時傳送給Log Queue、Chat Queue
  4. Consumer A會從Log Queue取出群組訊息並做寫入db的動作
  5. Consumer B會從Chat Queue取出群組訊息後至db取出該群組的人員名單並做傳送訊息的動作,傳送過程中發現收件人丁的app沒有打開則將要給戊的訊息丟至Notification Queue
  6. Consumer C會從Notification Queue取出群組訊息後做發送notification的動作

註:步驟4~6是各別的執行緒在處理,並沒有順序關係

結語

可能有人會想到,如果將訊息存放到db,另外再建立執行緒定時的向db query資料是否也能達到相同的功能?這跟使用Message Queue的差別會在於如何得知會有新的資料要處理?使用Message Queue架構的話當Queue有資料時會自動的通知Consumer,如果用db的話則必需自行的定時向db做查詢的動作,這可能會比較耗resource。

其實不使用Message Queue的話也是可以做的出所要的功能,但若使用的話可以讓整個系統增加效率與擴充性。