In the previous article, we demonstrated a sender and a consumer. This article describes how multiple consumers obtain messages in the same message queue.
In some applications, it takes a long time for the consumer to process the received message task. If the next data is obtained after the previous message is processed, there will inevitably be some latency. Data in the message queue will also increase, and the latency will increase. Of course, for a consumption process, in some cases, multiple threads can be processed, but here we will introduce another processing method, the situation of multiple consumption processes. Rabbitmq is well processed and encapsulated in this respect, so that the customer program can be conveniently used.
In fact, the code implementation is no different from the previous example. We only need to run two servers. We can see that the sent messages are processed by one of the two servers on average. There will be no duplicates. If a server program is closed, all subsequent messages will be processed on the server program that is still running. This can solve some of our scenarios that require Server Load balancer, And the expansion is very convenient, as long as a server is running, that is, worker, and the State synchronization between worker is free.
When we process a long task, if an exception occurs in the processing process or the message fails to be processed successfully due to a program failure, we usually do not want to lose the message task, other workers are expected to handle the problem or wait for the worker to restart. Similarly, rabbitmq provides a simple API for us to handle this scenario. In rabbitmq, it provides the concept of message response to prevent message loss. After receiving a message, the consumer needs to send a response message to the rabbitmq service to inform the service that I have received or correctly processed the message. So rabbitmq can safely delete the message in the queue. In the previous server code
channel.BasicConsume("TaskQueue", true, consumer);
The second parameter is true. Once the rabbitmq service delivers the message to the target queue and considers it as a response. To demonstrate that no response message is sent, we need to install a plug-in: Management UI of rabbitmq. Installing this plug-in is relatively simple.
You can go to http://www.rabbitmq.com/management.html to learn more.
Open the browser, http ://Localhost: 55672. Enter the default username and password guest/guest to go to the main interface. There is a lot of information on the interface, we only look at the top one: queued messages.
Or start a sender and an receiver. Modify the Consumer Code of the receiver definition
channel.BasicConsume("TaskQueue", false, consumer);
Then run two programs to send a message. Because no response packet is sent in the message receiving code, the following result is displayed on the monitoring webpage:
One message is not responded. Next we close the receiving program, do not modify any code, and then run the receiving program again. We found that the receiver received the original message again. The status displayed on the browser remains unchanged, indicating that there is still a message that does not respond, because the receiving program we run for the second time still does not send a response packet. Turn off the receiving program, modify the code, and add the following code when processing the message:
channel.BasicAck(ea.DeliveryTag, false);
At this time, I found that the program will still receive the message package as we expected, but the page for monitoring the web page has changed:
Then, no matter how many receiving programs are running, the message package is no longer received. This shows that rabbitmq truly considers the message to be correctly processed.
It should be noted that rabbitmq has no time limit for messages without a response packet, that is, there is no timeout. Rabbitmq will only re-allocate the message after the receiving program that processes the message is disconnected from the rabbitmq server. If the connection is not disconnected, but the handler does not send a response packet for a few days, it will not resend it. Therefore, if an exception occurs in the processing program, we can write code to disconnect the connection with rabbitmq to re-send the message (which may be sent to another Server Load balancer instance for processing ).
The modified acceptor code is as follows:
public class Worker
{
public void Listen()
{
ConnectionFactory factory = new ConnectionFactory();
factory.HostName = "localhost";
using (IConnection connection = factory.CreateConnection())
{
using (IModel channel = connection.CreateModel())
{
channel.QueueDeclare("TaskQueue", true, false, false, null);
//channel.BasicQos(500, 1, false);
QueueingBasicConsumer consumer = new QueueingBasicConsumer(channel);
channel.BasicConsume("TaskQueue", false, consumer);
while (true)
{
BasicDeliverEventArgs ea =
(BasicDeliverEventArgs)consumer.Queue.Dequeue();
Console.WriteLine("Receive a Message, start to handle");
// Simulate disconnection
return;
// Simulate long running
Thread.Sleep(60000);
byte[] bytes = ea.Body;
XmlSerializer xs = new XmlSerializer(typeof(RequestMessage));
using (MemoryStream ms = new MemoryStream(bytes))
{
RequestMessage message = (RequestMessage)xs.Deserialize(ms);
Console.WriteLine("Receive a Message, Id:" + message.MessageId + " Message:" + message.Message);
}
// Send the response packet
channel.BasicAck(ea.DeliveryTag, false);
}
}
}
}
}
Note that this function is effective only in point-point mode, that is, in one-stop mode. In the broadcasting mode of publishing and subscription, the results of such configuration items are somewhat different. Let's talk about it in the next article.
Download the complete sample code from here