Introduction

Some days ago, I wrote an article to introduce how to consume RabbitMQ messages via background service in ASP.NET Core.
And in this article, you will learn how to publish RabbitMQ messages.

Run Up RabbitMQ Service

Publishing RabbitMQ Message In ASP.NET Core

RabbitMQ Settings

Adding configuration of RabbitMQ in appsettings.json
  1. {
  2. "Logging": {
  3. "LogLevel": {
  4. "Default": "Warning"
  5. }
  6. },
  7. "AllowedHosts": "*",
  8. "rabbit": {
  9. "UserName": "guest",
  10. "Password": "guest",
  11. "HostName": "localhost",
  12. "VHost": "/",
  13. "Port": 5672
  14. }
  15. }
Creating a class that maps the rabbit section in appsettings.json
  1. public class RabbitOptions
  2. {
  3. public string UserName { get; set; }
  4. public string Password { get; set; }
  5. public string HostName { get; set; }
  6. public int Port { get; set; } = 5672;
  7. public string VHost { get; set; } = "/";
  8. }

Reuse Channels of RabbitMQ Connection

Why should we reuse channels?
Based on the official .NET/C# Client API Guide document, we can consider reusing channels because these are long-lived but since many recoverable protocol errors will result in channel closure, the closing and opening of new channels per operation are usually unnecessary.
Here, we will use the object pool to do this job! Microsoft provides a package named Microsoft.Extensions.ObjectPool can help us simplify some of the work.
Before using the object pool, we should declare the policy of the channel at first. Here, we create a class named RabbitModelPooledObjectPolicy that implements IPooledObjectPolicy<IModel>.
  1. using Microsoft.Extensions.ObjectPool;
  2. using Microsoft.Extensions.Options;
  3. using RabbitMQ.Client;
  4. public class RabbitModelPooledObjectPolicy : IPooledObjectPolicy<IModel>
  5. {
  6. private readonly RabbitOptions _options;
  7. private readonly IConnection _connection;
  8. public RabbitModelPooledObjectPolicy(IOptions<RabbitOptions> optionsAccs)
  9. {
  10. _options = optionsAccs.Value;
  11. _connection = GetConnection();
  12. }
  13. private IConnection GetConnection()
  14. {
  15. var factory = new ConnectionFactory()
  16. {
  17. HostName = _options.HostName,
  18. UserName = _options.UserName,
  19. Password = _options.Password,
  20. Port = _options.Port,
  21. VirtualHost = _options.VHost,
  22. };
  23. return factory.CreateConnection();
  24. }
  25. public IModel Create()
  26. {
  27. return _connection.CreateModel();
  28. }
  29. public bool Return(IModel obj)
  30. {
  31. if (obj.IsOpen)
  32. {
  33. return true;
  34. }
  35. else
  36. {
  37. obj?.Dispose();
  38. return false;
  39. }
  40. }
  41. }
There are two important methods in it, one is Create, the other one is Return.
The Create method tells the pool how to create the channel object.
The Return method tells the pool that if the channel object is still in a state that can be used, we should return it to the pool; otherwise, we should not use it the next time.

RabbitMQ Manager

We create a management interface to handle the Publish method.
  1. public interface IRabbitManager
  2. {
  3. void Publish<T>(T message, string exchangeName, string exchangeType, string routeKey)
  4. where T : class;
  5. }
The following code demonstrates an implementing class of it.
  1. public class RabbitManager : IRabbitManager
  2. {
  3. private readonly DefaultObjectPool<IModel> _objectPool;
  4. public RabbitManager(IPooledObjectPolicy<IModel> objectPolicy)
  5. {
  6. _objectPool = new DefaultObjectPool<IModel>(objectPolicy, Environment.ProcessorCount * 2);
  7. }
  8. public void Publish<T>(T message, string exchangeName, string exchangeType, string routeKey)
  9. where T : class
  10. {
  11. if (message == null)
  12. return;
  13. var channel = _objectPool.Get();
  14. try
  15. {
  16. channel.ExchangeDeclare(exchangeName, exchangeType, true, false, null);
  17. var sendBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(message));
  18. var properties = channel.CreateBasicProperties();
  19. properties.Persistent = true;
  20. channel.BasicPublish(exchangeName, routeKey, properties, sendBytes);
  21. }
  22. catch (Exception ex)
  23. {
  24. throw ex;
  25. }
  26. finally
  27. {
  28. _objectPool.Return(channel);
  29. }
  30. }
  31. }
We create an object pool in the constructor. Before publishing messages to RabbitMQ, we should get a channel from the object pool, then construct the payload.
After publishing, we should return this channel object to the object pool whether the publish succeeds or fails.

RabbitMQ Extension

Create an extension method to simplify the registration.
  1. public static class RabbitServiceCollectionExtensions
  2. {
  3. public static IServiceCollection AddRabbit(this IServiceCollection services, IConfiguration configuration)
  4. {
  5. var rabbitConfig = configuration.GetSection("rabbit");
  6. services.Configure<RabbitOptions>(rabbitConfig);
  7. services.AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>();
  8. services.AddSingleton<IPooledObjectPolicy<IModel>, RabbitModelPooledObjectPolicy>();
  9. services.AddSingleton<IRabbitManager, RabbitManager>();
  10. return services;
  11. }
  12. }
Go to Startup class.
  1. public void ConfigureServices(IServiceCollection services)
  2. {
  3. services.AddRabbit(Configuration);
  4. services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
  5. }

Usage of RabbitMQ Manager

We add some code in ValuesController .
  1. [Route("api/[controller]")]
  2. [ApiController]
  3. public class ValuesController : ControllerBase
  4. {
  5. private IRabbitManager _manager;
  6. public ValuesController(IRabbitManager manager)
  7. {
  8. _manager = manager;
  9. }
  10. // GET api/values
  11. [HttpGet]
  12. public ActionResult<IEnumerable<string>> Get()
  13. {
  14. // other opreation
  15. // if above operation succeed, publish a message to RabbitMQ
  16. var num = new System.Random().Next(9000);
  17. // publish message
  18. _manager.Publish(new
  19. {
  20. field1 = $"Hello-{num}",
  21. field2 = $"rabbit-{num}"
  22. }, "demo.exchange.topic.dotnetcore", "topic", "*.queue.durable.dotnetcore.#");
  23. return new string[] { "value1", "value2" };
  24. }
  25. }
Here we will create a topic type exchange named demo.exchange.topic.dotnetcore, and it also will send the message to the queues that are binding the routing key named *.queue.durable.dotnetcore.#.
Note
A message in a queue will only be consumed by one consumer.

Result

For demonstration, we create a queue and bind it to the routing key instead of creating consumers.
Publishing RabbitMQ Message In ASP.NET Core
After publishing a message, we can find out if the message is ready.
Publishing RabbitMQ Message In ASP.NET Core
We can use the GetMessage button to check the message.
Publishing RabbitMQ Message In ASP.NET Core

Summary

This article showed you how to publish the RabbitMQ message in ASP.NET Core. I hope this will help you!