Skip to content
This repository was archived by the owner on Apr 29, 2022. It is now read-only.

Commit 135c341

Browse files
Merge pull request #20 from AntonyVorontsov/feature/wildcards-docs
Feature/wildcards docs
2 parents 694e4e8 + 3239e27 commit 135c341

File tree

7 files changed

+133
-78
lines changed

7 files changed

+133
-78
lines changed

.github/workflows/dotnetcore.yml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
name: .NET Core
2+
3+
on: [push]
4+
5+
jobs:
6+
workflow:
7+
8+
runs-on: ubuntu-latest
9+
steps:
10+
- uses: actions/checkout@v2
11+
- name: Setup .NET Core
12+
uses: actions/setup-dotnet@v1
13+
with:
14+
dotnet-version: 3.1.100
15+
- name: Build
16+
run: dotnet build --configuration Release
17+
- name: Run tests
18+
run: dotnet test --configuration Release

docs/changelog.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,15 @@
22

33
All notable changes to this library will be documented in this file.
44

5+
## [3.1.1] - 2020-01-20
6+
7+
### Added
8+
9+
- Pattern matching (`WildcardExtensions`) so message handlers can now
10+
- Extension methods which allow user to set the exact exchange from which messages will be processed by message handlers.
11+
- `MessageHandlingService` which is responsible for message processing.
12+
- `WildcardExtensions` and `MessageHandlingService` unit tests.
13+
514
## [2.2.1] copy of [3.1.0] - 2019-12-06
615

716
### Added

docs/exchange-configuration.md

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
### Basics
44

5-
Client applications work with exchanges and queues, and they must be "declared" and "bound" to each other is a certain way before they can be used.
6-
Queues and exchanges can also be customized by using additional parameters. This library allows to do this routine simply calling `AddExchange` method passing additional parameters to it.
5+
Client applications work with exchanges and queues which must be "declared" and "bound" to each other in a certain way before they can be used.
6+
Queues and exchanges can also be customized by using additional parameters. This library allows you to do this routine simply calling the `AddExchange` method passing additional parameters to it.
77
You are allowed to configure multiple exchanges with multiple queues bound to them.
88

99
Your `Startup` code will look like this.
@@ -57,26 +57,26 @@ And the `appsettings.json` file will be like this.
5757
}
5858
```
5959

60-
The RabbitMQ client configuration section not specified in this example, for more information see the [documentation](rabbit-configuration.md) file.
60+
The RabbitMQ client configuration section is not specified in this example, for more information see the [documentation](rabbit-configuration.md) file.
6161

6262
Exchanges can be configured with properties:
63-
- `Type` - exchange type (direct, topic, fanout). Default value is `"direct"`.
64-
- `Durable` - durability option. Default value is `true`.
65-
- `AutoDelete` - option for exchange auto deleting. Default value is `false`.
66-
- `Arguments` - dictionary of additional arguments. Default value is `null`.
67-
- `RequeueFailedMessages` - option that specifies behaviour of re-queueing failed messages with delay through dead-letter-exchange. Default value is `true`. Mechanism of sending delayed messages covered in the [documentation](message-production.md).
68-
- `DeadLetterExchange` - default value for dead-letter-exchange. Default value for dead-letter-exchange name is `"default.dlx.exchange"`.
69-
- `Queues` - collection of queues bound to the exchange.
63+
- `Type` - an exchange type (direct, topic, fanout). The default value is `"direct"`.
64+
- `Durable` - a durability option. The default value is `true`.
65+
- `AutoDelete` - an option for exchange auto deleting. The default value is `false`.
66+
- `Arguments` - a dictionary of additional arguments. The default value is `null`.
67+
- `RequeueFailedMessages` - an option that specifies behaviour of re-queueing failed messages with certain delay through the dead-letter-exchange. The default value is `true`. The mechanism of sending delayed messages is covered in the [documentation](message-production.md).
68+
- `DeadLetterExchange` - a value for dead-letter-exchange. The default value for the dead-letter-exchange name is `"default.dlx.exchange"`.
69+
- `Queues` - a collection of queues bound to the exchange.
7070

7171
Queue options:
72-
- `Name` - queue name.
73-
- `Durable` - durability option. Default value is `true`.
74-
- `AutoDelete` - option for queue auto deleting. Default value is `false`.
75-
- `Exclusive` - exclusive option. Default value is `false`.
76-
- `Arguments` - dictionary of additional [arguments](https://www.rabbitmq.com/queues.html#optional-arguments). Default value is `null`.
77-
- `RoutingKeys` - collection of routing keys that queue "listens".
72+
- `Name` - a queue name.
73+
- `Durable` - a durability option. The default value is `true`.
74+
- `AutoDelete` - an option for queue auto deleting. The default value is `false`.
75+
- `Exclusive` - an exclusive option. The default value is `false`.
76+
- `Arguments` - a dictionary of additional [arguments](https://www.rabbitmq.com/queues.html#optional-arguments). The default value is `null`.
77+
- `RoutingKeys` - a collection of routing keys that the queue "listens".
7878

79-
Taking into account all the default values that you can skip configuration will look like this.
79+
Taking into account all the default values that you can skip, configuration will look like this.
8080

8181
```json
8282
{
@@ -92,7 +92,7 @@ Taking into account all the default values that you can skip configuration will
9292
}
9393
```
9494

95-
If you want to use routing keys matching with queue names you can skip `"RoutingKeys"` option and queues will be bound to the exchange by their names.
95+
If you want to use routing keys matching with queue names you can skip the `"RoutingKeys"` option and queues will be bound to the exchange by their names.
9696

9797
```json
9898
{
@@ -184,5 +184,6 @@ services.AddRabbitMqClient(clientConfiguration)
184184
.AddConsumptionExchange("ConsumptionExchange", exchangeConfiguration);
185185
```
186186

187-
For the RabbitMQ client configuration see the [Previous page](rabbit-configuration.md) <br>
187+
For the RabbitMQ client configuration see the [Previous page](rabbit-configuration.md)
188+
188189
For message production features see the [Next page](message-production.md)

docs/message-consumption.md

Lines changed: 38 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22

33
### Starting a consumer
44

5-
The first step that needs to be done to retrieve messages from queues is to start a consumer. This can be achieved by calling `StartConsuming` method of `IQueueService`.
6-
Without calling `StartConsuming` consumption exchanges will work only in production mode.
5+
The first step that has to be done to retrieve messages from queues is to start a consumer. This can be achieved by calling the `StartConsuming` method of `IQueueService`.
6+
Consumption exchanges will work only in a message-production mode if the `StartConsuming` method won't be called.
77

8-
Let's say that your configuration look like this.
8+
Let's say that your configuration looks like this.
99

1010
```c#
1111
public class Startup
@@ -27,13 +27,13 @@ public class Startup
2727
}
2828
```
2929

30-
You can register an `IHostedService` and inject the instance of `IQueueService` into it.
30+
You can register `IHostedService` and inject an instance of `IQueueService` into it.
3131

3232
```c#
3333
services.AddSingleton<IHostedService, ConsumingService>();
3434
```
3535

36-
And then simply call `StartConsuming` so consumer can work in a background.
36+
And then simply call `StartConsuming` so a consumer can work in the background.
3737

3838
```c#
3939
public class ConsumingService : IHostedService
@@ -64,7 +64,7 @@ public class ConsumingService : IHostedService
6464
}
6565
```
6666

67-
Otherwise you can implement a worker service template from .Net Core 3 like this.
67+
Otherwise, you can implement a worker service template from .Net Core 3 like this.
6868

6969
```c#
7070
public class Program
@@ -108,7 +108,7 @@ public class Worker : BackgroundService
108108

109109
The second step without which receiving messages does not make sense - configuration of message handling services. If there are no message handlers then received messages will not be processed.
110110

111-
Message handlers are classes that implement `IMessageHandler` interface (or a few others) and contain functionality including error handling for processing messages.
111+
Message handlers are classes that implement the `IMessageHandler` interface (or a few others) and contain functionality (including error handling) for processing messages.
112112
You can register `IMessageHandler` in your `Startup` like this.
113113

114114
```c#
@@ -132,30 +132,41 @@ public class Startup
132132
}
133133
```
134134

135-
The RabbitMQ client configuration and exchange configuration sections not specified in this example, but covered [here](rabbit-configuration.md) and [here](exchange-configuration.md).
135+
RabbitMQ client and exchange configuration sections are not specified in this example, but covered [here](rabbit-configuration.md) and [here](exchange-configuration.md).
136+
137+
`IMessageHandler` implementation will "listen" for messages by the specified routing key, or a collection of routing keys. If it is necessary, you can also register multiple message handler at once.
138+
139+
```c#
140+
services.AddRabbitMqClient(clientConfiguration)
141+
.AddExchange("ExchangeName", isConsuming: true, exchangeConfiguration)
142+
.AddMessageHandlerSingleton<CustomMessageHandler>("first.routing.key");
143+
.AddMessageHandlerSingleton<AnotherCustomMessageHandler>(new[] { "second.routing.key", "third.routing.key" });
144+
```
145+
146+
You can also use **pattern matching** in routes where `*` (star) can substitute for exactly one word and `#` (hash) can substitute for zero or more words.
136147

137-
`IMessageHandler` implementation will "listen" for messages by specified routing key, or a collection of routing keys.
138148
```c#
139149
services.AddRabbitMqClient(clientConfiguration)
140150
.AddExchange("ExchangeName", isConsuming: true, exchangeConfiguration)
141-
.AddMessageHandlerSingleton<CustomMessageHandler>(new[] { "first.routing.key", "second.routing.key", "third.routing.key" });
151+
.AddMessageHandlerSingleton<CustomMessageHandler>("*.routing.*");
152+
.AddMessageHandlerSingleton<AnotherCustomMessageHandler>(new[] { "#.key", "third.*" });
142153
```
143154

144-
You can register it in two modes - **singleton** or **transient** using `AddMessageHandlerSingleton` or `AddMessageHandlerTransient` methods respectively.
155+
You are also allowed to specify the exact exchange which will be "listened" by a message handler with the given routing key (or a pattern).
145156

146157
```c#
147158
services.AddRabbitMqClient(clientConfiguration)
148159
.AddExchange("ExchangeName", isConsuming: true, exchangeConfiguration)
149-
.AddMessageHandlerTransient<CustomMessageHandler>(new[] { "first.routing.key", "second.routing.key", "third.routing.key" });
160+
.AddMessageHandlerSingleton<CustomMessageHandler>("*.*.*", "ExchangeName");
161+
.AddMessageHandlerSingleton<AnotherCustomMessageHandler>("routing.key", "ExchangeName");
150162
```
151163

152-
If it is necessary you can also register multiple message handler at once.
164+
You can register it in two modes, **singleton** or **transient**, using `AddMessageHandlerSingleton` or `AddMessageHandlerTransient` methods respectively.
153165

154166
```c#
155167
services.AddRabbitMqClient(clientConfiguration)
156168
.AddExchange("ExchangeName", isConsuming: true, exchangeConfiguration)
157-
.AddMessageHandlerTransient<CustomMessageHandler>("first.routing.key")
158-
.AddMessageHandlerTransient<AnotherCustomMessageHandler>("second.routing.key");
169+
.AddMessageHandlerTransient<CustomMessageHandler>(new[] { "#.key", "third.*" });
159170
```
160171

161172
You can also set multiple message handlers for managing messages received by one routing key. This case can happen when you want to divide responsibilities between services (e.g. one contains business logic, and the other writes messages in the database).
@@ -168,8 +179,8 @@ services.AddRabbitMqClient(clientConfiguration)
168179
.AddMessageHandlerSingleton<OneMoreCustomMessageHandler>("first.routing.key");
169180
```
170181

171-
`IMessageHandler` consists of one method `Handle` that gets a message in string format. You can deserialize it (if it is a json message) or handle a raw value.
172-
Thus, message handler will look like this.
182+
`IMessageHandler` consists of one method `Handle` that gets a message in a string format. You can deserialize it (if it is a json message) or handle a raw value.
183+
Thus, a message handler will look like this.
173184

174185
```c#
175186
public class CustomMessageHandler : IMessageHandler
@@ -182,7 +193,7 @@ public class CustomMessageHandler : IMessageHandler
182193
}
183194
```
184195

185-
You can also inject services inside `IMessageHandler` constructor.
196+
You can also inject services inside the `IMessageHandler` constructor.
186197

187198
```c#
188199
public class CustomMessageHandler : IMessageHandler
@@ -200,8 +211,8 @@ public class CustomMessageHandler : IMessageHandler
200211
}
201212
```
202213

203-
The only exception is `IQueueService`, you can't inject it because of the appearance of cyclic dependencies. If you want to use an instance of `IQueueService` (e.g. handle one message and send another) use `INonCyclicMessageHandler`.
204-
`INonCyclicMessageHandler` can be registered the same way as `IMessageHandler`. There are similar semantic methods for adding it in **singleton** or **transient** mode.
214+
The only exception is the `IQueueService`. You can't inject it inside a message handler because of the appearance of cyclic dependencies. If you want to use an instance of `IQueueService` (e.g. handle one message and send another) use `INonCyclicMessageHandler`.
215+
`INonCyclicMessageHandler` can be registered the same way as `IMessageHandler`. There are similar semantic methods for adding it in **singleton** or **transient** modes.
205216

206217
```c#
207218
services.AddRabbitMqClient(clientConfiguration)
@@ -232,8 +243,8 @@ public class CustomNonCyclicMessageHandler : INonCyclicMessageHandler
232243

233244
### Asynchronous message handlers
234245

235-
`IMessageHandler` and `INonCyclicMessageHandler` work synchronously, but if you want an async version then use `IAsyncMessageHandler` and `IAsyncNonCyclicMessageHandler`.
236-
There are extension methods that allows you to register it the same way as synchronous ones in **singleton** or **transient** modes.
246+
`IMessageHandler` and `INonCyclicMessageHandler` work synchronously, but if you want to use an async technology then use `IAsyncMessageHandler` and `IAsyncNonCyclicMessageHandler`.
247+
There are extension methods that allow you to register it the same way as synchronous ones in **singleton** or **transient** modes.
237248

238249
```c#
239250
services.AddRabbitMqClient(clientConfiguration)
@@ -288,11 +299,11 @@ So you can use async/await power inside your message handler.
288299

289300
The message handling process is organized as follows:
290301

291-
- `IQueueMessage` receives a message as a byte array and decodes it in UTF8 string.
292-
- `IQueueMessage` checks if there are any message handlers in collections of `IMessageHandler`, `IAsyncMessageHandler`, `INonCyclicMessageHandler` and `IAsyncNonCyclicMessageHandler` instances and forwards a message to them.
293-
- All subscribed message handlers (`IMessageHandler`, `IAsyncMessageHandler`, `INonCyclicMessageHandler`, `IAsyncNonCyclicMessageHandler`) process the message.
294-
- `IQueueMessage` acknowledges the message by its `DeliveryTag`.
295-
- If any exception occurs `IQueueMessage` acknowledges the message anyway and checks if the message has to be re-send. If exchange option `RequeueFailedMessages` is set `true` then `IQueueMessage` adds a header `"requeued"` to the message and sends it again with delay in 60 seconds. Mechanism of sending delayed messages covered in the message production [documentation](message-production.md).
302+
- `IQueueMessage` receives a message and delegates it to `IMessageHandlingService`.
303+
- `IMessageHandlingService` gets a message (as a byte array) and decodes it to the UTF8 string. It also checks if there are any message handlers in collections of `IMessageHandler`, `IAsyncMessageHandler`, `INonCyclicMessageHandler` and `IAsyncNonCyclicMessageHandler` instances and forwards a message to them.
304+
- All subscribed message handlers (`IMessageHandler`, `IAsyncMessageHandler`, `INonCyclicMessageHandler`, `IAsyncNonCyclicMessageHandler`) process the given message.
305+
- `IMessageHandlingService` acknowledges the message by its `DeliveryTag`.
306+
- If any exception occurs `IMessageHandlingService` acknowledges the message anyway and checks if the message has to be re-send. If exchange option `RequeueFailedMessages` is set `true` then `IMessageHandlingService` adds a header `"requeued"` to the message and sends it again with delay in 60 seconds. Mechanism of sending delayed messages covered in the message production [documentation](message-production.md).
296307
- If any exception occurs within handling the message that has been already re-sent that message will not be re-send again (re-send happens only once).
297308

298309
For message production features see the [Previous page](message-production.md)

0 commit comments

Comments
 (0)