RabbitMQ在分佈式系統中的應用

(此處已添加圈子卡片,請到今日頭條客戶端查看)

在本文中,我們首先要來探討一下使用分佈式系統的好處,以及如何藉助RabbitMQ來遷移至分佈式系統。然後我們也會學習到一些RabbitMQ的基本知識,最後會結合理論知識,學習一下如何用Python編程語言跟它進行交互。

分佈式系統

我們先假設一下,設想我們正在做一個電商網站。有用戶下訂單了,除了要在數據庫中新建一條記錄之外,很顯然我們還需要給用戶發送一封郵件通知他們訂單的詳情以及一份報表,以便將來某些時候用戶可能會用到。

如果畫流程圖的話,可能會是這樣:


RabbitMQ在分佈式系統中的應用

但是,上述這個解決方案的問題在於發送郵件和生成報表都是非常耗時的任務。如果我們在一個請求/響應週期當中,使用同一個進程來處理這2個耗時任務,那麼用戶將會從服務器端等待比較長的時間。甚至,你的應用服務將會變得很難擴展,因為越多用戶向服務器發起請求,就要花越多的時間去處理這些請求。而且,一旦在處理請求上花費很多時間,那麼就會給服務器增加負擔,更壞的情況下,如果服務器處理的時間很長,那麼服務器甚至會向用戶返回一個請求超時的錯誤。

解決辦法是讓Web應用解耦。Web應用可以首先將消息發送給消息代理(message broker),然後由消息代理將這些消息分發給能執行這些任務的消息消費者,這樣一來,Web應用就不用親自去執行這些任務了。

RabbitMQ在分佈式系統中的應用

基本上,消費者是相互之間能獨立分開工作的程序,並且一般情況下消費者程序來自web應用本身。而那些用來服務消費者的服務器,可以坐落在不同的地方。

除了能減輕服務器的壓力之外,分佈式系統的另外一個優勢是即使其中一個應用掛了,整套系統仍然還是可以工作的。假如其中一個消費者無法給用戶發送通知郵件了,那麼我們可以把它停掉。即使我們的消費者掛了,我們的web應用仍然可以繼續處理用戶的請求並且給代理髮送消息。一旦消費者恢復了,它馬上就能接收到之前web應用發來的消息。

現在我們來看一下RabbitMQ ,它是一個在生產者(Web應用)和消費者之間的中間人。

RabbitMQ 要點知識

RabbitMQ 是一個消息代理。它實現了不同的協議,但是最重要的是,它實現了AMQP(高級消息隊列協議),AMQP是一個用來在多個系統之間通過生產者,代理以及消費者交換消息的協議。

AMQP是如何工作的

現在,我們有一個生產者和一個消費者。生產者產生消息,消費者消費消息。在它們二者之間我們還有一個代理,代理從生產者那裡接收消息然後發送給消費者。

如果我們仔細研究下代理的工作原理,可能會有些難理解。代理由如下3個組件組成:

  1. Exchange – 接受生產者發送的消息,並將消息路由給Queue。
  2. Queue – 消息隊列,一種磁盤或者內存中用來存儲消息的數據結構;
  3. Binding – 連接Exchange和Queue,它告訴Exchange消息應該被傳送到哪個Queue。

當創建Exchange時,我們會指定一個exchange類型。當創建一個binding用來連接一個exchange和一個隊列時,我們會指定一個Binding key。 當發佈一條消息時,我們會指定一個exchange和一個routing Key。 哪條消息會被髮送給哪個queue,取決於下面這4個標準:

一共有4種類型的exchange:

  • Fanout. 這種類型的exchange只是簡單地發送消息給它知道的所有隊列。
RabbitMQ在分佈式系統中的應用

  • Direct. 這種類型的exchange會發送消息給符合routing key = binding key條件的隊列。
RabbitMQ在分佈式系統中的應用

  • Topic. 這種類型的exchange發送消息給符合routing key能部分匹配binding key的隊列。
RabbitMQ在分佈式系統中的應用

  • Header. 這種類型的exchange允許你跟你根據header的值來路由消息,而不是根據routing key。

最後,說點題外話,默認情況下RabbitMQ其實是有一個匿名的exchange。這個exchange會用隊列的名字跟routing key做匹配,而不是binding key。所以,如果你發佈一個routing key = “order”的消息到這個exchange,exchange將會路由這個消息到名為“order”的隊列。

Python操作RabbitMQ

下面我來演示一下如何創建一個簡單的Python程序。它可以幫助我們更好地理解生產者/代理/消費者這套流程。

我們會用到RabbitMQ客戶端Python庫Pika:

RabbitMQ在分佈式系統中的應用

我們來聲明一個類型為direct的exchange,然後發佈幾條消息到這個exchange中:

RabbitMQ在分佈式系統中的應用

如果你跑一下這個腳本,你將得到下面結果:

RabbitMQ在分佈式系統中的應用

你可以通過在終端執行以下命令,來檢查exchange是否真的創建成功:

RabbitMQ在分佈式系統中的應用

這個腳本里我們發送了2條消息。第一條消息的routing key = “order.notify”,第二條消息的routing key = “order.report”。不過這2條消息現在還不知道要發送到哪裡,因為我們在exchange中並沒有將它們綁定到任何的隊列(queue)。

下面讓我們來創建一個消費者,以便可以消費這些notify類型的消息:

RabbitMQ在分佈式系統中的應用

首先我們定義了一個隊列名為“order_notify”的queue,接著我們使用binding key = “order.notify”將其綁定到exchange。可以了,這樣當我們發佈一條routing key = “order.notify”的消息時,該條消息就會被髮送給這條queue,然後我們就可以在callback回調函數裡消費這條消息了。

可能最後一行的回調函數寫得有些疑惑:

RabbitMQ在分佈式系統中的應用

這行回調函數代碼向RabbitMQ發送了一個回執來告訴它消息已經接收並且被處理了,你RabbitMQ可以放心地將這條消息從你那邊刪除了。所以,如果一個消費者接收到了消息然後掛了,那樣的話消息也不會丟。

首先,執行notify.py啟動消費者:

RabbitMQ在分佈式系統中的應用

接著,使用之前創建的腳本publish.py發佈消息:

RabbitMQ在分佈式系統中的應用

你將會在終端看到如下輸出:

RabbitMQ在分佈式系統中的應用

從輸出可以看出,消息已經被成功消費掉了。

使用同樣的方式,我們可以創建一個report類型的消費者:

RabbitMQ在分佈式系統中的應用

總結

使用RabbitMQ作為消息代理是一個很不錯的選擇。這篇文章裡,我們學到了RabbitMQ的基礎知識以及如何使用Pika庫來跟它交互。但是在實際使用中,我們可能更願意使用諸如Celery這類的庫而不是Pika。所以,如果你願意掌握地更升入一些,你不妨可以去找來研究研究。

英文原文:https://apirobot.me/posts/distributed-systems-with-rabbitmq

譯者:瘋禽忘腫

相關推薦

推薦中...