RabbitMQ 入門系列:8、擴展內容:接收資訊時:可否根據RoutingKey過濾監聽資訊,答案是不能。

系列目錄

RabbitMQ 入門系列:1、MQ的應用場景的選擇與RabbitMQ安裝。

RabbitMQ 入門系列:2、基礎含義:鏈接、通道、隊列、交換機。

RabbitMQ 入門系列:3、基礎含義:持久化、排它性、自動刪除、強制性、路由鍵。

RabbitMQ 入門系列:4、基礎編碼:官方SDK使用:鏈接創建、單例改造、發送消息、接收消息。

RabbitMQ 入門系列:5、基礎編碼:交換機的進階介紹及編碼方式。

RabbitMQ 入門系列:6、保障消息:不丟失:發送方、Rabbit存儲端、接收方。

RabbitMQ 入門系列:7、保障消息:不重複消費:產生消息的唯一ID。

RabbitMQ 入門系列:8、擴展內容:接收資訊時:可否根據RoutingKey過濾監聽資訊,答案是不能。

RabbitMQ 入門系列:9、擴展內容:死信隊列:真不適合當延時隊列。

RabbitMQ 入門系列:10、擴展內容:延時隊列:延時隊列插件及其有限的適用場景。

前言:

初看交換機的概念時,以為會有根據綁定的RoutingKey進行過濾監聽的功能。

而網上,也有一些誤導人程式碼,連我也被誤了不少時間。

答案已經在標題上有了,下面看一些錯誤的演示。

1、網上離譜的標準錯誤答案:DotNet版本

1、發送方:

ConnectionFactory factory = new ConnectionFactory()
{
    HostName = "127.0.0.1",
    UserName = "guest",
    Password = "guest",
    VirtualHost = "/"
};
string myexchange = "myexchange";string myqueue = "myqueue";
using (var connection = factory.CreateConnection())
{
    var channel = connection.CreateModel();

    channel.ExchangeDeclare(myexchange, ExchangeType.Direct, true, false, null);
    channel.QueueDeclare(myqueue, true, false, false, null);
    channel.QueueBind(myqueue, myexchange, "log_info", null);
    channel.QueueBind(myqueue, myexchange, "log_error", null);
    for (int i = 0; i < 10; i++)
    {
        var msg = Encoding.UTF8.GetBytes($"{i},你好");
        var routeKey = i % 2 == 0 ? "log_info" : "log_error";
        channel.BasicPublish(myexchange, routingKey: routeKey, basicProperties: null, body: msg);
    }
}

向隊列myqueue發送了10條數據:

 

可以看到,隊列消息里是帶有Routing Key。

然後以為可以只監聽獲取對應的Routing Key的數據,這種錯誤認知就被帶出了,還演示了無效的程式碼:

2、接收方:

ConnectionFactory factory = new ConnectionFactory()
{
    HostName = "127.0.0.1",
    UserName = "guest",
    Password = "guest",
    VirtualHost = "/"
};
string myexchange = "myexchange";

string myqueue = "myqueue";
using (var connection = factory.CreateConnection())
{
    var channel = connection.CreateModel();

    channel.QueueBind(myqueue, myexchange, "log_info", null);

    EventingBasicConsumer consumer = new EventingBasicConsumer(channel);

    consumer.Received += (sender, e) =>
    {
        var msg = Encoding.UTF8.GetString(e.Body);

        Console.WriteLine(msg + " Routing Key :" + e.RoutingKey);
    };

    channel.BasicConsume(myqueue, false, consumer);

    Console.ReadKey();
}

天真以為綁定對應路由器就有效,運行結果仍是:

真實的答案就只有一個:它是隊列,不是資料庫,無法根據條件過濾過查詢。 

2、網上容易誤導的進階式標準錯誤答案2:來源參考的是Java版本

 發送方保持不變,修改接收方的程式碼:

ConnectionFactory factory = new ConnectionFactory()
{
    HostName = "127.0.0.1",
    UserName = "guest",
    Password = "guest",
    VirtualHost = "/"
};
string myexchange = "myexchange";

using (var connection = factory.CreateConnection())
{
    var channel = connection.CreateModel();

    string name = channel.QueueDeclare().QueueName;
    channel.QueueBind(name, myexchange, "log_info", null);

    EventingBasicConsumer consumer = new EventingBasicConsumer(channel);

    consumer.Received += (sender, e) =>
    {
        var msg = Encoding.UTF8.GetString(e.Body);

        Console.WriteLine(msg + " Routing Key :" + e.RoutingKey);
    };

    channel.BasicConsume(name, false, consumer);

    Console.ReadKey();
}

重新定義臨時隊列,然後綁定了臨時隊列的名稱,開啟監聽,然後讓發送方再次發送數據:

右邊發送了10條,左邊根據過濾條件,收到了5條,看起來好像達到效果了。

表現上很正確,這就很TMD的誤導人了。

下面看看這種是什麼情況:

 

實際的情況是:

1、接收方:創建了臨時隊列,同時綁定log_info,監聽的是臨時隊列。

2、發送方:即發送到myqueue,對log_info的數據,再copy一份發送到臨時隊列。

這就很離譜了,會造成以下幾個問題:

1、只能收到監聽開啟之後發送的數據,對myqueue之前的數據是獲取不到的。

也即是說,僅有雙方同時在線,發送時的數據才能收到,其餘條件的數據都會丟失。

2、myqueue還是存有一份資訊,不消費,攢著留著過年嗎?

第2個條件還能通過設置過期解決,第1個條件,都不知道什麼應用場景才適合,但肯定不屬於MQ的應用場景。

總結:

還是一句話:它是隊列,不是資料庫,無法根據條件過濾過查詢。