RabbitX 2.3.0
Requires NuGet 6.0 or higher.
dotnet add package RabbitX --version 2.3.0
NuGet\Install-Package RabbitX -Version 2.3.0
<PackageReference Include="RabbitX" Version="2.3.0" />
<PackageVersion Include="RabbitX" Version="2.3.0" />
<PackageReference Include="RabbitX" />
paket add RabbitX --version 2.3.0
#r "nuget: RabbitX, 2.3.0"
#:package RabbitX@2.3.0
#addin nuget:?package=RabbitX&version=2.3.0
#tool nuget:?package=RabbitX&version=2.3.0

RabbitX
The Modern RabbitMQ Integration for .NET 10+
RabbitX is a robust RabbitMQ abstraction library designed exclusively for modern .NET applications. Built on top of the official RabbitMQ.Client 7.x, it provides a clean fluent API for publishers and consumers, built-in resilience patterns, retry policies with exponential backoff, dead letter exchange support, and RPC capabilities.
Version 1.x marks the initial release: a complete RabbitMQ integration solution with 100% feature parity between Fluent API and appsettings.json configuration, comprehensive retry strategies, and automatic dead letter exchange handling.
Our philosophy is simple: make RabbitMQ integration as straightforward as possible while providing enterprise-grade reliability. RabbitX is built with the latest C# 14 features and targets .NET 10. This is not just another library; it's a commitment to simplifying message-driven architectures.
💖 Support the Project
RabbitX is a passion project, driven by the desire to simplify RabbitMQ integration for the .NET community. Maintaining this library requires significant effort: staying current with each .NET release, addressing issues promptly, implementing new features, and keeping documentation up to date.
If RabbitX has helped you build better applications or saved you development time, I would be incredibly grateful for your support. Your contribution—no matter the size—helps me dedicate time to respond to issues quickly, implement improvements, and keep the library evolving alongside the .NET platform.
I'm also looking for sponsors who believe in this project's mission. Sponsorship helps ensure RabbitX remains actively maintained and continues to serve the .NET community for years to come.
Of course, there's absolutely no obligation. If you prefer, simply starring the repository or sharing RabbitX with fellow developers is equally appreciated!
⭐ Star the repository on GitHub to raise its visibility
💬 Share RabbitX with your team or community
☕ Support via Donations:
✨ Features
- Fluent API: Type-safe configuration with full IntelliSense support
- appsettings.json: Complete configuration from JSON files with 100% parity
- Multiple Publishers & Consumers: Configure and use multiple named publishers and consumers
- Reliable Publishing: Publisher confirmations with broker acknowledgments
- Retry Policies: Specific delays or exponential backoff with jitter support
- Dead Letter Exchange: Automatic DLX setup for failed message routing
- RPC Support: Request-Reply pattern with Direct Reply-To optimization
- QoS Control: Prefetch count, prefetch size, and global QoS settings
- Connection Recovery: Automatic reconnection on connection failures
- Built-in Resilience: Retry policies with exponential backoff and jitter via
RetryDelayProvider - Health Checks: Built-in health check for ASP.NET Core with connection and blocked state detection
- OpenTelemetry: Distributed tracing, metrics, and W3C TraceContext propagation
🎉 What's New in 1.2.0
OpenTelemetry Instrumentation! RabbitX 1.2.0 adds full observability support:
- Distributed Tracing: Automatic spans for publish, consume, and RPC operations following OTel Messaging Semantic Conventions
- 16 Metrics: Counters and histograms for messages published/consumed, durations, errors, retries, RPC calls, and connections
- W3C TraceContext: Automatic propagation of
traceparent/tracestatethrough AMQP headers, linking producer and consumer spans across services - Zero Overhead: No performance impact when OpenTelemetry SDK is not configured
// Add RabbitX tracing and metrics to your OTel pipeline
builder.Services.AddOpenTelemetry()
.WithTracing(tracing => tracing
.AddRabbitXInstrumentation()
.AddOtlpExporter())
.WithMetrics(metrics => metrics
.AddRabbitXInstrumentation()
.AddOtlpExporter());
See CHANGELOG.md for full details.
🚀 Getting Started
Installation
dotnet add package RabbitX
Configuration
Register RabbitX in your Program.cs with either Fluent API or appsettings.json.
Option A: Fluent API
builder.Services.AddRabbitX(options => options
.UseConnection("localhost", 5672)
.UseCredentials("guest", "guest")
.AddPublisher("OrderPublisher", pub => pub
.ToExchange("shop.orders.exchange", "topic")
.WithRoutingKey("orders.created"))
.AddConsumer("OrderConsumer", con => con
.FromQueue("shop.orders.created.queue")
.BindToExchange("shop.orders.exchange", "orders.created")));
Option B: appsettings.json
{
"RabbitX": {
"Connection": { "HostName": "localhost", "UserName": "guest", "Password": "guest" },
"Publishers": {
"OrderPublisher": { "Exchange": "shop.orders.exchange", "ExchangeType": "topic", "RoutingKey": "orders.created" }
},
"Consumers": {
"OrderConsumer": { "Queue": "shop.orders.created.queue", "Exchange": "shop.orders.exchange", "RoutingKey": "orders.created" }
}
}
}
builder.Services.AddRabbitX(builder.Configuration);
Publishing Messages
Inject IPublisherFactory and create a publisher to send messages:
public record OrderCreated(Guid OrderId, string CustomerId, decimal Total);
public class OrderService(IPublisherFactory factory)
{
public async Task CreateOrderAsync(Order order)
{
var publisher = factory.CreateReliablePublisher<OrderCreated>("OrderPublisher");
var message = new OrderCreated(order.Id, order.CustomerId, order.Total);
await publisher.PublishWithConfirmAsync(message);
}
}
Consuming Messages
Create a handler to process incoming messages:
public class OrderCreatedHandler : IMessageHandler<OrderCreated>
{
public Task<ConsumeResult> HandleAsync(MessageContext<OrderCreated> context, CancellationToken ct)
{
var order = context.Message;
Console.WriteLine($"Processing order {order.OrderId} for {order.CustomerId}");
// Process the order...
return Task.FromResult(ConsumeResult.Ack);
}
}
Register the handler and start the consumer as a hosted service:
builder.Services.AddMessageHandler<OrderCreated, OrderCreatedHandler>();
builder.Services.AddHostedConsumer<OrderCreated>("OrderConsumer");
📅 Versioning & .NET Support Policy
RabbitX follows a clear versioning strategy aligned with .NET's release cadence:
Version History
| RabbitX | .NET | C# | Status |
|---|---|---|---|
| 1.x | .NET 10 | C# 14 | Current |
Future Support Policy
RabbitX will always support the current LTS version plus the next standard release. When a new LTS version is released, support for older versions will be discontinued:
| RabbitX | .NET | C# | Notes |
|---|---|---|---|
| 1.x | .NET 10 | C# 14 | LTS only |
| 2.x | .NET 10 + .NET 11 | C# 14 / C# 15 | LTS + Standard |
| 3.x | .NET 12 | C# 16 | New LTS (drops .NET 10/11) |
Why this policy?
- Focused development: By limiting supported versions, we can dedicate more effort to quality, performance, and new features
- Modern features: Each .NET version brings improvements that RabbitX can fully leverage
- Clear upgrade path: Users know exactly when to plan their upgrades
Note: We recommend always using the latest LTS version of .NET for production applications.
📚 Documentation
Comprehensive guides to help you master RabbitX:
Getting Started
- Getting Started - Installation, requirements, and first message
- Configuration - Fluent API and appsettings.json complete reference
Core Features
- Publishers - Publishing messages with confirms
- Consumers - Consuming messages with handlers
- RPC - Request-Reply pattern
Advanced Topics
- Retry & Resilience - Retry policies and strategies
- Dead Letter Queues - DLX configuration
- Health Checks - ASP.NET Core health check integration
- OpenTelemetry - Distributed tracing, metrics, and context propagation
Examples
Check out the samples folder for complete working examples.
📋 Requirements
- .NET 10.0 or later
- RabbitMQ 3.12+ (recommended)
- RabbitMQ.Client 7.0.0 (included as dependency)
🙏 Acknowledgments
RabbitX is built on top of excellent open-source libraries:
- RabbitMQ.Client - The official RabbitMQ .NET client by VMware
We are immensely grateful for their contribution to the .NET ecosystem, which provided the foundation for this library.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.Extensions.Configuration.Abstractions (>= 10.0.8)
- Microsoft.Extensions.Configuration.Binder (>= 10.0.8)
- Microsoft.Extensions.DependencyInjection.Abstractions (>= 10.0.8)
- Microsoft.Extensions.Diagnostics.HealthChecks (>= 10.0.8)
- Microsoft.Extensions.Hosting.Abstractions (>= 10.0.8)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.8)
- Microsoft.Extensions.Options (>= 10.0.8)
- Microsoft.Extensions.Options.ConfigurationExtensions (>= 10.0.8)
- OpenTelemetry.Api (>= 1.15.3)
- RabbitMQ.Client (>= 7.2.1)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated | |
|---|---|---|---|
| 2.3.0 | 176 | 8/20/2026 | |
| 2.2.1 | 460 | 8/18/2026 | |
| 2.2.0 | 443 | 8/17/2026 | |
| 2.1.0 | 2,744 | 7/6/2026 | |
| 2.0.0 | 2,594 | 7/4/2026 | |
| 1.7.1 | 2,559 | 7/3/2026 | |
| 1.7.0 | 2,637 | 7/1/2026 | |
| 1.6.0 | 3,771 | 6/11/2026 | |
| 1.5.0 | 3,801 | 6/10/2026 | |
| 1.4.1 | 3,074 | 6/10/2026 | |
| 1.4.0 | 4,152 | 6/9/2026 | |
| 1.3.0 | 4,114 | 6/9/2026 | |
| 1.2.4 | 5,014 | 5/26/2026 | |
| 1.2.3 | 4,941 | 5/25/2026 | |
| 1.2.2 | 5,990 | 5/6/2026 | |
| 1.2.1 | 5,917 | 5/6/2026 | |
| 1.2.0 | 11,504 | 2/10/2026 | |
| 1.1.0 | 11,225 | 2/7/2026 |
v2.3.0 - FIXED: a broker outage terminated the host process. ConsumerHostedService, RawConsumerHostedService and RpcConsumerHostedService all rethrew a startup failure out of ExecuteAsync; with the host default BackgroundServiceExceptionBehavior.StopHost that stops the entire application, so a service whose broker was unavailable exited and, because a container restart runs the same failing initial connect, kept exiting in a crash loop until the broker returned - taking down an HTTP API in the same process even though nothing it served depended on messaging. All three hosts now retry with capped exponential backoff and never propagate the failure. AutorecoveringConnection does not cover this case: it recovers a connection established at least once, not one that never succeeded. FIXED: a consumer discarded after a failed start was not disposed, leaking a channel per retry. ADDED: ConsumerRecoveryOptions bound at RabbitX:ConsumerRecovery (InitialDelaySeconds 5, MaxDelaySeconds 60, BackoffMultiplier 2.0), deliberately separate from RetryOptions which bounds message redelivery - a message that keeps failing is given up on, an outage must be retried for as long as it lasts, so there is no attempt limit; plus an optional recovery parameter on all three host constructors, defaulted so existing construction sites keep compiling. BEHAVIOUR CHANGE: the process now stays up with the consumer disconnected, so liveness no longer signals messaging state - assert on the health checks, which report the connection state. v2.2.1 - FIXED: a client certificate configured through Tls:ClientCertificatePath was never presented to the broker. The path was validated for existence but neither SslOption.CertPath nor SslOption.CertPassphrase was assigned, so the underlying client had nothing to send; against a broker with fail_if_no_peer_cert the handshake failed with "tlsv13 alert certificate required" while the connection log reported mutual TLS, because that flag only reflects that a source was configured. The certificate-store and provider sources were unaffected. Anyone on 2.2.0 configuring mutual TLS by file path should upgrade. CHANGED: TLS settings present while Tls:Enabled is false no longer refuse to start - that combination is the normal state of a service deployed with its certificate paths in place before the switch that enables encryption, and the state a rollback leaves behind, so throwing made a staged migration and its rollback impossible; it is now a warning naming the configured settings, and the connection log line continues to state the TLS state of every connection. Both items were found by deploying 2.2.0 to a real environment. v2.2.0 - TLS and mutual TLS support: new TlsOptions section on ConnectionOptions (bindable from configuration and via the new UseTls/UseMutualTls fluent methods and TlsOptionsBuilder), mapped onto RabbitMQ.Client SslOption by the internal SslOptionFactory. Supports a private certificate authority as trust anchor (X509ChainTrustMode.CustomRootTrust), client certificates from a PKCS#12 file, the OS certificate store, or a caller-supplied delegate that is re-invoked per connection so certificates can rotate without a restart, and SASL EXTERNAL authentication (AuthenticationMechanism enum) where the broker derives identity from the client certificate. New fail-closed ConnectionOptionsValidator runs on BOTH AddRabbitX overloads (the IConfiguration overload previously performed no validation at all): TLS enabled without ServerName, TLS on port 5672, EXTERNAL without a client certificate, ambiguous certificate sources, an orphaned certificate passphrase, and deprecated protocol versions all fail at startup - as does the reverse case where TLS settings are present but Enabled is false, which would otherwise connect in plaintext while appearing configured for TLS. FIXED: ConnectionOptions.ConnectionTimeoutSeconds was documented and configurable but never mapped to ConnectionFactory.RequestedConnectionTimeout, so the configured value was ignored and the client default applied; it now also bounds the TLS handshake. Additive and non-breaking: configurations without a Tls section bind and behave exactly as before. v2.1.0 - Restored the type-erased raw-publish surface removed by mistake in 2.0.0 (IRawMessagePublisher, IPublisherFactory.CreateRawPublisher, RabbitMQPublisherFactory raw-publisher cache, RabbitMQPublisher<TMessage>.PublishRawAsync): it does not touch SQL Server, it is generic type-erased AMQP publish, and other consumers (e.g. DeadLetter replay) need it independently of the Outbox. Doc-comments updated to describe it as a general-purpose capability, no longer referencing the removed OutboxDispatcher. Also removed DapperDeadLetterStore — the only file in the DeadLetter drain feature that touched SQL Server/Dapper; AddDeadLetterDrain no longer auto-registers a default IDeadLetterStore, consumers wanting persistence for the drain must supply their own implementation. Dapper and Microsoft.Data.SqlClient NuGet package references removed from RabbitX and RabbitX.Tests — nothing in the library uses them anymore. RabbitMQ-handling surface (raw consumer pipeline, XDeathParser, DeadLetterDrainOptions, DeadLetterDrainHandler, native DLX config) is untouched and remains complete. MINOR release: additive restoration of previously-existing public API plus an internal-only DI behavior change, no public contract broken. v2.0.0 - BREAKING: removed Transactional Outbox (OutboxDispatcher, IOutboxStore, DapperOutboxStore, IOutboxWriter, IOutboxSignal, OutboxSignal, OutboxRow, OutboxOptions, AddOutboxDispatcher) and Consumer Inbox/Idempotency (IdempotentConsumerPipeline<T>, IConsumerUnitOfWork, ConsumerUnitOfWork, IInboxStore, DapperInboxStore, InboxIterationCounter, InboxOptions, AddIdempotentConsumerPipeline, AddIdempotentMessageHandler) primitives, plus the type-erased raw-publish surface used only by the Outbox dispatcher (IRawMessagePublisher, IPublisherFactory.CreateRawPublisher, RabbitMQPublisherFactory raw-publisher cache, RabbitMQPublisher<TMessage>.PublishRawAsync) and the now-orphaned rabbitx.inbox.dedup.hits counter. Reason: scope simplification — RabbitX is now a pure RabbitMQ integration library; message persistence, outbox dispatch, and consumer-side idempotency are the responsibility of each consuming service. IDbConnectionFactory and the internal DbConnectionAsyncExtensions.OpenIfClosedAsync helper were NOT removed — they were relocated from the RabbitX.Outbox namespace to the new RabbitX.Persistence namespace because DapperDeadLetterStore (DeadLetter feature) still depends on them; consuming services must update `using RabbitX.Outbox;` to `using RabbitX.Persistence;` for these two types. DeadLetter feature is unaffected otherwise. v1.7.1 - Connection-pool-exhaustion fix: the 6 remaining synchronous SQL connection opens (OutboxDispatcher.DrainBatchAsync/RunRetentionAsync, DapperDeadLetterStore.InsertIfNotExistsAsync/DeleteExpiredReplayedAsync, IdempotentConsumerPipeline.HandleAsync/TryCleanupAsync) now open their connection via the new internal DbConnectionAsyncExtensions.OpenIfClosedAsync helper, which awaits DbConnection.OpenAsync instead of blocking a thread-pool thread on the synchronous Open(). Under concurrent load (e.g. PaymentsRouter/PaymentsIntegration running ConsumerDispatchConcurrency=15 across 2 consumers each) the blocking Open() calls could starve the CLR thread pool over several hours and exhaust the SQL connection pool. Public API is untouched (IDbConnectionFactory.Create still returns IDbConnection); the helper does a runtime cast to DbConnection with a synchronous Open() fallback for non-DbConnection implementations. Wire format and transactional semantics (commit/rollback ordering, isolation level, idempotency dedup) are byte-identical to 1.7.0 — behavior-preserving, PATCH release. v1.7.0 - Dead-letter hardening: IDeadLetterStore.InsertIfNotExistsAsync now returns DeadLetterInsertResult { NewRow, Reopened, Duplicate } instead of bool (breaking, in-repo only — DapperDeadLetterStore is internal sealed). DapperDeadLetterStore rewrites the capture as an explicit IF-UPDATE-then-INSERT under UPDLOCK/HOLDLOCK: a previously Replayed/Discarded row that dies again is reopened (Status→Pending, DeathCount incremented, CapturedAt/XDeathRaw/Reason refreshed, replay bookkeeping nulled) and reported as Reopened; an unchanged Pending row is Duplicate. Three new OpenTelemetry counters: rabbitx.dlq.captured (tags queue, reason), rabbitx.dlq.redeaths (tags queue, reason), rabbitx.inbox.dedup.hits (tag queue). DeadLetterDrainHandler switches on the result to fire captured/redeaths; IdempotentConsumerPipeline fires dedup.hits on the duplicate branch. Wire format unchanged. Additionally, the dead-letter drain is always-on: removed DeadLetterDrainOptions.Enabled; AddDeadLetterDrain now registers RawConsumerHostedService unconditionally (availability of the admin endpoints is governed by the consuming service's authorization/RBAC, not a config toggle). v1.6.0 - Retry policy consolidation: NEW RabbitX:ConsumerDefaults:Retry configuration section — shared retry defaults inherited by every consumer with field-level fallback (per-consumer explicit value > ConsumerDefaults > C# default); fully-specified legacy configurations bind byte-identically, no behavior change. RetryOptions properties now use nullable backing fields with internal Is*Set tracking so "omitted" is distinguishable from "set to the default value". Jitter now uses the thread-safe Random.Shared (a shared new Random() instance corrupts under concurrency). PollyRetryPolicyProvider renamed to RetryDelayProvider; GetDelayForRetry now delegates to RetryOptions.GetDelayForAttempt (single canonical backoff formula; jitter still applied after the MaxDelay cap). REMOVED (breaking for unused API surface): RetryContext class, both IRetryPolicyProvider.CreateRetryPolicy overloads, and the Polly package dependency — all dead code with zero callers. Two-layer retry model (transport x-retry-count vs task-level deterministic-id retries) documented at the code seams. v1.5.0 - Dead-letter drain pipeline (CH-D): type-erased raw consumer (RawMessageContext, IRawMessageHandler, IRawMessageConsumer, IConsumerFactory.CreateRawConsumer, internal RawRabbitMQConsumer + RawConsumerHostedService) that maps Reject/Defer onto Retry so a DLX-less DLQ message is never destroyed. New DeadLetter/ folder: DeadLetterDrainOptions (ships disabled), IDeadLetterStore + DapperDeadLetterStore (INSERT...WHERE NOT EXISTS on (Queue, MessageId) + Replayed-only retention sweep), DeadLetterRow, XDeathParser, DeadLetterDrainHandler, DeadLetterIterationCounter, and the AddDeadLetterDrain DI extension which FORCES OnRetryExhausted=Requeue on the drain consumer and THROWS at startup if the drain consumer declares a DeadLetter section. Wire format unchanged; backward-compatible with RabbitX 1.4.x consumers. v1.4.1 - Bugfix: DapperOutboxStore.DeleteSentBeforeAsync now passes the ambient transaction to Dapper. Microsoft.Data.SqlClient 7.0.1 (new in 1.4.0) is strict about commands inheriting the connection's pending transaction, so the retention sweep was throwing InvalidOperationException ("BeginExecuteNonQuery requires the command have a transaction..."). Log spam only — no data loss. v1.4.0 - Consumer Inbox primitives (CH-C): InboxOptions, IConsumerUnitOfWork, IInboxStore, DapperInboxStore, InboxIterationCounter, IdempotentConsumerPipeline<T> (generic IMessageHandler decorator) and the AddIdempotentConsumerPipeline + AddIdempotentMessageHandler<T,THandler> DI extensions. MessageEnvelope.CreateReply now derives MessageId deterministically via the new DeterministicMessageId.For(Guid, string) overload so consumer-side Inbox dedup catches retried replies. Wire format unchanged; backward-compatible with RabbitX 1.3.0 consumers.