RabbitMQ .NET訊息佇列使用入門(四)【RabbitMQ用法大全】

風靈使發表於2018-07-21

一.Hello World

生產者

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using RabbitMQ.Client;
using RabbitMQ.Client.Framing.Impl;

namespace Hello
{
    class Program
    {
        static void Main(string[] args)
        {

            var factory = new ConnectionFactory();//建立連線
            factory.HostName = "localhost";       //設定目標,如果RabbitMQ不在本機,只需要設定目標機器的IP地址或者機器名稱即可
            factory.UserName = "guest";              //設定使用者名稱
            factory.Password = "guest";          //設定對應的密碼

            using (var connection = factory.CreateConnection())//建立連線
            {
                using (var channel = connection.CreateModel())//建立通道
                {

                    //佇列宣告 傳送端佇列宣告要與此處的一致
                    channel.QueueDeclare("hello", false, false, true, null);//要傳送的資訊 是否持久化 是否私有的 連線關閉時是否刪除佇列 引數

                    string message = "共有100個資訊";
                    var body = Encoding.UTF8.GetBytes(message);//組織 要傳送的資訊
                    channel.BasicPublish("", "hello", null, body);//把資訊傳送到佇列channel裡
                    Console.WriteLine(" 資訊 {0}", message);


                    int msgNum = 100;
                    while (true)
                    {
                        Thread.Sleep(300);

                        message = "傳送:這是第" + msgNum + "個Hello World";
                        body = Encoding.UTF8.GetBytes(message);
                        channel.BasicPublish("", "hello", null, body);
                        Console.WriteLine(" 資訊 {0}", message);

                        msgNum--;
                        if (msgNum == 0)
                        {
                            message = "資訊傳送完畢";
                            body = Encoding.UTF8.GetBytes(message);
                            channel.BasicPublish("", "hello", null, body);//第一個引數是交換區的名字 第二個是訊息佇列的名字 第三個是引數  第四個是傳送的資訊  
                            Console.WriteLine(" 資訊 {0}", message);

                            Console.ReadLine();

                            break;
                        }
                    }
                }
            }
        }
    }
}

消費者

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;

namespace HelloReceive
{
    class Program
    {
        static void Main(string[] args)
        {
            #region 宣告連結

            var factory = new ConnectionFactory(); //建立連線
            factory.HostName = "localhost"; //設定目標,如果RabbitMQ不在本機,只需要設定目標機器的IP地址或者機器名稱即可
            factory.UserName = "guest"; //設定使用者名稱
            factory.Password = "guest"; //設定對應的密碼

            #endregion

            using (var connection=factory.CreateConnection())//建立連線
            {
                using (var channel=connection.CreateModel())//建立通道
                {

                    #region 訊息佇列與客戶端繫結
                    //佇列宣告 此處的佇列宣告要與傳送端一致
                    channel.QueueDeclare("hello", false, false, true, null); //要傳送的資訊 是否持久化 是否私有的 連線關閉時是否刪除佇列 引數

                    var consumer = new QueueingBasicConsumer(channel); //在通道channel裡新增消費者
                    channel.BasicConsume("hello", true, consumer);//消費者訂閱佇列 // 訊息佇列的名字 是否關閉訊息響應 消費者的名字

                    //channel.BasicCancel(consumer.ConsumerTag);//停止訂閱

                    #endregion

                    Console.WriteLine(" waiting for message.");

                    #region 接收資訊
                    while (true)
                    {

                        var ea = (BasicDeliverEventArgs) consumer.Queue.Dequeue(); //佇列裡的資訊出列
                        var body = ea.Body;
                        var message = Encoding.UTF8.GetString(body);
                        Console.WriteLine("接收: {0}", message);

                    }
                    #endregion
                }
            }
        }
    }
}

二.工作佇列(輪詢分發)

生產者

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

namespace RMQ_WorkQueuesSend
{
    class Program
    {
        static void Main(string[] args)
        {
            var factory = new ConnectionFactory();
            factory.HostName = "localhost";
            factory.UserName = "guest";
            factory.Password = "guest";

            using (var connection=factory.CreateConnection())
            {
                using (var channel=connection.CreateModel())
                {
                    channel.QueueDeclare("WorkQueues", false, false, false, null);
                    while (true)
                    {
                        string message = GetMessage(args);
                        var body = Encoding.UTF8.GetBytes(message);
                        channel.BasicPublish("", "WorkQueues", null, body);
                        Console.WriteLine("傳送:" + message);
                    }
                }
            }
        }

        private static string GetMessage(string[] args)
        {
            return  Console.ReadLine();
        }
    }
}

消費者1

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;

namespace RMQ_WorkQueuesReceive
{
    class Program
    {
        static void Main(string[] args)
        {
            var factory = new ConnectionFactory();
            factory.HostName = "localhost";
            factory.UserName = "guest";
            factory.Password = "guest";

            using (var connection = factory.CreateConnection())
            {
                using (var channel = connection.CreateModel())
                {
                    channel.QueueDeclare("WorkQueues", false, false, false, null);

                    var consumer=new QueueingBasicConsumer(channel);
                    channel.BasicConsume("WorkQueues", true, consumer);

                    while (true)
                    {
                        var ea = (BasicDeliverEventArgs)consumer.Queue.Dequeue();

                        var body = ea.Body;
                        var message = Encoding.UTF8.GetString(body);

                        Console.WriteLine("接收端1:" + message + Environment.NewLine);
                    }
                }
            }
        }
    }
}

消費者2

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;

namespace RMQ_WorkerReceive
{
    class Program
    {
        static void Main(string[] args)
        {
            var factory = new ConnectionFactory();
            factory.HostName = "localhost";
            factory.UserName = "guest";
            factory.Password = "guest";

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

                    channel.QueueDeclare("WorkQueues", false, false, false, null);

                    var consumer = new QueueingBasicConsumer(channel);
                    channel.BasicConsume("WorkQueues", true, consumer);

                    while (true)
                    {
                        var ea = (BasicDeliverEventArgs)consumer.Queue.Dequeue();

                        var body = ea.Body;
                        var message = Encoding.UTF8.GetString(body);

                        Console.WriteLine("接收端2:" + message + Environment.NewLine);
                    }
                }
            }
        }
    }
}

二.工作佇列(公平分發)

要和訊息響應一起使用

你可能會注意到,訊息的分發可能並沒有如我們想要的那樣公平分配。比如,對於兩個工作者。
當奇數個訊息的任務比較重,但是偶數個訊息任務比較輕時,奇數個工作者始終處理忙碌狀態,
而偶數個工作者始終處理空閒狀態。但是RabbitMQ並不知道這些,他仍然會平均依次的分發訊息。

為了改變這一狀態,我們可以使用basicQos方法,設定perfetchCount=1
這樣就告訴RabbitMQ不要在同一時間給一個工作者傳送多於1個的訊息,或者換句話說。
在一個工作者還在處理訊息,並且沒有響應訊息之前,不要給他分發新的訊息。相反,將這條新的訊息傳送給下一個不那麼忙碌的工作者。

channel.BasicQos(0, 1, false); 

公平分發解決的問題就是 訊息優先傳送給空閒的worker

RMQ_BasicQos_ReceiveOneRMQ_BasicQos_ReceiveSecond
接收時 都是通過迴圈的方式來做

RMQ_BasicQos_ReceiveThree 接收的時候 不通過while迴圈的方式


生產者

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Security.Policy;
using System.Text;
using RabbitMQ;
using RabbitMQ.Client;

namespace RMQ_BasicQos_Send
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] location=new string[]
            {
                "localhost",
                "123.206.216.30"
            };
            var factory = new ConnectionFactory();
            factory.HostName = location[0];
            factory.UserName = "guest";
            factory.Password = "guest";
            using (var connetion = factory.CreateConnection())
            {
                using (var channel = connetion.CreateModel())
                {
                    //第二個引數設定佇列持久化 避免伺服器重啟 資訊丟失
                    channel.QueueDeclare("Lasting_queue", true, false, true, null);//要傳送的資訊 是否持久化 是否私有的 連線關閉時是否刪除佇列 引數
                    channel.QueueDeclare("Lasting_queue1", true, false, true, null);//要傳送的資訊 是否持久化 是否私有的 連線關閉時是否刪除佇列 引數
                    channel.QueueDeclare("Lasting_queue2", true, false, true, null);//要傳送的資訊 是否持久化 是否私有的 連線關閉時是否刪除佇列 引數
                    var properties = channel.CreateBasicProperties();
                    properties.DeliveryMode = 2;
                    properties.SetPersistent(true); //設定訊息持久化 

                    while (true)
                    {
                        string[] message = GetMessage().Split(' ');
                        byte[] body;

                        if (!connetion.IsOpen)
                        {
                            Console.WriteLine("與伺服器連線斷開");
                            continue;
                        }
                        if (message.Length<=0)
                            continue;
                        body = Encoding.UTF8.GetBytes(message[0]);
                        channel.BasicPublish("", "Lasting_queue", properties, body);
                        Console.WriteLine("傳送:" + message[0]);

                        if (message.Length <= 1)
                            continue;
                        body = Encoding.UTF8.GetBytes(message[1]);
                        channel.BasicPublish("", "Lasting_queue1", properties, body);
                        Console.WriteLine("傳送1:" + message[1]);

                        if (message.Length <= 2)
                            continue;
                        body = Encoding.UTF8.GetBytes(message[2]);
                        channel.BasicPublish("", "Lasting_queue2", properties, body);
                        Console.WriteLine("傳送2:" + message[2]);
                    }
                }
            }
        }

        private static string GetMessage()
        {
            return Console.ReadLine();
        }
    }
}

消費者1

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using RabbitMQ;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using RabbitMQ.Client.Framing;

namespace RMQ_BasicQos_ReceiveOne
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] location = new string[]
            {
                "localhost",
                "123.206.216.30"
            };
            var factory=new ConnectionFactory();
            factory.HostName = location[1];
            factory.UserName = "guest";
            factory.Password = "guest";

            using (var connection=factory.CreateConnection())
            {
                using (var channel=connection.CreateModel())
                {
                    channel.QueueDeclare("Lasting_queue", true, false, true, null);


                    channel.BasicQos(0, 1, false);//設定該客戶在同一時間 RabbitMQ只發給該客戶一個訊息,在該客戶沒有響應訊息之前不要給他分發訊息將這條新的訊息傳送給下一個不那麼忙碌的工作者。

                    var consumer = new QueueingBasicConsumer(channel);
                    channel.BasicConsume("Lasting_queue", false, consumer);


                    while (true)
                    {
                        var ea = (BasicDeliverEventArgs)consumer.Queue.Dequeue();
                        var body = ea.Body;
                        var message = Encoding.UTF8.GetString(body);
                        Console.WriteLine("接收:" + message);
                        channel.BasicAck(ea.DeliveryTag, false);//訊息訊息響應
                    }
                }
            }
        }


    }
}

消費者2

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;

namespace RMQ_BasicQos_ReceiveSecond
{
    class Program
    {
        static void Main(string[] args)
        {
            var factory = new ConnectionFactory();
            factory.HostName = "localhost";
            factory.UserName = "guest";
            factory.Password = "guest";

            using (var connection = factory.CreateConnection())
            {
                using (var channel = connection.CreateModel())
                {
                    channel.QueueDeclare("Lasting_queue1", true, false, true, null);

                    channel.BasicQos(0, 1, false);//設定該客戶在同一時間 RabbitMQ只發給該客戶一個訊息,在該客戶沒有響應訊息之前不要給他分發訊息將這條新的訊息傳送給下一個不那麼忙碌的工作者。

                    var consumer = new QueueingBasicConsumer(channel);
                    channel.BasicConsume("Lasting_queue1", false, consumer);

                    while (true)
                    {
                        var ea = (BasicDeliverEventArgs)consumer.Queue.Dequeue();

                        var body = ea.Body;
                        var message = Encoding.UTF8.GetString(body);

                        //int dots = message.Split(',').Length - 1;
                        //Thread.Sleep(1000 * dots);

                        Console.WriteLine("接收:" + message);

                        channel.BasicAck(ea.DeliveryTag, false);//訊息訊息響應
                    }
                }
            }
        }
    }
}

消費者3

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;

namespace RMQ_BasicQos_ReceiveThree
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] location = new string[]
            {
                "localhost",
                "123.206.216.30"
            };
            var factory = new ConnectionFactory();
            factory.HostName = location[0];
            factory.UserName = "guest";
            factory.Password = "guest";

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


            channel.QueueDeclare("Lasting_queue1", true, false, true, null);
            channel.QueueDeclare("Lasting_queue2", true, false, true, null);//要傳送的資訊 是否持久化 是否私有的 連線關閉時是否刪除佇列 引數

            channel.BasicQos(0, 1, false);//設定該客戶在同一時間 RabbitMQ只發給該客戶一個訊息,在該客戶沒有響應訊息之前不要給他分發訊息將這條新的訊息傳送給下一個不那麼忙碌的工作者。

            var consumer = new EventingBasicConsumer(channel);
            channel.BasicConsume("Lasting_queue1", false, consumer);
            channel.BasicConsume("Lasting_queue2", false, consumer);

            consumer.Received += consumer_Received;

        }

        static void consumer_Received(object sender, BasicDeliverEventArgs args)
        {
            IBasicConsumer basicConsumer = sender as IBasicConsumer;
            var body = args.Body;
            var message = Encoding.UTF8.GetString(body);

            Console.WriteLine("接收:" + args.DeliveryTag.ToString()+"\t" + message);
            basicConsumer.Model.BasicAck(args.DeliveryTag, false);//訊息訊息響應
        }
    }
}

三.釋出/訂閱(Exchanges)

RabbitMQ的Exchange型別有4種,direct,fanout,topic,headers

報訊息發到Exchange 上 當有一個消費者接收到了訊息,其他的消費者仍然可以去該Exchange上去接收

  1. fanout 廣播型別
  2. direct 根據routingKey 全字匹配
  3. topic 根據routingKey 模糊匹配
    這個型別的路由規則如果你掌握啦,那是相當的好用,與靈活。他是根據RoutingKey的設定,來做匹配的,其中這裡還有兩個萬用字元為:
    *,代表任意的一個詞。例如topic.zlh.*,他能夠匹配到,topic.zlh.one ,topic.zlh.two ,topic.zlh.abc, ....
    #,代表任意多個詞。例如topic.#,他能夠匹配到,topic.zlh.one ,topic.zlh.two ,topic.zlh.abc, topic.zlh.abc.edg,....

  4. headers 忽略routingKey的一種路由方式 但是是使用Headers來匹配的 Headers是一個鍵值對,可以定義成 Dictionary
    匹配有兩種方式allany。這兩種方式是在接收端必須要用鍵值”x-mactch“來定義。all代表定義的多個鍵值對都要滿足,而any則程式碼只要滿足一個就可以了。

Exchange還沒有繫結佇列的時候向Exchange裡傳送資訊Exchange會把資訊丟掉

三.Exchanges的direct型別

生產者

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

namespace Excehange_Direct_Send
{
    class Program
    {
        static void Main(string[] args)
        {

            var queueName = "ExchangDirect_queue1";//訊息佇列的名字
            var exchangeName = "ExchangeDirect"; //交換器的名字
            var exchangeType = "direct";//topic、fanout、direct  fanout為廣播型別
            var routingKey = "abc.a";        //關鍵字

            var factory = new ConnectionFactory()
            {
                HostName = "localhost",
                UserName = "guest",
                Password = "guest"
            };

            using (var connetion = factory.CreateConnection())
            {
                using (var channel = connetion.CreateModel())
                {
                    //Exchange的名字 型別 是否持久化 是否自動刪除 引數
                    channel.ExchangeDeclare(exchangeName, exchangeType, false, true, null);

                    while (true)
                    {
                        string message = GetMessage();

                        var body = Encoding.UTF8.GetBytes(message);
                        channel.BasicPublish(exchangeName, routingKey, null, body);
                        Console.WriteLine("傳送到Exchange:" + message);
                    }
                }
            }
        }

        private static string GetMessage()
        {
            return Console.ReadLine();
        }
    }
}

消費者

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;

namespace RMQ_ReceiveOne
{
    class Program
    {
        static void Main(string[] args)
        {
            var queueName = "ExchangDirect_queue1";//訊息佇列的名字
            var exchangeName = "ExchangeDirect"; //交換器的名字
            var exchangeType = "direct";//topic、fanout、direct  fanout為廣播型別
            var routingKey = "abc.a";        //關鍵字

            var factory = new ConnectionFactory()
            {
                HostName = "localhost",
                UserName = "guest",
                Password = "guest"
            };

            var connection = factory.CreateConnection();


            var channel = connection.CreateModel();
            channel.QueueDeclare(queueName, false, false, true, null);
            channel.QueueBind(queueName,exchangeName,routingKey);


            var consumer=new EventingBasicConsumer(channel);
            channel.BasicConsume(queueName, false, consumer);
            consumer.Received += consumer_Received;
        }

        private static void consumer_Received(object sender, BasicDeliverEventArgs e)
        {
            IBasicConsumer basicConsumer = sender as IBasicConsumer;
            var body = e.Body;
            var message = Encoding.UTF8.GetString(body);
            Console.WriteLine("收到ExchangeDirect:" + message);

            if (basicConsumer != null) basicConsumer.Model.BasicAck(e.DeliveryTag, false);
        }
    }
}

三.Exchanges的topic型別

生產者

using System;
using System.Runtime.InteropServices;
using System.Text;
using RabbitMQ.Client;

namespace RMQ_Send
{
    class Program
    {
        static void Main(string[] args)
        {
            var queueName = "Exchang_queue1";//訊息佇列的名字
            var exchangeName = "MyExchange"; //交換器的名字
            var exchangeType = "topic";//topic、fanout、direct  fanout為廣播型別
            var routingKey = "abc.a";        //關鍵字

            var factory = new ConnectionFactory()
            {
                HostName = "localhost",
                UserName = "guest",
                Password = "guest"
            };

            using (var connetion = factory.CreateConnection())
            {
                using (var channel = connetion.CreateModel())
                {
                    //Exchange的名字 型別 是否持久化 是否自動刪除 引數
                    channel.ExchangeDeclare(exchangeName, exchangeType, false , true, null);
                    //var properties = channel.CreateBasicProperties(); //訊息持久化
                    //properties.DeliveryMode = 2;

                    while (true)
                    {
                        string message = GetMessage();


                        var body = Encoding.UTF8.GetBytes(message);
                        channel.BasicPublish(exchangeName, routingKey, null, body);
                        Console.WriteLine("傳送到Exchange:" + message);
                    }
                }
            }
        }

        private static string GetMessage()
        {
            return Console.ReadLine();
        }
    }
}

消費者

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;

namespace RMQ_ReceiveThree
{
    class Program
    {
        static void Main(string[] args)
        {
            var queueName = "Exchang_queue1";//訊息佇列的名字
            var exchangeName = "MyExchange"; //交換器的名字
            var exchangeType = "topic";//topic、fanout
            var routingKey = "abc.*";        //關鍵字

            var factory = new ConnectionFactory()
            {
                HostName = "localhost",
                UserName = "guest",
                Password = "guest"
            };

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

            channel.ExchangeDeclare(exchangeName, exchangeType, false, true, null);
            //需要先建立訊息佇列
            channel.QueueDeclare(queueName, false, false, true, null);
            channel.QueueBind(queueName, exchangeName, routingKey);

            /*channel.BasicQos(0,1, false);//設定該客戶在同一時間 RabbitMQ只發給該客戶一個訊息,
             *在該客戶沒有響應訊息之前不要給他分發訊息將這條新的訊息傳送給下一個不那麼忙碌的工作者。  引數說明 
             *prefetchSize 預讀的個數
             *prefetchCount:會告訴RabbitMQ不要同時給一個消費者推送多於N個訊息,即一旦有N個訊息還沒有ack,則該consumer將block掉,直到有訊息ack
             *global:true\false 是否將上面設定應用於channel,簡單點說,就是上面限制是channel級別的還是consumer級別
            */
            var consumer = new EventingBasicConsumer(channel);
            channel.BasicConsume(queueName, false, consumer);//接收到資訊時不傳送資訊響應
            consumer.Received += consumer_Received;

        }

        static void consumer_Received(object sender, BasicDeliverEventArgs args)
        {
            IBasicConsumer basicConsumer = sender as IBasicConsumer;
            var body = args.Body;
            var message = Encoding.UTF8.GetString(body);
            Console.WriteLine("收到:" + message);

            //basicConsumer.Model.BasicAck(args.DeliveryTag, false);//把資訊處理完後再傳送訊息響應
        }
    }
}

三.Exchanges的headers型別

生產者

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Cache;
using System.Text;
using System.Xml.Serialization;
using RabbitMQ.Client;

namespace RMQExchange_Header_Send
{
    class Program
    {
        static void Main(string[] args)
        {
            var queueName = "Exchang_Headers_queue";//訊息佇列的名字
            var exchangeName = "Exchange_Headers"; //交換器的名字
            var exchangeType = "headers";//topic、fanout、direct、headers  fanout為廣播型別
            //var routingKey = "abc.a";        //關鍵字

            var factory = new ConnectionFactory()
            {
                HostName = "localhost",
                UserName = "guest",
                Password = "guest"
            };

            using (var connection = factory.CreateConnection())
            {
                using (var channel = connection.CreateModel())
                {
                    channel.ExchangeDeclare(exchangeName,exchangeType,false,true,null);

                    var properties = channel.CreateBasicProperties();
                    properties.Headers = new Dictionary<string, object>();


                    while (true)
                    {
                        string[] message = GetMessage().Split(' ');

                        properties.Headers.Clear();
                        properties.Headers.Add("key1", "123");

                        channel.BasicPublish(exchangeName, "", properties, Encoding.UTF8.GetBytes(message[0]));
                        Console.WriteLine("傳送到" + exchangeName + ":" + message[0]);


                        properties.Headers.Clear();
                        properties.Headers.Add("key2","123abc");

                        channel.BasicPublish(exchangeName, "", properties, Encoding.UTF8.GetBytes(message[1]));
                        Console.WriteLine("傳送到" + exchangeName + ":" + message[1]);

                    }
                }
            }

        }
        private static string GetMessage()
        {
            return Console.ReadLine();
        }
    }
}

消費者1

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;

namespace RMQExchange_Header_Receive
{
    class Program
    {
        static void Main(string[] args)
        {
            var queueName = "Exchang_Headers_queue";
            var exchangeName = "Exchange_Headers";
            var exchangeType = "headers";//topic、fanout、direct  fanout為廣播型別

            var factory = new ConnectionFactory()
            {
                HostName = "localhost",
                UserName = "guest",
                Password = "guest"
            };

            var connetion = factory.CreateConnection();
            var channel = connetion.CreateModel();

            channel.ExchangeDeclare(exchangeName, exchangeType, false, true, null);

            var htDictionary = new Dictionary<string,object>();

            htDictionary.Add("x-match", "any");
            htDictionary.Add("key1", "123");
            htDictionary.Add("key2", "123abc");

            channel.QueueDeclare(queueName, false, false, true, null);
            channel.QueueBind(queueName, exchangeName, "", htDictionary);

            var consumer =new EventingBasicConsumer(channel);
            channel.BasicConsume(queueName, false, consumer);//訂閱

            consumer.Received += consumer_Received;

        }

        static void consumer_Received(object sender, BasicDeliverEventArgs args)
        {
            IBasicConsumer basicConsumer = sender as IBasicConsumer;
            var body = args.Body;
            var message = Encoding.UTF8.GetString(body);
            Console.WriteLine("收到:"+message);

            //訊息響應
            if (basicConsumer != null) basicConsumer.Model.BasicAck(args.DeliveryTag, false);
        }
    }
}

消費者2

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;

namespace RMQExchange_Header_Receive
{
    class Program
    {
        static void Main(string[] args)
        {
            var queueName = "Exchang_Headers_queueOther";
            var exchangeName = "Exchange_Headers";
            var exchangeType = "headers";//topic、fanout、direct  fanout為廣播型別

            var factory = new ConnectionFactory()
            {
                HostName = "localhost",
                UserName = "guest",
                Password = "guest"
            };

            var connetion = factory.CreateConnection();
            var channel = connetion.CreateModel();

            channel.ExchangeDeclare(exchangeName, exchangeType, false, true, null);

            var htDictionary = new Dictionary<string, object>();
            htDictionary.Add("x-match", "any");
            htDictionary.Add("key1", "123");
            htDictionary.Add("key2", "123abc");

            channel.QueueDeclare(queueName, false, false, true, null);
            channel.QueueBind(queueName, exchangeName, "", htDictionary);

            var consumer = new EventingBasicConsumer(channel);
            channel.BasicConsume(queueName, false, consumer);//訂閱

            consumer.Received += consumer_Received;
        }

        static void consumer_Received(object sender, BasicDeliverEventArgs args)
        {
            IBasicConsumer basicConsumer = sender as IBasicConsumer;
            var body = args.Body;
            var message = Encoding.UTF8.GetString(body);
            Console.WriteLine("收到:" + message);

            //訊息響應
            if (basicConsumer != null) basicConsumer.Model.BasicAck(args.DeliveryTag, false);
        }
    }
}

四.獲取RabbitMQ日誌

消費者

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;

namespace RMQ_ReceiveLog
{
    class Program
    {
        static void Main(string[] args)
        {
            string[] location = new string[]
            {
                "localhost",
                "123.206.216.30"
            };
            var factory = new ConnectionFactory();
            factory.HostName = location[1];
            factory.UserName = "guest";
            factory.Password = "guest";

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

            channel.QueueDeclare("ReceiveLog", false, false, true, null);//預先建立好訊息佇列

            channel.QueueBind("ReceiveLog", "amq.rabbitmq.log", "error");
            channel.QueueBind("ReceiveLog", "amq.rabbitmq.log", "warning");
            channel.QueueBind("ReceiveLog", "amq.rabbitmq.log", "info");


            var consumer=new EventingBasicConsumer(channel);
            channel.BasicConsume("ReceiveLog", false, consumer);
            channel.BasicQos(0, 1, false);//設定該客戶在同一時間 RabbitMQ只發給該客戶一個訊息,在該客戶沒有響應訊息之前不要給他分發訊息將這條新的訊息傳送給下一個不那麼忙碌的工作者。

            consumer.Received += consumer_Received;
        }

        static void consumer_Received(object sender, BasicDeliverEventArgs args)
        {
            IBasicConsumer basicConsumer = sender as IBasicConsumer;
            var body = args.Body;
            string message = args.RoutingKey + " **" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + "** " + Encoding.UTF8.GetString(body);
            Console.WriteLine(message);

            if (basicConsumer != null) basicConsumer.Model.BasicAck(args.DeliveryTag, false);//訊息響應 確認消費者已收到訊息
        }
    }
}

五.訊息持久化

當RabbitMQ Server 關閉或者崩潰,那麼裡面儲存的佇列和訊息預設是不會儲存下來的。如果要讓RabbitMQ儲存住訊息,需要在兩個地方同時設定:需要保證佇列和訊息都是持久化的。

首先,要保證RabbitMQ不會丟失佇列,所以要做如下設定:

bool durable = true;
channel.QueueDeclare("hello", durable, false, false, null);

雖然在語法上是正確的,但是在目前階段是不正確的,因為我們之前已經定義了一個非持久化的hello佇列。RabbitMQ不允許我們使用不同的引數重新定義一個已經存在的同名佇列,如果這樣做就會報錯。現在,定義另外一個不同名稱的佇列:

bool durable = true;
channel.queueDeclare("task_queue", durable, false, false, null);

queueDeclare 這個改動需要在傳送端和接收端同時設定。

現在保證了task_queue這個訊息佇列即使在RabbitMQ Server重啟之後,佇列也不會丟失。 然後需要保證訊息也是持久化的, 這可以通過設定IBasicProperties.SetPersistenttrue來實現:

var properties = channel.CreateBasicProperties();
properties.SetPersistent(true);

需要注意的是,將訊息設定為持久化並不能完全保證訊息不丟失。雖然他告訴RabbitMQ將訊息儲存到磁碟上,但是在RabbitMQ接收到訊息和將其儲存到磁碟上這之間仍然有一個小的時間視窗。 RabbitMQ 可能只是將訊息儲存到了快取中,並沒有將其寫入到磁碟上。持久化是不能夠一定保證的,但是對於一個簡單任務佇列來說已經足夠。如果需要訊息佇列持久化的強保證,可以使用publisher confirms

生產者

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

namespace RMQ_Lasting_Send
{
    class Program
    {
        static void Main(string[] args)
        {
            var factory = new ConnectionFactory();
            factory.HostName = "localhost";
            factory.UserName = "guest";
            factory.Password = "guest";

            using (var connection = factory.CreateConnection())
            {
                using (var channel = connection.CreateModel())
                {
                    //不設定持久化的話在RabbitMQ Server關閉或者崩潰後重啟 原來在服務裡的訊息會全部丟失掉 設定持久化可以避免這種情況
                    //我們之前已經定義了一個非持久化的`hello`佇列。RabbitMQ不允許我們使用不同的引數重新定義一個已經存在的同名佇列,如果這樣做就會報錯。現在,定義另外一個不同名稱的佇列:
                    channel.QueueDeclare("Lasting_queue", true, false, false, null);//要傳送的資訊 是否持久化 是否私有的 連線關閉時是否刪除佇列 引數

                    var properties = channel.CreateBasicProperties();
                    properties.SetPersistent(true); //設定訊息持久化 

                    while (true)
                    {
                        string message = GetMessage(args);

                        var body = Encoding.UTF8.GetBytes(message);
                        channel.BasicPublish("", "Lasting_queue", properties, body);//第一個引數是交換區的名字 第二個是訊息佇列的名字 第三個是引數  第四個是傳送的資訊  
                        Console.WriteLine("傳送:" + message);
                    }
                }
            }
        }

        private static string GetMessage(string[] args)
        {
            return Console.ReadLine();
        }
    }
}

消費者

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;

namespace RMQ_Lasting_Receuve
{
    class Program
    {
        static void Main(string[] args)
        {
            var factory = new ConnectionFactory();
            factory.HostName = "localhost";
            factory.UserName = "guest";
            factory.Password = "guest";

            using (var connection = factory.CreateConnection())
            {
                using (var channel = connection.CreateModel())
                {
                    channel.QueueDeclare("Lasting_queue", true, false, false, null); //設定第二個引數為true 設定佇列持久化 

                    var consumer = new QueueingBasicConsumer(channel);
                    channel.BasicConsume("Lasting_queue", false, consumer);// 訊息佇列的名字 是否關閉訊息響應 消費者的名字

                    while (true)
                    {
                        var ea = (BasicDeliverEventArgs)consumer.Queue.Dequeue();

                        var body = ea.Body;
                        var message = Encoding.UTF8.GetString(body);

                        int doit = message.Split(',').Length - 1;
                        Thread.Sleep(doit * 1000);

                        Console.WriteLine("接收端1:" + message + Environment.NewLine);
                        channel.BasicAck(ea.DeliveryTag, false);
                    }
                }
            }
        }
    }
}

六.訊息響應

當處理一個比較耗時得任務的時候,也許想知道消費者(consumers)是否執行到一半就掛掉。在當前的程式碼中,當RabbitMQ將訊息傳送給消費者(consumers)之後,馬上就會將該訊息從佇列中移除。
此時,如果把處理這個訊息的工作者(worker)停掉,正在處理的這條訊息就會丟失。同時,所有傳送到這個工作者的還沒有處理的訊息都會丟失。

我們不想丟失任何任務訊息。如果一個工作者(worker)掛掉了,我們希望該訊息會重新傳送給其他的工作者(worker)。

為了防止訊息丟失,RabbitMQ提供了訊息響應(acknowledgments)機制。消費者會通過一個ack(響應),告訴RabbitMQ已經收到並處理了某條訊息,然後RabbitMQ才會釋放並刪除這條訊息。

如果消費者(consumer)掛掉了,沒有傳送響應,RabbitMQ就會認為訊息沒有被完全處理,然後重新傳送給其他消費者(consumer)。這樣,即使工作者(workers)偶爾的掛掉,也不會丟失訊息。

訊息是沒有超時這個概念的;當工作者與它斷開連的時候,RabbitMQ會重新傳送訊息。這樣在處理一個耗時非常長的訊息任務的時候就不會出問題了。

總之就是有兩個接收端receive1receive2,由於RabbitMQ是輪詢分發 receive1收到第一個資訊 receive2收到第二個資訊

下面receive1會收到第三個資訊 。。。。

但是當receive1處理第三個資訊時還沒有處理完就 死掉了 那麼 RabbitMQ就會把第三個資訊發給receive2,讓receive2去處理,這樣就會避免資訊的丟失

生產者

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

namespace RMQ_Ack_send
{
    class Program
    {
        static void Main(string[] args)
        {
            var factory = new ConnectionFactory();
            factory.HostName = "localhost";
            factory.UserName = "guest";
            factory.Password = "guest";

            using (var connection = factory.CreateConnection())
            {
                using (var channel = connection.CreateModel())
                {
                    channel.QueueDeclare("WorkQueues", false, false, false, null);

                    //var properties = channel.CreateBasicProperties();
                    //properties.DeliveryMode = 2;


                    while (true)
                    {
                        string message = GetMessage(args);

                        var body = Encoding.UTF8.GetBytes(message);
                        channel.BasicPublish("", "WorkQueues", null, body);
                        Console.WriteLine("傳送:" + message);
                    }
                }
            }
        }

        private static string GetMessage(string[] args)
        {
            return Console.ReadLine();
        }
    }
}

消費者1

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;

namespace RMQ_Ack_ReceiveOne
{
    class Program
    {
        static void Main(string[] args)
        {
            var factory = new ConnectionFactory();
            factory.HostName = "localhost";
            factory.UserName = "guest";
            factory.Password = "guest";

            using (var connection = factory.CreateConnection())
            {
                using (var channel = connection.CreateModel())
                {
                    channel.QueueDeclare("WorkQueues", false, false, false, null);

                    var consumer = new QueueingBasicConsumer(channel);
                    channel.BasicConsume("WorkQueues", false, consumer);// 訊息佇列的名字 是否關閉訊息響應 消費者的名字

                    while (true)
                    {
                        var ea = (BasicDeliverEventArgs)consumer.Queue.Dequeue();

                        var body = ea.Body;
                        var message = Encoding.UTF8.GetString(body);

                        int doit = message.Split(',').Length - 1;
                        Thread.Sleep(doit*1000);

                        Console.WriteLine("接收端1:" + message + Environment.NewLine);
                        channel.BasicAck(ea.DeliveryTag, false);
                    }
                }
            }
        }
    }
}

消費者2

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;

namespace RMQ_Ack_ReceiveSecond
{
    class Program
    {
        static void Main(string[] args)
        {
            var factory = new ConnectionFactory();
            factory.HostName = "localhost";
            factory.UserName = "guest";
            factory.Password = "guest";

            using (var connection = factory.CreateConnection())
            {
                using (var channel = connection.CreateModel())
                {
                    channel.QueueDeclare("WorkQueues", false, false, false, null);

                    var consumer = new QueueingBasicConsumer(channel);
                    channel.BasicConsume("WorkQueues", false, consumer);// 訊息佇列的名字 是否關閉訊息響應 消費者的名字

                    while (true)
                    {
                        var ea = (BasicDeliverEventArgs)consumer.Queue.Dequeue();

                        var body = ea.Body;
                        var message = Encoding.UTF8.GetString(body);

                        int doit = message.Split('.').Length - 1;
                        Thread.Sleep(doit * 1000);

                        Console.WriteLine("接收端2:" + message + Environment.NewLine);
                        channel.BasicAck(ea.DeliveryTag, false);
                    }
                }
            }
        }
    }
}

相關文章