5.【RabbitMQ實戰】- 交換機(Exchange)

RabbitMQ 消息傳遞模型的核心思想是: 生產者生產的消息從不會直接發送到隊列。實際上,通常生產者甚至都不知道這些消息傳遞傳遞到了哪些隊列中。相反,生產者只能將消息發送到交換機(exchange),交換機工作的內容非常簡單,一方面它接收來自生產者的消息,另一方面將它們推入隊列。交換機必須確切知道如何處理收到的消息。是應該把這些消 息放到特定隊列還是說把他們到許多隊列中還是說應該丟棄它們。這就的由交換機的類型來決定

交換機類型

image.png

常用的四大類型

  • direct 直連交換機,根據Routing Key(路由鍵)進行投遞到不同隊列。
  • topic 主題交換機,對路由鍵進行模式匹配後進行投遞,符號#表示一個或多個詞,*表示一個詞。
  • header 頭交換機,不處理路由鍵。而是根據發送的消息內容中的headers屬性進行匹配。
  • fanout 扇形交換機,採用廣播模式,根據綁定的交換機,路由到與之對應的所有隊列。

默認交換機(AMQP default)

下面示例爲默認交換機
image.png

臨時隊列和綁定(binding)

臨時隊列

image.png
image.png

綁定(binding)

binding 其實是 exchange 和 queue 之間的橋樑,它告訴我們 exchange 和那個隊
列進行了綁定關係。比如說下面這張圖告訴我們的就是 X 與 Q1 和 Q2 進行了綁定

image.png

扇形交換機(fanout)發佈訂閱模式

兩個routingkey一樣

image.png

生產者代碼

using rabbitmq.common;

using System.Text;

namespace Exchange.Producer
{
    public class Program
    {
        static void Main(string[] args)
        {
            using var channel = RabbitmqUntils.GetChannel();
            channel.ExchangeDeclare(exchange: RabbitmqUntils.FanoutExchangeName, type: "fanout", durable: false, autoDelete: false, null);// 創建交換機
            Console.WriteLine("請輸入要發送的消息:");
            string message = Console.ReadLine();
            if (string.IsNullOrEmpty(message))
            {
                while (true)
                {
                    Console.WriteLine("請輸入要發送的消息: {0}", message);
                }
            }
            else
            {

                while (true)
                {
                    var body = Encoding.UTF8.GetBytes(message);
                    /*
                    * 發送一個消息
                    * 1.發送到那個交換機
                    * 2.路由的 key 是哪個
                    * 3.其他的參數信息
                    * 4.發送消息的消息體
                    */
                    channel.BasicPublish(exchange: RabbitmqUntils.FanoutExchangeName, "",false, null, body); //開始傳遞
                    Console.WriteLine("已發送: {0}", message);
                    Console.WriteLine("請輸入要發送的消息:");
                    message = Console.ReadLine();
                }
            }

            Console.ReadKey();
        }
    }
}

消費者代碼

using rabbitmq.common;

using RabbitMQ.Client;
using RabbitMQ.Client.Events;

using System.Text;

namespace Exchange.Consumer
{
    public class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine($"{args[0]}接收消息");
            using var channel = RabbitmqUntils.GetChannel();
            channel.ExchangeDeclare(exchange: RabbitmqUntils.FanoutExchangeName, type: "fanout", durable: false, autoDelete: false, null); // 創建交換機
            string queueName = channel.QueueDeclare(queue: "", durable: false, exclusive: false, autoDelete: true, null).QueueName; // 創建臨時隊列
            channel.QueueBind(queue: queueName, exchange: RabbitmqUntils.FanoutExchangeName, routingKey: "", null);

            //時間對象
            var consumer = new EventingBasicConsumer(channel);
            channel.BasicConsume(queue:queueName, false, consumer);

            consumer.Received += (sender, e) =>
            {
                var body = e.Body.ToArray();
                var message = Encoding.UTF8.GetString(body);
                Console.WriteLine("接收消息: {0}", message);
            };

            Console.ReadKey();
        }
    }
}

測試效果

image.png

直接交換機(direct)路由模式

兩個routingkey不一樣
這種類型的工作方式是,消息只去到它綁定的routingKey 隊列中去
image.png
在上面這張圖中,我們可以看到 X 綁定了兩個隊列,綁定類型是 direct。隊列Q1 綁定鍵爲 orange,隊列 Q2 綁定鍵有兩個:一個綁定鍵爲 black,另一個綁定鍵爲 green.在這種綁定情況下,生產者發佈消息到 exchange 上,綁定鍵爲 orange 的消息會被髮布到隊列Q1。綁定鍵爲 black,green 和的消息會被髮布到隊列 Q2,其他消息類型的消息將被丟棄

生產者代碼

image.png

消費者代碼

image.png
image.png

測試效果

image.png
image.png

主題交換機(topic)

儘管使用direct 交換機改進了我們的系統,但是它仍然存在侷限性-比方說我們想接收的日誌類型有
info.base 和 info.advantage,某個隊列只想 info.base 的消息,那這個時候direct 就辦不到了。這個時候
就只能使用 topic 類型

Topic 的要求

發送到類型是 topic 交換機的消息的 routing_key 不能隨意寫,必須滿足一定的要求,它必須是一個單詞列表,以點號分隔開。這些單詞可以是任意單詞,比如說:"stock.usd.nyse", "nyse.vmw",
"quick.orange.rabbit".這種類型的。當然這個單詞列表最多不能超過 255 個字節。
在這個規則列表中,其中有兩個替換符是大家需要注意的
***(星號)可以代替一個單詞 **
#(井號)可以替代零個或多個單詞

Topic 匹配案例

下圖綁定關係如下
Q1-->綁定的是
中間帶 orange 帶 3 個單詞的字符串(.orange.)
Q2-->綁定的是
最後一個單詞是 rabbit 的 3 個單詞(..rabbit)
第一個單詞是 lazy 的多個單詞(lazy.#)
image.png
上圖是一個隊列綁定關係圖,我們來看看他們之間數據接收情況是怎麼樣的

quick.orange.rabbit 被隊列 Q1Q2 接收到
lazy.orange.elephant 被隊列 Q1Q2 接收到
quick.orange.fox 被隊列 Q1 接收到
lazy.brown.fox 被隊列 Q2 接收到
lazy.pink.rabbit 雖然滿足兩個綁定但只被隊列 Q2 接收一次
quick.brown.fox 不匹配任何綁定不會被任何隊列接收到會被丟棄
quick.orange.male.rabbit 是四個單詞不匹配任何綁定會被丟棄
lazy.orange.male.rabbit 是四個單詞但匹配 Q2

生產者代碼

image.png

    	public static void TopicExchange()
        {
            using var channel = RabbitmqUntils.GetChannel();
            channel.ExchangeDeclare(exchange: RabbitmqUntils.TopicExchangeName, type: "topic", durable: false, autoDelete: false, null);// 創建交換機
   
            // 多個bindingkey
            Dictionary<string, string> dic = new Dictionary<string, string>();
            dic.Add("quick.orange.rabbit", "被隊列 Q1Q2 接收到");
            dic.Add("lazy.orange.elephant", "被隊列 Q1Q2 接收到");
            dic.Add("quick.orange.fox", "被隊列 Q1 接收到");
            dic.Add("lazy.brown.fox", "被隊列 Q2 接收到");
            dic.Add("lazy.pink.rabbit", "雖然滿足兩個綁定但只被隊列 Q2 接收一次");
            dic.Add("quick.brown.fox", "不匹配任何綁定不會被任何隊列接收到會被丟棄");
            dic.Add("quick.orange.male.rabbit", "是四個單詞不匹配任何綁定會被丟棄");
            dic.Add("lazy.orange.male.rabbit", "是四個單詞但匹配 Q2");

            foreach (var item in dic)
            {
                string message = item.Value;
                var body = Encoding.UTF8.GetBytes(message);
                /*
                * 發送一個消息
                * 1.發送到那個交換機
                * 2.路由的 key 是哪個
                * 3.其他的參數信息
                * 4.發送消息的消息體
                */
                channel.BasicPublish(exchange: RabbitmqUntils.TopicExchangeName, item.Key, false, null, body); //開始傳遞
                Console.WriteLine("已發送: {0}", message);
            }
        }

消費者代碼

image.png


        public static void TopicExchangeClient1()
        {
            Console.WriteLine("TopicExchangeClient1 開始接受消息:");
            using var channel = RabbitmqUntils.GetChannel();
            channel.ExchangeDeclare(exchange: RabbitmqUntils.TopicExchangeName, type: "topic", durable: false, autoDelete: false, null); // 創建交換機
            channel.QueueDeclare(queue: "Q1", durable: false, exclusive: false, autoDelete: false, null);//申明Q1隊列
            //routingkey orange綁定Q1隊列
            channel.QueueBind(queue: "Q1", exchange: RabbitmqUntils.TopicExchangeName, routingKey: "*.orange.*", null);

            //事件對象
            var consumer = new EventingBasicConsumer(channel);
            channel.BasicConsume(queue: "Q1", false, consumer);

            consumer.Received += (sender, e) =>
            {
                var body = e.Body.ToArray();
                var message = Encoding.UTF8.GetString(body);
                Console.WriteLine("TopicExchangeClient1 接收消息: {0}", message);
            };
        }

        public static void TopicExchangeClient2()
        {
            Console.WriteLine("TopicExchangeClient2 開始接受消息:");
            using var channel = RabbitmqUntils.GetChannel();
            channel.ExchangeDeclare(exchange: RabbitmqUntils.TopicExchangeName, type: "topic", durable: false, autoDelete: false, null); // 創建交換機
            channel.QueueDeclare(queue: "Q2", durable: false, exclusive: false, autoDelete: false, null);//申明Q1隊列
            //routingkey orange綁定Q1隊列
            channel.QueueBind(queue: "Q2", exchange: RabbitmqUntils.TopicExchangeName, routingKey: "*.*.rabbit", null);
            channel.QueueBind(queue: "Q2", exchange: RabbitmqUntils.TopicExchangeName, routingKey: "lazy.#", null);

            //事件對象
            var consumer = new EventingBasicConsumer(channel);
            channel.BasicConsume(queue: "Q2", false, consumer);

            consumer.Received += (sender, e) =>
            {
                var body = e.Body.ToArray();
                var message = Encoding.UTF8.GetString(body);
                Console.WriteLine("TopicExchangeClient2 接收消息: {0}", message);
            };
        }

測試效果

image.png
image.png
image.png

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