原文出處: Listen
在上一小節中我們改進了log系統,由於使用fanout類型的exchange只能進行全域的廣播,因此我們使用direct類型的exchange做了代替, 使得我們可以選擇性的接收訊息。儘管使用fanout exchange改進了log系統,但它仍然有限制——不能基於多個條件做路由。 Topics
在log系統中可能不只是基於不同的記錄層級作訂閱,也可能會基於日誌的來源。你也許聽過Unix下名為syslog的工具, 它把日誌按照嚴重層級(info/warn/crit…)和裝置(auth/cron/ker…)進行路由。
這會給我們許多的靈活性,也許我們只想監聽’cron’中的’critical’層級的錯誤記錄檔,以及所有’kern’中的日誌。 為了實現這種日誌系統,我們需要學習一個更複雜的topic類型的exchange。 Topic exchange
發送到topic exchange中的訊息不能有一個任意的routing_key——它必須是一個使用點分隔的單字清單。單詞可以是任意的, 但是通常會指定訊息的一些特定。一些有效routing key例子:”stock.usd.nyse”,”nyse.vmw”,”quick.orange.rabbit”。 routing key的長度限制為255個位元組數。
binding key也必須是相同的形式。topic exchange背後的邏輯類似於direct——一條使用特定的routing key發送的訊息將會被傳遞至所有使用與該routing key相同的binding key進行綁定的隊列中。 然而,對binding key來說有兩種特殊的情況: *(star)可以代替任意一個單詞 #(hash)可以代替0個或多個單詞
使用一張圖可以很簡單地來說明:
在圖中,我們將要發送被描述的動物的訊息。訊息的routing key將由三個單片語成(通過兩個點分隔)。routing key中的第一個單詞將描述速度, 第二個是顏色,第三個是物種:"<speed>.<colour>.<species>"。
我們建立三個綁定:Q1使用binding key"*.orange.*"來綁定,Q2使用"*.*.rabbit"以及lazy.#綁定。
這些綁定可以被總結為: Q1對所有橘色的的動物感興趣 Q2想要接收所有關於兔子的訊息以及所有關於lazy的動物的訊息
一條使用routing key"quick.orange.rabbit"發送的訊息將被同時傳遞到兩個隊列中。訊息"lazy.orange.elephant"同樣如此。 另一方面,"quick.orange.fox"只會被第一個queue接收,"lazy.brown.fox"只會被第二個queue接收。 "lazy.pink.rabbit"只會被傳遞到Q2一次,即使它對兩個binding key都匹配。"quick.brown.fox"與兩個queue的binding key都不匹配, 因此將被丟棄。
如果打破我們的約定,使用一個單詞或者四個單詞的routing key例如"orange","quick.orange.male.rabbit"發送訊息將會發生什麼。 這些訊息不會匹配任何綁定,因此會丟失。
但是對於"lazy.orange.male.rabbit",即使它有四個單詞,但是它與第二個queue的binding key匹配,因此將會被發送到第二個queue中。
當一個queue使用"#"(hash)作為binding key,那麼它將會接收所有的訊息,忽略routing key,就好像使用了fanout exchange。 當特殊字元”*“(star)和”#“(hash)在綁定中沒有用到,topic exchange將會與direct exchange的行為相同。
瞭解了topic exchange之後,我們將它用在我們的log系統中,我們定義的routing key將會有兩個單片語成:"<facility>.<severity>"。
完成的EmitLogTopic.java: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class EmitLogTopic { private static final String EXCHANGE_NAME = "topic_logs" ; public static void main(String[] argv) throws Exception { ConnectionFactory factory = new ConnectionFactory(); factory.setHost( "localhost" ); Connection connection = factory.newConnection(); Channel channel = connection.createChannel();