|
1 | 1 | # Advanced usage |
2 | 2 |
|
3 | | -todo |
| 3 | +`IQueueService` is an interface that implements two other interfaces - `IConsumingService` and `IProducingService`. By default a RabbitMQ Client is registered as `IQueueService` without a logical separation at producing and consuming code. Thus, you can inject only a `IQueueService` instance, and `IConsumingService` or `IProducingService` won't be available. This is not a real deal until you want to control the way a RabbitMQ Client connects to the server. |
4 | 4 |
|
| 5 | +An instance of `IQueueService` opens two connections to the RabbitMQ server, one is for message production and the other one is for message consumption. Normally a RabbitMQ Client is added in a singleton mode, so both connections stay opened while application is running. It is also noticeable that a RabbitMQ Client uses the same credentials for both connections. If you add `IQueueService` in a transient mode (via the `AddRabbitMqClientTransient` extension method) both connections will be opened each time `IQueueService` is being injected somewhere else. This behavior does not fit everybody, so you can change it a little. |
| 6 | + |
| 7 | +You are allowed to register a RabbitMQ Client as an implementation of two interfaces that have been mentioned before - `IConsumingService` and `IProducingService`. Each interface defines its own connection and its own collection of methods, obviously, for message production and message consumption. You can also use different credentials for different connections, and there is an option `ClientProvidedName` which allows you to create a "named" connection (which will be easier to find in the RabbitMQ management UI). There is also a possibility of registering `IConsumingService` and `IProducingService` in different lifetime modes, in case you want your consumption connection to be persist (singleton `IConsumingService`) and open a connection each time you want to send a message (a transient `IProducingService`). This situation will be covered in code examples below. |
| 8 | + |
| 9 | +Let' say your application is a web API and you want to use both `IConsumingService` and `IProducingService`. Your `Startup` will look like this. |
| 10 | + |
| 11 | +```c# |
| 12 | +public class Startup |
| 13 | +{ |
| 14 | + private IConfiguration Configuration { get; } |
| 15 | + |
| 16 | + public Startup(IConfiguration configuration) |
| 17 | + { |
| 18 | + Configuration = configuration; |
| 19 | + } |
| 20 | + |
| 21 | + public void ConfigureServices(IServiceCollection services) |
| 22 | + { |
| 23 | + services.AddControllers(); |
| 24 | + |
| 25 | + // We will use different credentials for connections. |
| 26 | + var rabbitMqConsumerSection = Configuration.GetSection("RabbitMqConsumer"); |
| 27 | + var rabbitMqProducerSection = Configuration.GetSection("RabbitMqProducer"); |
| 28 | + |
| 29 | + // And we also configure different exchanges just for a better example. |
| 30 | + var producingExchangeSection = Configuration.GetSection("ProducingExchange"); |
| 31 | + var consumingExchangeSection = Configuration.GetSection("ConsumingExchange"); |
| 32 | + |
| 33 | + services.AddRabbitMqConsumingClientSingleton(rabbitMqConsumerSection) |
| 34 | + .AddRabbitMqProducingClientSingleton(rabbitMqProducerSection) |
| 35 | + .AddProductionExchange("exchange.to.send.messages.only", producingExchangeSection) |
| 36 | + .AddConsumptionExchange("consumption.exchange", consumingExchangeSection) |
| 37 | + .AddMessageHandlerTransient<CustomMessageHandler>("routing.key"); |
| 38 | + |
| 39 | + services.AddHostedService<ConsumingHostedService>(); |
| 40 | + } |
| 41 | + |
| 42 | + public void Configure(IApplicationBuilder app) |
| 43 | + { |
| 44 | + app.UseRouting(); |
| 45 | + app.UseEndpoints(endpoints => endpoints.MapControllers()); |
| 46 | + } |
| 47 | +} |
| 48 | +``` |
| 49 | + |
| 50 | +We have added `IConsumingService` and `IProducingService` via `AddRabbitMqConsumingClientSingleton` and `AddRabbitMqProducingClientSingleton` extension methods. We have also added two exchanges (for different purposes) via `AddProductionExchange` and `AddConsumptionExchange` methods which are covered in previous documentation sections. To start a message consumption we add a custom `IHostedService`, which injects `IConsumingService` and uses its `StartConsuming` method. |
| 51 | + |
| 52 | +```c# |
| 53 | +public class ConsumingHostedService : IHostedService |
| 54 | +{ |
| 55 | + readonly IConsumingService _consumingService; |
| 56 | + |
| 57 | + public ConsumingHostedService(IConsumingService consumingService) |
| 58 | + { |
| 59 | + _consumingService = consumingService; |
| 60 | + } |
| 61 | + |
| 62 | + public Task StartAsync(CancellationToken cancellationToken) |
| 63 | + { |
| 64 | + _consumingService.StartConsuming(); |
| 65 | + return Task.CompletedTask; |
| 66 | + } |
| 67 | + |
| 68 | + public Task StopAsync(CancellationToken cancellationToken) |
| 69 | + { |
| 70 | + return Task.CompletedTask; |
| 71 | + } |
| 72 | +} |
| 73 | +``` |
| 74 | + |
| 75 | +To send messages we can only use `IProducingService`. Let's inject it inside a controller. |
| 76 | + |
| 77 | +```c# |
| 78 | +[ApiController] |
| 79 | +[Route("api/example")] |
| 80 | +public class ExampleController : ControllerBase |
| 81 | +{ |
| 82 | + readonly ILogger<ExampleController> _logger; |
| 83 | + readonly IProducingService _producingService; |
| 84 | + |
| 85 | + public ExampleController( |
| 86 | + IProducingService producingService, |
| 87 | + ILogger<ExampleController> logger) |
| 88 | + { |
| 89 | + _producingService = producingService; |
| 90 | + _logger = logger; |
| 91 | + } |
| 92 | + |
| 93 | + [HttpGet] |
| 94 | + public async Task<IActionResult> Get() |
| 95 | + { |
| 96 | + _logger.LogInformation($"Sending messages with {typeof(IProducingService)}."); |
| 97 | + var message = new { message = "text" }; |
| 98 | + await _producingService.SendAsync(message, "exchange.to.send.messages.only", "some.routing.key"); |
| 99 | + return Ok(message); |
| 100 | + } |
| 101 | +} |
| 102 | +``` |
| 103 | + |
| 104 | +And the last thing we have to look at is a configuration file. |
| 105 | +``` |
| 106 | +{ |
| 107 | + "RabbitMqConsumer": { |
| 108 | + "ClientProvidedName": "Consumer", |
| 109 | + "TcpEndpoints": [ |
| 110 | + { |
| 111 | + "HostName": "127.0.0.1", |
| 112 | + "Port": 5672 |
| 113 | + } |
| 114 | + ], |
| 115 | + "Port": "5672", |
| 116 | + "UserName": "user-consumer", |
| 117 | + "Password": "passwordForConsumer" |
| 118 | + }, |
| 119 | + "RabbitMqProducer": { |
| 120 | + "ClientProvidedName": "Producer", |
| 121 | + "TcpEndpoints": [ |
| 122 | + { |
| 123 | + "HostName": "127.0.0.1", |
| 124 | + "Port": 5672 |
| 125 | + } |
| 126 | + ], |
| 127 | + "Port": "5672", |
| 128 | + "UserName": "user-producer", |
| 129 | + "Password": "passwordForProducer" |
| 130 | + }, |
| 131 | + "ConsumingExchange": { |
| 132 | + "Queues": [ |
| 133 | + { |
| 134 | + "Name": "consuming.queue", |
| 135 | + "RoutingKeys": [ "routing.key" ] |
| 136 | + } |
| 137 | + ] |
| 138 | + }, |
| 139 | + "ProducingExchange": { |
| 140 | + "Queues": [ |
| 141 | + { |
| 142 | + "Name": "queue.of.producing.exchange", |
| 143 | + "RoutingKeys": [ "produce.messages", "produce.events" ] |
| 144 | + } |
| 145 | + ] |
| 146 | + } |
| 147 | +} |
| 148 | +``` |
| 149 | + |
| 150 | +As you can see, we set up a RabbitMQ client, which will create a connection for message production each time we call a `IProducingService`. We have also configured connections with different names and credentials. And the most important part is that we separated `IQueueService` for a logical parts. Be aware that `IQueueService` won't be available for injecting when you configure a RabbitMQ client in a `IConsumingService` plus `IProducingService` way. |
5 | 151 |
|
6 | 152 | For basic message consumption features see the [Previous page](message-consumption.md) |
0 commit comments