《RabbitMQ Tutorial》譯文 第 5 章 主題

原文來自 RabbitMQ 英文官網教程(5.Topics),其示例代碼採用了 .NET C# 語言。html

Markdown

In the previous tutorial we improved our logging system. Instead of using a fanout exchange only capable of dummy broadcasting, we used a direct one, and gained a possibility of selectively receiving the logs.git

在以前的教程中,咱們改進了日誌系統。比起只可以單一廣播的 fanout 型交換機,咱們如今採用了 direct 型,從而得到了選擇性接收日誌的可能性。github

Although using the direct exchange improved our system, it still has limitations - it can't do routing based on multiple criteria.app

儘管使用 direct 型交換機對系統有所改進,但仍然有其侷限性 - 即在多重條件下沒辦法進行路由。less

In our logging system we might want to subscribe to not only logs based on severity, but also based on the source which emitted the log. You might know this concept from the syslog unix tool, which routes logs based on both severity (info/warn/crit...) and facility (auth/cron/kern...).工具

在該日誌系統中,咱們可能但願不單單訂閱基於嚴重性(這一項指標)的日誌,也但願能基於產生日誌的源頭。你大概會知道這個概念來自於 syslog unix 工具,它能夠針對嚴重性(info/warn/crit...)和設備(auth/cron/kern...)進行路由。學習

That would give us a lot of flexibility - we may want to listen to just critical errors coming from 'cron' but also all logs from 'kern'.flex

這便給了咱們許多靈活性 - 咱們能夠作到監聽「cron」類型的嚴重錯誤,以及全部來自於「kern」設備的日誌。ui

To implement that in our logging system we need to learn about a more complex topic exchange.this

爲了在咱們的日誌系統中實現上述功能,咱們須要學習更爲複雜的話題(Topic)交換機。

Topic exchange

話題交換機

Messages sent to a topic exchange can't have an arbitrary routing_key - it must be a list of words, delimited by dots. The words can be anything, but usually they specify some features connected to the message. A few valid routing key examples: "stock.usd.nyse", "nyse.vmw", "quick.orange.rabbit". There can be as many words in the routing key as you like, up to the limit of 255 bytes.

發送至 topic 型交換機的消息已不能攜帶任意的 routing_key - 而必須是一個字符串列表,並基於小數點來分隔。

The binding key must also be in the same form. The logic behind the topic exchange is similar to a direct one - a message sent with a particular routing key will be delivered to all the queues that are bound with a matching binding key. However there are two important special cases for binding keys:

  • * (star) can substitute for exactly one word.
  • # (hash) can substitute for zero or more words.

「綁定鍵」也必須是相同的格式。topic 型交換機背後的邏輯與 direct 型交換機類似 - 採用指定 routing key 所發送的消息將會被遞送到與 binding key 相匹配的隊列中,然而針對 binding key 有兩個很是重要且特殊的情形:

  • * (星號)能夠替代一個明確的字符串。
  • # (哈希,井號)能夠替代 0 至多個字符串。

It's easiest to explain this in an example:

在示例中能夠很容易的對其解釋:

Markdown

In this example, we're going to send messages which all describe animals. The messages will be sent with a routing key that consists of three words (two dots). The first word in the routing key will describe speed, second a colour and third a species: " . . ".

在這個示例中,咱們打算髮送用來描述動物的消息,它是基於三個字符串(包含兩個小數點)組成的 routing key,其第一個字符串將用來描述速度,第二個是顏色,第三個是物種,以下:「 <速度> . <顏色> . <物種> 」。

We created three bindings: Q1 is bound with binding key ".orange." and Q2 with "..rabbit" and "lazy.#".

咱們建立了三個綁定,隊列 Q1 將關聯到綁定鍵「*.orange.*」,隊列 Q2 則關聯到「*.*.rabbit」和「lazy.#」。

These bindings can be summarised as:

  • Q1 is interested in all the orange animals.
  • Q2 wants to hear everything about rabbits, and everything about lazy animals.

這些綁定能夠歸納爲:

  • 隊列 Q1 對全部桔黃色(orange)的動物感興趣。
  • 隊列 Q2 指望監聽到全部 rabbit 後綴,以及全部 lazy 前綴的動物。

A message with a routing key set to "quick.orange.rabbit" will be delivered to both queues. Message "lazy.orange.elephant" also will go to both of them. On the other hand "quick.orange.fox" will only go to the first queue, and "lazy.brown.fox" only to the second. "lazy.pink.rabbit" will be delivered to the second queue only once, even though it matches two bindings. "quick.brown.fox" doesn't match any binding so it will be discarded.

routing key 設置爲"quick.orange.rabbit"的消息將會被遞送到兩個隊列。消息"lazy.orange.elephant"一樣也會去往兩個隊列。另外一方面,"quick.orange.fox"將只會去往第一個隊列,"lazy.brown.fox"則只會去第二個隊列。"lazy.pink.rabbit",儘管它匹配了兩個綁定,但只會被遞送到第二個隊列一次(而不會是兩次)。"quick.brown.fox"則不匹配任何綁定,因此將會被丟棄。

What happens if we break our contract and send a message with one or four words, like "orange" or "quick.orange.male.rabbit"? Well, these messages won't match any bindings and will be lost.

若是咱們打破約定將會發生什麼呢?好比發送一個字符串或者四個字符串,相似於「orange」或者「quick.orange.male.rabbit」。

On the other hand "lazy.orange.male.rabbit", even though it has four words, will match the last binding and will be delivered to the second queue.

另外一方面像「lazy.orange.male.rabbit」,即便它有四個字符串,但仍然會匹配最後一個綁定(lazy.#)並被遞送到第二個隊列。

Topic exchange
話題交換機

Topic exchange is powerful and can behave like other exchanges.

話題(Topic)交換機很強大,它能夠表現得相似其餘交換機。

When a queue is bound with "#" (hash) binding key - it will receive all the messages, regardless of the routing key - like in fanout exchange.

當一個隊列採用"#"做爲綁定鍵時,它將接收到全部的消息而忽略掉路由鍵,這就像是 fanout 型交換機同樣。

When special characters "*" (star) and "#" (hash) aren't used in bindings, the topic exchange will behave just like a direct one.

當特殊字符"*" (星號) and "#" (哈希,井號)都沒有應用到綁定中時,topic 型交換機將表現得與 direct 型同樣。

Putting it all together

融合一塊兒

We're going to use a topic exchange in our logging system. We'll start off with a working assumption that the routing keys of logs will have two words: " . ".

咱們即將在日誌系統中使用 topic 型交換機,首先從一個假定的工做前替起步,即擬定日誌的路由鍵爲兩個字符串:" . "

The code is almost the same as in the previous tutorial.

其代碼與以前教程幾乎相同。

The code for EmitLogTopic.cs:

EmitLogTopic.cs 類文件代碼:

using System;
using System.Linq;
using RabbitMQ.Client;
using System.Text;

class EmitLogTopic
{
    public static void Main(string[] args)
    {
        var factory = new ConnectionFactory() { HostName = "localhost" };
        using(var connection = factory.CreateConnection())
        using(var channel = connection.CreateModel())
        {
            channel.ExchangeDeclare(exchange: "topic_logs",
                                    type: "topic");

            var routingKey = (args.Length > 0) ? args[0] : "anonymous.info";
            var message = (args.Length > 1)
                          ? string.Join(" ", args.Skip( 1 ).ToArray())
                          : "Hello World!";
            var body = Encoding.UTF8.GetBytes(message);
            channel.BasicPublish(exchange: "topic_logs",
                                 routingKey: routingKey,
                                 basicProperties: null,
                                 body: body);
            Console.WriteLine(" [x] Sent '{0}':'{1}'", routingKey, message);
        }
    }
}

The code for ReceiveLogsTopic.cs:

ReceiveLogsTopic.cs 類文件代碼:

using System;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System.Text;

class ReceiveLogsTopic
{
    public static void Main(string[] args)
    {
        var factory = new ConnectionFactory() { HostName = "localhost" };
        using(var connection = factory.CreateConnection())
        using(var channel = connection.CreateModel())
        {
            channel.ExchangeDeclare(exchange: "topic_logs", type: "topic");
            var queueName = channel.QueueDeclare().QueueName;

            if(args.Length < 1)
            {
                Console.Error.WriteLine("Usage: {0} [binding_key...]",
                                        Environment.GetCommandLineArgs()[0]);
                Console.WriteLine(" Press [enter] to exit.");
                Console.ReadLine();
                Environment.ExitCode = 1;
                return;
            }

            foreach(var bindingKey in args)
            {
                channel.QueueBind(queue: queueName,
                                  exchange: "topic_logs",
                                  routingKey: bindingKey);
            }

            Console.WriteLine(" [*] Waiting for messages. To exit press CTRL+C");

            var consumer = new EventingBasicConsumer(channel);
            consumer.Received += (model, ea) =>
            {
                var body = ea.Body;
                var message = Encoding.UTF8.GetString(body);
                var routingKey = ea.RoutingKey;
                Console.WriteLine(" [x] Received '{0}':'{1}'",
                                  routingKey,
                                  message);
            };
            channel.BasicConsume(queue: queueName,
                                 autoAck: true,
                                 consumer: consumer);

            Console.WriteLine(" Press [enter] to exit.");
            Console.ReadLine();
        }
    }
}

Run the following examples:

運行示例:

To receive all the logs:

接收全部的日誌:

cd ReceiveLogsTopic
dotnet run "#"

To receive all logs from the facility "kern":

只接收來自「kern」設備的日誌:

cd ReceiveLogsTopic
dotnet run "kern.*"

Or if you want to hear only about "critical" logs:

或者你只想關心「critical」級別的日誌:

ReceiveLogsTopic.exe "*.critical"

You can create multiple bindings:

你能夠建立多重綁定:

cd ReceiveLogsTopic
dotnet run "kern.*" "*.critical"

And to emit a log with a routing key "kern.critical" type:

基於路由鍵「kern.critical」來產生日誌,能夠輸入:

cd EmitLogTopic
dotnet run "kern.critical" "A critical kernel error"

Have fun playing with these programs. Note that the code doesn't make any assumption about the routing or binding keys, you may want to play with more than two routing key parameters.

程序執行起來仍是很使人愉快的,要注意的是,這些代碼並無針對路由鍵或者綁定鍵作任何預設 ,你可能會須要用到超出兩個以上的路由鍵參數。

(Full source code for EmitLogTopic.cs and ReceiveLogsTopic.cs)

( EmitLogTopic.csReceiveLogsTopic.cs 完整代碼)

相關文章
相關標籤/搜索