Best Practices¶
This page collects practical recommendations for using ServiceBus.Core.Rabbit in production systems.
Use Descriptors As The Source Of Truth¶
Topology should be described through descriptors.
Avoid scattering RabbitMQ declaration logic across application code.
graph LR
Descriptor["Descriptor Configuration"] --> Client["Publisher / Subscriber"]
Client --> RabbitMQ
Recommended:
var descriptor = new PublisherBoundDescriptor
{
Exchanges =
{
new ExchangeBoundDescriptor
{
Name = "orders",
Type = ExchangeTypes.Direct,
Durable = true
}
}
};
Avoid:
channel.ExchangeDeclare(...);
channel.QueueDeclare(...);
channel.QueueBind(...);
Use Meaningful Names¶
Use business-oriented names.
Recommended:
orders
orders.created
orders.updated
payments.completed
Avoid:
test
queue1
event2
message
The physical RabbitMQ object name is derived from the descriptor FullName.
The naming model is documented in:
Reference / Descriptors / Topology / ServiceDescriptor
Prefer Direct Or Topic Exchanges¶
For most business scenarios:
| Scenario | Recommended Exchange |
|---|---|
| Exact routing | Direct |
| Pattern routing | Topic |
| Broadcast | Fanout |
| Metadata-based routing | Headers |
Start with direct.
Use topic when routing keys become hierarchical.
Keep Routing Keys Business-Oriented¶
Recommended:
orders.created
orders.updated
payments.authorized
payments.completed
For topic exchanges:
orders.eu.created
orders.us.created
orders.eu.updated
Avoid technical or temporary values.
event1
test
debug
Choose Queue Types Deliberately¶
RabbitMQ queue type is configured through queue arguments.
Arguments =
{
["x-queue-type"] = "quorum"
}
General guidance:
| Queue Type | Recommended Usage |
|---|---|
classic |
Compatibility and simple workloads. |
quorum |
Durable production workloads. |
stream |
Event streaming and replay. |
For production systems, prefer quorum queues unless there is a specific reason not to.
Tune Prefetch Count¶
PrefetchCount controls how many unacknowledged messages RabbitMQ can deliver to a subscriber.
Start conservatively.
PrefetchCount = 10
Use lower values when:
- processing is expensive;
- messages are large;
- fairness between consumers matters.
Use higher values when:
- messages are small;
- processing is fast;
- throughput is more important than fairness.
Use Retry Policies For Transient Failures¶
Retries are useful for temporary failures.
Examples:
- database timeout;
- downstream service unavailable;
- temporary network error.
RetryPolicy = new RetryPolicyDescriptor
{
Enabled = true,
MaxRetry = 5,
Delayed = TimeSpan.FromSeconds(10),
Durable = true,
AutoDelete = false
}
Do not use retries for invalid messages or permanent business failures.
Use Dead Letter Strategies¶
Messages that cannot be processed should be isolated.
Arguments =
{
["x-dead-letter-exchange"] = "orders.dead-letter",
["x-dead-letter-routing-key"] = "orders.failed"
}
Dead-letter queues should be monitored.
Use Delivery Confirmation For Critical Publishers¶
Enable delivery confirmation when the publisher must know whether RabbitMQ accepted the message.
Confirmation = new DeliveryConfirmation
{
Enabled = true,
WaitFor = true,
Timeout = TimeSpan.FromSeconds(30)
}
Use it for:
- financial events;
- integration events;
- audit events;
- critical business messages.
Observe Channel Events¶
Enable channel events when diagnostics and observability are required.
EnableChannelEvents = true
Then attach handlers through:
publisher.OnChannelEventFired.Add(...);
subscriber.OnChannelEventFired.Add(...);
Channel events are useful for:
- flow control;
- callback exceptions;
- channel shutdown events.
Prefer Dependency Injection In Applications¶
Manual construction is useful for learning and tests.
For applications, prefer dependency injection.
This keeps object creation and lifetime management centralized.
See:
How To / Dependency Injection