基於Python語言使用RabbitMQ消息隊列(五)

Topics

在前面教程中我們改進了日誌系統,相比較於使用fanout類型交易所只能傻瓜一樣地廣播,我們用direct獲得了選擇性接收日誌的能力。

雖然使用direct類型交易所改進了我們的系統,但它仍然有所限制——它不能做基於多重條件(multiple criteria)的路由。
在日誌系統中我們可能不只是想要基於嚴重級別來訂閱日誌。也想要基於產生日誌的來源。你可能從unix的系統日誌工具(syslog unix tool)知道了這個概念,它就是基於嚴重級別 (info/warn/crit…)和設施(auth/cron/kern…)來路由日誌的。
這會給我們很大靈活性——我們可能只想監聽來自於‘cron’的嚴重錯誤(critical errors)和來自‘kern’的所有日誌。
爲了在我們的日誌系統中實現這個功能我們需要了解更復雜的topic類型交易所。

Topic 交易所

發往topic類型交易所的消息不能只有一個獨斷的路由鍵(routing_key)——它必須是個詞彙列表,詞與詞之間由‘.’來界定。可以是任何詞彙,但通常它們指定了一些與消息相關聯的特性。一些有效的路由鍵例子:”stock.usd.nyse”, “nyse.vmw”, “quick.orange.rabbit”,只要你想,在路由鍵裏可以加儘可能多的詞彙,上限是255 bytes。

綁定鍵必須是同樣的形式。topic交易所背後的邏輯與direct類似——使用特定路由鍵發送的消息會傳遞給所有擁有匹配的綁定鍵的隊列。但對於綁定鍵有兩種重要的特殊情形:

“*” (star) 正好代替一個詞.
“#” (hash) 能代替零個或多個詞.

可以很容易地用一個例子解釋:
這裏寫圖片描述
在這個例子中我們發送的消息都是描述動物的。將要發送的消息的路由鍵包含三個詞(兩個點號)。第一個詞描述的是敏捷性,第二個詞是顏色,第三個詞是種類:“<敏捷性>.<顏色>.<種類>”。

我們創建了三個綁定: Q1 綁定鍵是 “.orange.” , Q2 是 “..rabbit” 和 “lazy.#”.

這些綁定可以總結爲:

  • Q1 對所有橙色(orange)動物感興趣.
  • Q2想要知道關於rabbits的每件事情, 和關於lazy 類型動物的所有.

路由鍵設置爲 “quick.orange.rabbit” 的消息會同時傳送給兩個隊列。消息”lazy.orange.elephant” 也會傳送給兩個隊列。”quick.orange.fox”會傳送給第一個隊列。”lazy.brown.fox”只會傳送給第二個隊列。”quick.brown.fox” 不匹配任何綁定,所以它會被忽略。

如果我們破壞約定,發送帶有一個詞或四個詞的消息綁定,像”orange”或者”quick.orange.male.rabbit”,會發生什麼呢?當然,由於這些消息不匹配任何綁定會被丟失。

另一方面即便 “lazy.orange.male.rabbit”, 有4個詞,但它匹配最後一個綁定,所以它會被傳送給第二個隊列。

Topic 交易所

Topic 交易所很強大並且可以擁有其他類型交易所的的表現
當一個隊列使用了“#”綁定鍵它就會接收所有消息,不管是什麼路由鍵。此時就像是fanout類型交易所。 當在綁定中沒有使用特殊字符 “*”
(star) 和 “#” (hash) topic交易所就跟direct交易所一樣。

整合

我們將在日誌系統中使用topic交易所。假設日誌的路由鍵有兩個詞 “場所.嚴重級別”。

代碼同之前的教程中幾乎相同

emit_log_topic.py完整代碼:

#!/usr/bin/env python
import pika
import sys

connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel()

channel.exchange_declare(exchange='topic_logs',
                         type='topic')

routing_key = sys.argv[1] if len(sys.argv) > 2 else 'anonymous.info'
message = ' '.join(sys.argv[2:]) or 'Hello World!'
channel.basic_publish(exchange='topic_logs',
                      routing_key=routing_key,
                      body=message)
print(" [x] Sent %r:%r" % (routing_key, message))
connection.close()

receive_log_topic.py完整代碼:

#!/usr/bin/env python
import pika
import sys

connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost'))
channel = connection.channel()

channel.exchange_declare(exchange='topic_logs',
                         type='topic')

result = channel.queue_declare(exclusive=True)
queue_name = result.method.queue

binding_keys = sys.argv[1:]
if not binding_keys:
    sys.stderr.write("Usage: %s [binding_key]...\n" % sys.argv[0])
    sys.exit(1)

for binding_key in binding_keys:
    channel.queue_bind(exchange='topic_logs',
                       queue=queue_name,
                       routing_key=binding_key)

print(' [*] Waiting for logs. To exit press CTRL+C')

def callback(ch, method, properties, body):
    print(" [x] %r:%r" % (method.routing_key, body))

channel.basic_consume(callback,
                      queue=queue_name,
                      no_ack=True)

channel.start_consuming()

我在Ubuntu終端開啓了四個控制檯,其中三個接收日誌,日誌的接收和發送情況如圖:
接收所有日誌
這裏寫圖片描述

接收級別爲critical日誌
這裏寫圖片描述

接收產生自kernel日誌
這裏寫圖片描述

日誌的發送
這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章