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,