Talking RabbitMQ with Python and Kombu

什么是 RabbitMQ

  • RabbitMQ是一個使用AMQP協(xié)議實現(xiàn)的開源消息隊列,它允許你從隊列中讀取信息和想隊列寫入信息。

  • 最基本的場景:一個應(yīng)用將消息發(fā)送到RabbitMQ服務(wù),然后服務(wù)將消息轉(zhuǎn)發(fā)(route)到指定的隊列。然后,另一個應(yīng)用監(jiān)聽這個隊列并且從這個隊列中接受消息并對它進行處理。

  • 發(fā)送消息的應(yīng)用叫做生產(chǎn)者,監(jiān)聽并消費消息的應(yīng)用叫做消費者。

  • 在RabbitMQ中,使用的是Exchange來進行消息的轉(zhuǎn)發(fā)。所以生產(chǎn)者會告訴服務(wù)器它想使用哪個Exchange,由Exchange來決定向哪個隊列發(fā)送消息(如下圖)。


  • 一個Exchange能夠使用很多不同的隊列。那么它是如何知道改向哪個隊列發(fā)送消息的呢? 實際上,Exchange有3中不同的類型(direct, fanout, topic)并且每種類型都有特定的方法來轉(zhuǎn)發(fā)消息。在本篇文章中,我將會使用direct類型的exchange,其他的exchange類型會在之后解釋。

  • 使用direct類型的exchange,當一個隊列被定義(創(chuàng)建)的時刻,它會通過routing key綁定在一個exchange上。routing key 是一個字符串(例如 blue, bob 等等)。Exchange會使用routing key 去匹配綁定在它上面的隊列所指定的routing key。找到任意的routing key之后,Exchange就會將消息發(fā)送到這個隊列,如果routing key匹配到了多個隊列,那么消息就會被發(fā)送到所有匹配到的隊列里。

  • 下面的例子描述了三個隊列綁定到一個Exchange的情景: A、B、C 三個隊列,隊列A和B使用名為BOB的routing key綁定到Exchange,隊列C使用名為BLUE的routing key 綁定到了Exchange。生產(chǎn)者使用BOB這個routing key發(fā)送消息時,Exchange只會把消息發(fā)送到隊列A和B,因為隊列C的routing key不匹配。


  • 現(xiàn)在,就像上面提到的,消費者為了從隊列中獲取消息,必須指定它所監(jiān)聽的隊列,或者聲明一個新的隊列并去監(jiān)聽它。注意:一個消費者能同時監(jiān)聽多個隊列。
    示意圖如下:


    clipboard (2).png
  • 消費者D個消費者E將會接受消息并處理。最后,關(guān)于消費者,要說明的一點是:它們必須確認消息已經(jīng)被處理完成,否則RabbitMQ無法知道這條消息應(yīng)不應(yīng)該移除。

編碼時間

使用Python3和Kombu來建立一個生產(chǎn)者

  • Kombu是一個使用AMQP協(xié)議的Python庫。
  • 安裝Kombu
    pip install kombu
  • 首先,我們需要與RabbitMQ服務(wù)建立鏈接。Kombu使用Connection類
    來進行這個操作
    from kombu import Connection
  • 然后,我們能夠?qū)嵗粋€對象并將RabbitMQ的url作為參數(shù)
    rabbit_url = "amqp://localhost:5672/" conn = Connection(rabbit_url)
  • 我們需要創(chuàng)建一個信道
    (channnel)來使用連接
    channel = conn.channel()
  • 然后,需要指定想要使用的Exchange。Kombu有Exchange類,我們創(chuàng)建了一個direct類型,名叫'example-exchange'的exchange
from kombu import Connection, Exchange 

exchange = Exchange("example-exchange", type="direct")
  • 接著是創(chuàng)建生產(chǎn)者。Kombu有Producer類,我們需要傳一個exchange對象和我們創(chuàng)建的channel,同時,還需要指定一個routing key
from kombu import Connection, Exchange, Producer
producer = Producer(exchange=exchange, channel=channel, routing_key="BOB")
  • 接下來,需要創(chuàng)建一個用來接收消息的隊列。生產(chǎn)者發(fā)送消息并不需要隊列,但是在這個例子中我們還是會創(chuàng)建一個。Kombu有Queue類。我們需要指定隊列的名字,隊列綁定的exchange和routing key
from kombu import Connection, Exchange, Producer, Queue

queuer = Queue(name="example-queue", exchange=exchange, routing_key="BOB")
  • 我們需要綁定隊列到exchcange然后再聲明它(此時隊列才真正地創(chuàng)建成功了)
queue.maybe_bind(conn)
queue.declare()
  • 最后,我們需要使用producer來發(fā)送一條消息
producer.publish("Hello there!")

一個完整的生產(chǎn)者就創(chuàng)建完成了,完整代碼如下:

from kombu import Connection, Exchange, Producer, Queue
rabbit_url = “amqp://localhost:5672/”
conn = Connection(rabbit_url)
channel = conn.channel()
exchange = Exchange(“example-exchange”, type=”direct”)
producer = Producer(exchange=exchange, channel=channel, routing_key=”BOB”)
queue = Queue(name=”example-queue”, exchange=exchange, routing_key=”BOB”)
queue.maybe_bind(conn)
queue.declare()
producer.publish(“Hello there!”)
  • 如果要檢查隊列是否被創(chuàng)建成功并且隊列中是否存在消息,可以使用RabbitMQ自帶的工具rabbitmqctl:
rabbitmqctl list_queues
  • 你會看到下面這樣的輸出
Listing queues …
example-queue 1

創(chuàng)建消費者

  • 除了并不需要綁定或者聲明之外(隊列已經(jīng)在之前創(chuàng)建生產(chǎn)者的時候聲明了),Connection,exchange,和queue的創(chuàng)建方法和之前創(chuàng)建生產(chǎn)者類似。
from kombu import Connection, Exchange, Queue, Consumer
rabbit_url = “amqp://localhost:5672/”
conn = Connection(rabbit_url)
exchange = Exchange(“example-exchange”, type=”direct”)
queue = Queue(name=”example-queue”, exchange=exchange, routing_key=”BOB”)
  • 每當消費者接受一個消息的時候,它會調(diào)用一個函數(shù)來處理這個消息。這被稱作回調(diào)函數(shù)。我們需要編寫這個回調(diào)函數(shù),它有兩個參數(shù): body和message。它只回打印出消息體并確認消息已經(jīng)被處理了。
def process_message(body, message):
    print(“The body is {}”.format(body))
    message.ack()
  • 現(xiàn)在我們需要創(chuàng)建消費者了。它需要一個connection,一個它將要監(jiān)聽的隊列,回調(diào)函數(shù)和它接受的消息類型(例如plain text或者json)。消費者是一個上下文管理器因此我們能使用with關(guān)鍵字來調(diào)用它。
with Consumer(conn, queues=queue, callbacks=[process_message], accept=[“text/plain”]):
  • 最后,我們需要可以通過調(diào)用drain_events函數(shù)來告訴connection去初始化所有的消費者。
conn.drain_events(timeout=2)
  • drain_events函數(shù)會等待2秒來讓消費者去消費一條消息,當隊列為空時,會拋出一個異常。如果沒有指定timeout函數(shù),將會一直等待,直到有一條可消費的消息產(chǎn)生。后面會對此有更多介紹。

  • 現(xiàn)在一個消費者就創(chuàng)建成功了,完整代碼如下:

from kombu import Connection, Exchange, Queue, Consumer
rabbit_url = “amqp://localhost:5672/”
conn = Connection(rabbit_url)
exchange = Exchange(“example-exchange”, type=”direct”)
queue = Queue(name=”example-queue”, exchange=exchange, routing_key=”BOB”)
def process_message(body, message):
  print(“The body is {}”.format(body))
  message.ack()
with Consumer(conn, queues=queue, callbacks=[process_message], accept=["text/plain"]):  
  conn.drain_events(timeout=2)
  • 運行這段代碼,將會產(chǎn)生如下的輸出
The body is Hello there!

現(xiàn)在你可以使用rabbitmqctl來檢測隊列是否為空了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容