通過 .NET 學習 RabbitMQ 建立可靠的訊息佇列系統:發布訂閱模式 Direct & Topic & Header 通過 .NET 學習 RabbitMQ 建立可靠的訊息佇列系統:發布訂閱模式 Direct & Topic & Header

Published on Thursday, June 1, 2023

RabbitMQ Publish/Subscribe Direct, Topic And Header Type

今天要來學習發布訂閱模式中的 DirectTopic 模式這兩個模式使用起來非常相似。上一篇文章中我們學習到的 Fanout 模式有一個不太方便的點,那就是 它會一次發送給有綁定到 Exchange 上的所有 Queue,如果只想要發送給某個特定族群的 Queue 就可以選擇使用 Direct, Topic 或者 Header 模式。


Direct Type

Direct 從字面上來看就是直接的意思,還記得在第一篇文章中我們不使用 Exchange 直接使用 routingKey 發送到有同樣名稱的 Queue 嗎? Direct 模式也是使用 routingKey 來決定要發送到哪些 Queue,不過不同的是點對點模式中的 routingKey 需要輸入明確的 Queue 名稱, 但是在 Direct 模式的 routingKey 比較像群組的概念,我們需要先將多個 Queue 設定同一個群組才能使用 Direct 模式。

首先我們到 Receive 專案,定義一個新的 Direct 類型的 Exchange,並且建立一個隨機名稱的 Queue 並且 routingKey 會根據我們之後傳入的參數來設定

channel.ExchangeDeclare("direct_exchange", ExchangeType.Direct);
var queueName = channel.QueueDeclare().QueueName;
channel.QueueBind(queueName, "direct_exchange", string.Join("", args));

接下來開啟兩個 shell 並運行 Receive 專案

#shell 1
dotnet run "group1"
#shell 1
dotnet run "group2"

接下來修改 Send 專案,定義一個新的 Direct 類型的 Exchange,在發送信息時選擇發送到 group1

channel.ExchangeDeclare("direct_exchange", ExchangeType.Direct);

channel.BasicPublish(exchange: "direct_exchange",
    routingKey: "group1",
    mandatory: true,
    basicProperties: null,
    body: body);

這樣當我們直接執行 Send 專案會直接發送到有相同 routingKeyQueue,擁有不同的 routingKey 則不會收到信息

#shell 1
dotnet run "group1"

[*] Waiting for messages.
Press [enter] to exit.
[x] Received Hello World!
[x] Done
#shell 2
dotnet run "group2"

[*] Waiting for messages.
Press [enter] to exit.

你也可以在開啟一個 shell 3 並設定為 group1,可以看到 shell 1shell 3 能夠順利收到 Hello World 信息。


Topic Type

Topic 類型與 Direct 相比之下擁有更高的彈性,在 Topic 類型中我們可以使用兩個符號即可做到 wildcard 的功能,就可以同時匹配多個 routingKey

    • (star) 可以匹配一個單詞
  • (hash) 可以匹配一個單詞或多個單詞

這邊每個單詞需要用 . 來分割例如我們在定義 routingKey 時可以輸入以下格式 group.*,假如我們想要發送給 group.1group.2 因為都符合 group.* 格式因此這兩個群組的信息都可以收到

我們先到 Receive 專案,定義一個新的 Topic 類型的 Exchange

channel.ExchangeDeclare("topic_exchange", ExchangeType.Topic);
var queueName = channel.QueueDeclare().QueueName;
channel.QueueBind(queueName, "topic_exchange", string.Join("", args));

接下來開啟兩個 shell 並運行 Receive 專案

#shell 1
dotnet run "*.group.*"
#shell 2
dotnet run "vip.group.1"

接下來修改 Send 專案,定義一個新的 Direct 類型的 Exchange,在發送信息時選擇發送到 vip.group.1

channel.ExchangeDeclare("topic_exchange", ExchangeType.Topic);

channel.BasicPublish(exchange: "topic_exchange",
    routingKey: "vip.group.1",
    mandatory: true,
    basicProperties: null,
    body: body);

可以看到 shell1 使用了星字號代表可以替代整個單詞,因此可以接收到 vip.group.1 的信息

#shell 1
dotnet run "*.group.*"

[*] Waiting for messages.
Press [enter] to exit.
[x] Received Hello World!
[x] Done
#shell 2
dotnet run "vip.group.1"

[*] Waiting for messages.
Press [enter] to exit.
[x] Received Hello World!
[x] Done

並且我們知道井字號代表可以替換一個或多個單詞,因此只要 routingKey 直接輸入井字號就代表可以接收所有信息

#shell 1
dotnet run "#"

Header Type

最後一個 Exchange 類型是 Header 這個類型概念也相同不過 Header 類型會忽略 routingKey 的值,因此我們需要額外定義一組 Header 字典 來提供之後進行匹配。

先到 Receive 專案,定義一個新的 Header 類型的 Exchange,並且定義一個字典我們可以設定多個 key,value 值,這邊需要注意 x-match 有兩個設定值可以使用分別是 allanyall 代表需要匹配所有的 key,value 值,any 代表只需要匹配其中一個的 key,value

channel.ExchangeDeclare("header_exchange", ExchangeType.Headers);
var queueName = channel.QueueDeclare().QueueName;
var header = new Dictionary<string, object>()
{
    { "group", "vip" },
    { "level", "1" },
    { "x-match", "any" }
};
channel.QueueBind(queueName, "header_exchange", string.Empty, header);

接下來修改 Send 專案,定義一個新的 Direct 類型的 Exchange

channel.ExchangeDeclare("header_exchange", ExchangeType.Headers);

var properties = channel.CreateBasicProperties();
properties.Headers = new Dictionary<string, object>()
{
    {"group", "vip"},
    {"level", "2"},
};

channel.BasicPublish(exchange: "header_exchange",
    routingKey: string.Empty,
    mandatory: true,
    basicProperties: properties,
    body: body);

接下來運行 ReceiveSend 專案,這邊因為 x-match 設定為 any 因此雖然只有 {"group", "vip"} 這組符合但是還是能階收到信息

dotnet run

[*] Waiting for messages.
Press [enter] to exit.
[x] Received Hello World!
[x] Done

Summary

今天學習剩下的三種發布訂閱模式 Direct, TopicHeader 模式,可以看得出來 DirectTopic 本質上幾乎是一樣的因此直接使用 Topic 模式是比較好的選擇,另外 Header 模式因為可以同時設定多個 key, value 值擁有更高的彈性,之後可以按照自己的需求來選擇。

今天的進度 Github