The. NET Core uses RabbitMQ

Keywords: RabbitMQ

The. NET Core uses RabbitMQ

 

Introduction to RabbitMQ

RabbitMQ is an open source, complete and reusable enterprise message team based on AMQP(Advanced Message Queuing Protocol). RabbitMQ can realize message processing modes such as point-to-point, publish and subscribe.

RabbitMQ is an open source AMQP implementation. The server side is written in Erlang. It supports Linux,windows,macOS,FreeBSD and other operating systems. It also supports many languages, such as Python,Java,Ruby,PHP,C#,JavaScript,Go,Elixir,Objective-C,Swift and so on.

RabbitMQ installation

The environment I use is Ubuntu 18.04,

  • RabbitMq requires Erlang language support. You need to install Erlang before installing RabbitMq
    sudo apt-get install erlang-nox
  • Update source
    sudo apt-get update
  • Installing RabbitMq
    sudo apt-get install rabbitmq-server
  • Add the users user and set the password to admin
    sudo rabbitmqctl add_user  users  admin
  • Give permissions to added users
    sudo rabbitmqctl set_user_tags users administrator
  • Grant configuration, write and read permissions to all resources in the virtual host to manage the resources therein
    rabbitmqctl set_permissions -p users '.*' '.*' '.*'
  • An official web management tool (rabbitmq_management), locate the Rabbitmq installation directory and start the web console
    sudo  rabbitmq-plugins enable rabbitmq_management
  • After success, you can enter in the browser http://localhost:15672/ View RabbitMq information

RabbitMQ common commands

  • start-up
    sudo rabbitmq-server start
  • stop it
    sudo rabbitmq-server stop
  • restart
    sudo rabbitmq-server restart
  • View status
    sudo rabbitmqctl status
  • View all users
    rabbitmqctl list_users
  • view user permission
    rabbitmqctl list_user_permissions users
  • Delete user permissions
    rabbitmqctl clear_permissions [-p VHostPath] users
  • delete user
    rabbitmqctl delete_user users
  • Modify user password
    rabbitmqctl change_password users newpassword

The. NET Core uses RabbitMQ

  • Install rabbitmq.client package through the install package rabbitmq.client command or nuget

Producer implementation

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

namespace RabbitMQ
{
    class MainClass
    {
        static void Main(string[] args)
        {
            Console.WriteLine("producer");
            IConnectionFactory factory = new ConnectionFactory//Create connection factory object
            {
                HostName = "106.12.90.208",//IP address
                Port = 5672,//Port number
                UserName = "admin",//User account
                Password = "admin"//User password
            };
            IConnection con = factory.CreateConnection();//Create connection object
            IModel channel = con.CreateModel();//Create connection session object
            string name = "demo";
            //Declare a queue
            channel.QueueDeclare(
              queue: name,//Message queue name
              durable: false,//Whether to persist. If true, the queue will save the disk. When the server is restarted, it can ensure that relevant information is not lost.
              exclusive: false,//Exclusive or not, true exclusive. If a queue is declared as an exclusive queue, the queue is only visible to the connection that declares it for the first time, and will be automatically deleted when the connection is disconnected
              autoDelete: false,//Whether to delete automatically. true is auto delete. The premise of automatic deletion is that at least one consumer is connected to this queue, and then it will be deleted automatically when all consumers connected to this queue are disconnected
              arguments: null //Set some other parameters of the queue
               );
            string str = string.Empty;
            do
            {
                Console.WriteLine("send content:");
                str = Console.ReadLine();
                //Message content
                byte[] body = Encoding.UTF8.GetBytes(str);
                //send message
                channel.BasicPublish("", name, null, body);
                Console.WriteLine("Message sent successfully:" + str);
            }while(str.Trim().ToLower() != "exit");
            con.Close();
            channel.Close();
        }
    }
}

Consumer realization

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

namespace RabbitMQ
{
    class MainClass
    {

        static void Main(string[] args)
        {
            Console.WriteLine("consumer");
            IConnectionFactory factory = new ConnectionFactory//Create connection factory object
            {
                HostName = "106.12.90.208",//IP address
                Port = 5672,//Port number
                UserName = "admin",//User account
                Password = "admin"//User password
            };
            IConnection conn = factory.CreateConnection();
            IModel channel = conn.CreateModel();
            string name = "demo";
            //Declare a queue
            channel.QueueDeclare(
              queue: name,//Message queue name
              durable: false,//Whether to persist. If true, the queue will save the disk. When the server is restarted, it can ensure that relevant information is not lost.
              exclusive: false,//Exclusive or not, true exclusive. If a queue is declared as an exclusive queue, the queue is only visible to the connection that declares it for the first time, and will be automatically deleted when the connection is disconnected
              autoDelete: false,//Whether to delete automatically. true is auto delete. The premise of automatic deletion is that at least one consumer is connected to this queue, and then it will be deleted automatically when all consumers connected to this queue are disconnected
              arguments: null ////Set some other parameters of the queue
               );
            //Create consumer object
            var consumer = new EventingBasicConsumer(channel);
            consumer.Received += (model, ea) =>
            {
                byte[] message = ea.Body;//Received message
                        Console.WriteLine("Received message is:" + Encoding.UTF8.GetString(message));
            };
            //Consumer turns on listening
            channel.BasicConsume(name, true, consumer);
            Console.ReadKey();
            channel.Dispose();
            conn.Close();
        }

    }
}

Run two projects at the same time to see the effect

Worker mode of RabbitMQ

The Worker mode is actually a one to many mode. We define two consumers to see the effect

By default, RabbitMQ sends messages to the next consumer in sequence. Each consumer gets an average number of messages. This method is called round robin
However, in many cases, we do not want the message to be evenly distributed, but to consume more quickly and less. In many cases, once one of them goes down, the other receiver cannot receive the data that the receiver originally wanted to receive.
We modified one of the consumer codes to wait for 3 seconds. Stop running while waiting to see the effect

consumer.Received += (model, ea) =>
{
    Thread.Sleep(3000);
    byte[] message = ea.Body;
    Console.WriteLine("Received message is:" + Encoding.UTF8.GetString(message));
};

Consumer 1 did not accept the data after the outage, so we need message confirmation to solve this problem.

RabbitMQ message acknowledgement

There are two message confirmation modes in Rabbit

  • Automatic mode - as long as the message is obtained from the queue, whether the consumer successfully consumes the message or not is considered as successful consumption of the message
  • Manual mode - after the consumer obtains a message from the queue, the server will put the message in an unavailable state and wait for the consumer's feedback. If the consumer has an exception in the consumption process, disconnects and does not send a reply, RabbitMQ will re deliver the message.

Modify two consumer codes and delay confirmation in one.

consumer.Received += (model, ea) =>
{
    byte[] message = ea.Body;
    Console.WriteLine("Received message is:" + Encoding.UTF8.GetString(message));
    Thread.Sleep(3000); //Wait three seconds for manual confirmation
    channel.BasicAck(ea.DeliveryTag, true);//Return message confirmation
};
////Set autoAck to false to turn off automatic confirmation
channel.BasicConsume(name, false, consumer);

If the consumer disconnects during the delay, RabbitMQ will resend the unacknowledged message

"Those who can do more" model

Those who can do more work give more information to those who consume faster. Those who are less responsible consume less information. Those who can do more work are realized on the basis of manual confirmation.
Add BasicQos to deferred confirmation consumption

channel.QueueDeclare(
    queue: name,//Message queue name
    durable: false,//Whether to persist. If true, the queue will save the disk. When the server is restarted, it can ensure that relevant information is not lost.
    exclusive: false,//Exclusive or not, true exclusive. If a queue is declared as an exclusive queue, the queue is only visible to the connection that declares it for the first time, and will be automatically deleted when the connection is disconnected
    autoDelete: false,//Whether to delete automatically. true means to delete automatically. The premise of automatic deletion is that there is less than one consumer connected to this queue, and then it will be deleted automatically when all consumers connected to this queue are disconnected
    arguments: null ////Set some other parameters of the queue
    );
//Only one message can be sent to the consumer at a time. Before the consumer confirms, no message will be sent to him
channel.BasicQos(0,1,false);

It can be seen that consumers with fast consumption accept more news, which is the embodiment of the model of "those who can do more work"

Exchange mode

In the Exchange mode of RabbitMQ, instead of sending messages directly to the Queue, producers send messages to the Exchange (switch), and consumers create their own queues and bind them to the switch

Publish subscribe mode (fanout)

The producer implements to replace the queue with a switch, tell the switch name to RabbitMQ when publishing messages, and set the switch to fanout publish subscribe mode

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

namespace RabbitMQ
{
    class MainClass
    {
        static void Main(string[] args)
        {
            Console.WriteLine("producer");
            IConnectionFactory factory = new ConnectionFactory//Create connection factory object
            {
                HostName = "106.12.90.208",//IP address
                Port = 5672,//Port number
                UserName = "admin",//User account
                Password = "admin"//User password
            };
            IConnection con = factory.CreateConnection();//Create connection object
            IModel channel = con.CreateModel();//Create connection session object
            sstring exchangeName = "exchange1";//Switch name
            //Set the switch to fanout publish subscribe mode
            channel.ExchangeDeclare(name, type: "fanout");
            string str;
            do
            {
                str = Console.ReadLine();
                //Message content
                byte[] body = Encoding.UTF8.GetBytes(str);
                //Release news,
                channel.BasicPublish(exchangeName, "", null, body);
            }while(str.Trim().ToLower() != "exit");
            con.Close();
            channel.Close();
        }
    }
}

Consumer realization

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

namespace mq
{
    class MainClass
    {

        static void Main(string[] args)
        {
            IConnectionFactory factory = new ConnectionFactory//Create connection factory object
            {
                HostName = "106.12.90.208",//IP address
                Port = 5672,//Port number
                UserName = "admin",//User account
                Password = "admin"//User password
            };
            IConnection conn = factory.CreateConnection();
            IModel channel = conn.CreateModel();
            //Switch name
            string exchangeName = "exchange1";
            //Claim switch
            channel.ExchangeDeclare(exchangeName, ExchangeType.Fanout);
            //Message queue name
            string queueName = DateTime.Now.Second.ToString();
            //Declaration queue
            channel.QueueDeclare(queueName, false, false, false, null);
            //Bind queue to switch
            channel.QueueBind(queueName, exchangeName, "", null);
            //Define consumer
            var consumer = new EventingBasicConsumer(channel);
            Console.WriteLine($"Queue name:{queueName}");
            //Receive event
            consumer.Received += (model, ea) =>
            {
                byte[] message = ea.Body;//Received message
                Console.WriteLine($"Received message is:{Encoding.UTF8.GetString(message)}");
                //Return message confirmation
                channel.BasicAck(ea.DeliveryTag, true);
            };
            //Turn on listening
            channel.BasicConsume(queueName, false, consumer);
            Console.ReadKey();
        }
    }
}

When consumers bind the same switch, it can be seen that two different consumers can accept all messages sent by the producer.

Routing mode (Direct Exchange)

In routing mode, different routekeys are specified when publishing messages, and the switch will distribute messages to different queues according to different routekeys

Producer implementation

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

namespace RabbitMQ
{
    class MainClass
    {
        static void Main(string[] args)
        {
            Console.WriteLine("producer");
            IConnectionFactory factory = new ConnectionFactory//Create connection factory object
            {
                HostName = "106.12.90.208",//IP address
                Port = 5672,//Port number
                UserName = "admin",//User account
                Password = "admin"//User password
            };
            IConnection con = factory.CreateConnection();//Create connection object
            IModel channel = con.CreateModel();//Create connection session object
            string exchangeName = "exchange1"; //Switch name
            string routeKey = "key1"; //Matching key,
            //Set the switch to Direct mode
            channel.ExchangeDeclare(exchangeName, ExchangeType.Direct);
            string str;
            do
            {
                str = Console.ReadLine();
                //Message content
                byte[] body = Encoding.UTF8.GetBytes(str);
                //send message
                channel.BasicPublish(exchangeName, routeKey, null, body);
            }while(str.Trim().ToLower() != "exit");
            con.Close();
            channel.Close();
        }

    }
}

Declare a routeKey value as key1, and tell RabbitMQ when publishing the message that the routeKey must match during message delivery before it can be received by the queue, otherwise the message will be discarded.

Consumer realization

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

namespace mq
{
    class MainClass
    {

        static void Main(string[] args)
        {
            Console.WriteLine($"Input accept key name:");
            string routeKey = Console.ReadLine();
            IConnectionFactory factory = new ConnectionFactory//Create connection factory object
            {
                HostName = "106.12.90.208",//IP address
                Port = 5672,//Port number
                UserName = "admin",//User account
                Password = "admin"//User password
            };
            IConnection conn = factory.CreateConnection();
            IModel channel = conn.CreateModel();
            //Switch name
            string exchangeName = "exchange11";
            //Claim switch
            channel.ExchangeDeclare(exchangeName, ExchangeType.Direct);
            //Message queue name
            string queueName = DateTime.Now.Second.ToString();
            //Declaration queue
            channel.QueueDeclare(queueName, false, false, false, null);
            //Bind the queue and key to the switch
            channel.QueueBind(queueName, exchangeName, routeKey, null);
            //Define consumer
            var consumer = new EventingBasicConsumer(channel);
            Console.WriteLine($"Queue name:{queueName}");
            //Receive event
            consumer.Received += (model, ea) =>
            {
                byte[] message = ea.Body;//Received message
                Console.WriteLine($"Received message is:{Encoding.UTF8.GetString(message)}");
                //Return message confirmation
                channel.BasicAck(ea.DeliveryTag, true);
            };
            //Turn on listening
            channel.BasicConsume(queueName, false, consumer);
            Console.ReadKey();
        }
    }

}

Messages are received only when the routekeys match. The receiver message queue can declare multiple routekeys to bind with the switch

If the routeKey does not match, no message will be received.

Wildcard mode (Topic Exchange)

The wildcard mode is similar to the routing mode. Different from the matching mode, the route can be declared as a fuzzy query

The symbol "#" matches one or more words
The symbol "*" matches a word.

The wildcard in RabbitMQ uses "." to split strings. For example, a. * can only match A.B and A.C, while a. # can match a.a.c and a.a.b

Generator implementation

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

namespace RabbitMQ
{
    class MainClass
    {
        static void Main(string[] args)
        {
            Console.WriteLine("producer");
            IConnectionFactory factory = new ConnectionFactory//Create connection factory object
            {
                HostName = "106.12.90.208",//IP address
                Port = 5672,//Port number
                UserName = "admin",//User account
                Password = "admin"//User password
            };
            IConnection con = factory.CreateConnection();//Create connection object
            IModel channel = con.CreateModel();//Create connection session object
            string exchangeName = "exchange114"; //Switch name
            string routeKey = "key.a"; //Matching key,
            //Set the switch to Topic mode
            channel.ExchangeDeclare(exchangeName, ExchangeType.Topic);
            string str;
            do
            {
                str = Console.ReadLine();
                //Message content
                byte[] body = Encoding.UTF8.GetBytes(str);
                //send message
                channel.BasicPublish(exchangeName, routeKey, null, body);
            }while(str.Trim().ToLower() != "exit");
            con.Close();
            channel.Close();
        }

    }
}

Consumer realization

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

namespace mq
{
    class MainClass
    {

        static void Main(string[] args)
        {
            Console.WriteLine($"Input accept key name:");
            string routeKey = "key.*"; //Use wildcards to match key s
            IConnectionFactory factory = new ConnectionFactory//Create connection factory object
            {
                HostName = "106.12.90.208",//IP address
                Port = 5672,//Port number
                UserName = "admin",//User account
                Password = "admin"//User password
            };
            IConnection conn = factory.CreateConnection();
            IModel channel = conn.CreateModel();
            //Switch name
            string exchangeName = "exchange114";
            //Claim switch
            channel.ExchangeDeclare(exchangeName, ExchangeType.Topic);
            //Message queue name
            string queueName = DateTime.Now.Second.ToString();
            //Declaration queue
            channel.QueueDeclare(queueName, false, false, false, null);
            //Bind queue to switch
            channel.QueueBind(queueName, exchangeName, routeKey, null);
            //Define consumer
            var consumer = new EventingBasicConsumer(channel);
            Console.WriteLine($"Queue name:{queueName}");
            //Receive event
            consumer.Received += (model, ea) =>
            {
                byte[] message = ea.Body;//Received message
                Console.WriteLine($"Received message is:{Encoding.UTF8.GetString(message)}");
                //Return message confirmation
                channel.BasicAck(ea.DeliveryTag, true);
            };
            //Turn on listening
            channel.BasicConsume(queueName, false, consumer);
            Console.ReadKey();
        }
    }

}

The message will only be received if the wildcard match passes,

Posted by joebloggs1987 on Wed, 24 Nov 2021 04:29:43 -0800