Most mature messaging systems have the possibility to "dead letter" messages. Learn when this occurs in RabbitMQ and what happens with a dead letter message.
- [Instructor] Most mature messaging systems have a way of dealing with messages that go nowhere. These are called dead letters. In RabbitMQ messages can become dead letters when one of the following occurs. The message is rejected or negatively acknowledged by the consumer and the consumer tells RabbitMQ not to requeue the message or the message expires due to a time-to-live option specified on the queue or on the message. And the last reason could be that the queue exceeded a length limit. These dead letter messages are published through a dead letter exchange. This is a regular exchange that you can specify when declaring the queue. RabbitMQ will also add some extra headers to the message allowing you to identify where it came from and why and when it was dead lettered. Each time a message is dead lettered a new entry is prepended to the x-death header. Each entry will contain key-value pairs that provide information about the queue the message was in, the reason the message was dead lettered, the date and time when this happened, the exchange the message was published to and the original routing keys. The first time a message is dead lettered RabbitMQ will also add three headers containing details of the original dead-lettering event. These values queues never change. Let's create a new application that will handle our dead letter messages. In Visual Studio create a new project and select the .NET Core Console App template. Give the application a name, and click on the Create button. Next, in Solution Explorer right-click on your project and select Manage NuGet Packages. In the Browse tab search for RabbitMQ.Client, and install the package. Now, in the Program.cs file, we can add the using statement using RabbitMQ.Client and we need the necessary code to connect. You can find this code in the Exercise Files. In the Exercise Files, you can open the Ch_03 folder, and open the connectionCode.txt file. Copy this code and paste it in the Main method of our program. Also change the username and password and as you can see I'm using the backoffice credentials. In a real-life application you would want to create a separate user for this application. Now that we can connect we'll declare an exchange first, called the channel.ExchangeDeclare method, and give the exchange a name. I'll call it DLX which is short for Dead Letter Exchange. I'll make this exchange a direct exchange, and make it a durable exchange, meaning that it will survive the broker restart. We'll also declare a queue, with the QueueDeclare method. And we call it deadLetters, make it a durable exchange. We don't want it to be exclusive to our connection and we don't want it to automatically delete when we disconnect. And then we'll bind this queue to the exchange. So we'll bind the deadLetters queue to the DLX exchange with an empty routing key. Now we need a consumer. We'll find that in another namespace. So we'll add it using RabbitMQ.Client.Events namespace. And then we can create a new instance of the EventingBasicConsumer, passing in the channel we've created earlier. This consumer will fire a ReceivedEvent whenever a message is received. So we'll handle this event and in this event we'll extract a message. The message will be binary so we'll use the System.Text.Encoding .UTF8.GetString method to decode it from the eventArgs.Body property. We'll also extract the deathReason. Again these will be bytes so we'll extract the bytes first. And we find that in the eventArgs.BasicProperties.Headers and in the x-first-death-reason header, and we'll cast it to a byte array. Now that we have the raw bytes you can use the System.Text.Encoding .UTF8.GetString method again to decode these bytes to a regular string. And then we can use Console.writeLine to write this to the Console. So I'll write Deadletter and the message and then the Reason. Finally I'll call channel.Basic Consume to actually start consuming our deadLetters queue. I'll pass in true so that we automatically acknowledge the fact that we have received the message and I'll also provide our consumer. And then I'll just call Console.ReadLine so that our application doesn't exit automatically. Now we need a way of getting a message on the dead letter exchange. In our backoffice application we can define the dead letter exchange of our queue and then reject a messages we receive. You can find the backoffice application in the Exercise Files in the Ch_03 folder. In the 03_04 Begin folder you'll find a BackOffice folder that contains the BackOffice solution. In the Program.cs file before we declare the queue we'll create a new Dictionary of string and object, and we'll provide one element with the key being x-dead-letter-exchange and the value being DLX, and then all we need to do is pass these arguments to our QueueDeclare call. In the Received handler we'll now reject a message by calling channel.BasicReject and provide the delivery tag of the message. This is so that RabbitMQ knows which message was actually rejected. And also tell RabbitMQ that we don't want it to requeue the message, and because we're now manually rejecting the message we'll change the auto-ack argument to false. This way we won't automatically acknowledge the fact that we have received a message. We'll also delete the existing queue because we are recreating it with different options. In a browser go to the management UI at localhost and port 15672. Click on the Queues tab, select the backOfficeQueue, and at the bottom of the page, click on the Delete Queue button and confirm. Now we can run our applications. So I'll start the backOffice application by selecting the Debug menu and clicking on Start Without Debugging. I'll do the same for the deadLetter application. And we'll do the same for our web application which actually sends out the messages. You can find the web application in the Exercise Files, in the Ch_03 folder in the 03_04 Begin folder. There is a web application folder that contains the solution. To start the web application open the Debug menu and select Start Without Debugging. In the browser that pops up click on the TOURS link at the top of the page, scroll down, select learn more for the Backpack Cal tour, scroll down again, and click on a book now button. Put in the necessarily details and click on the Book button. And as you can see our dead letter service has received a dead lettered message and we can see that the reason was the fact that it was rejected. Dead lettering is a common pattern in messaging systems. It allows you to investigate potential issues with your applications. You might also have applications consume the dead letter messages to trigger alerts or execute corrective actions. All in all dead letter exchanges are a useful feature of RabbitMQ and they're crucial to any critical messaging system.
- How message-based systems are used
- The AMQP protocol
- Exchange type use cases
- Publishing to RabbitMQ
- Consuming from RabbitMQ
- Filtering messages with direct and topic exchanges
- Setting up and using authentication
- Authorizing and blocking application actions
- Tracking message contents for troubleshooting