Your first message bus works beautifully in development. Then a consumer throws on a malformed payload at 2 a.m., the message disappears, and nobody can tell you whether it was ever delivered. That is the moment teams discover the difference between a library that sends messages and a framework that owns delivery guarantees. In .NET, four libraries occupy that space — and they disagree fundamentally about how much they should do for you.
MassTransit (7,812 stars), Wolverine (2,398), Rebus (2,675), and NServiceBus (2,169) all publish messages over RabbitMQ, Azure Service Bus, Amazon SQS, or Kafka. All four support retries, sagas, and an outbox. The differences show up in the operational details: how endpoints are configured, what happens to a poison message, how much ceremony a new developer must learn, and what you pay — in licensing and in cognitive load — once the system has forty consumers instead of four.
TL;DR — The 30-Second Verdict
Pick Rebus if you want the smallest possible surface area and full control. It is a lean service bus: one AddRebus call, handlers by convention, no opinionated application framework. Best for teams of 1-5 services who already understand their broker.
Pick MassTransit if you want the largest ecosystem and the most documented patterns. Its consumer/endpoint/saga model is the de facto standard in .NET, and the documentation covers almost every topology you will encounter. Heaviest abstraction layer of the four.
Pick Wolverine if you want MassTransit-style capability with a convention-over-configuration core — and you like the idea of your message handlers doubling as HTTP endpoints and scheduled jobs. Newest of the four, fastest-moving, smallest amount of boilerplate.
Pick NServiceBus if you are building a long-lived, large-scale system and want a vendor behind it. You get the most rigorous saga and persistence tooling, plus commercial support — and you pay for a license once you outgrow the free tier.
Side-by-Side Comparison
| Dimension | MassTransit | Wolverine | Rebus | NServiceBus |
|---|---|---|---|---|
| GitHub stars | 7,812 | 2,398 | 2,675 | 2,169 |
| License | Apache 2.0 | MIT | MIT | Commercial (free tier) |
| Last commit | 2026-10-21 | 2026-10-30 | 2026-09-29 | 2026-10-30 |
| Primary abstraction | IBus + consumers | Handlers + cascading messages | Handlers + IBus | IEndpointInstance + handlers |
| Transports | RabbitMQ, Azure SB, SQS, Kafka, SQL, in-memory | RabbitMQ, Azure SB, SQS, Kafka, PostgreSQL, SQL Server, local queues | RabbitMQ, Azure SB, SQS, Kafka, SQL, file system | RabbitMQ, Azure SB, SQS, MSMQ |
| Built-in outbox | Yes (EF Core, MongoDB, SQL) | Yes (durable inbox/outbox) | Yes (via Rebus.TransactionScopes/outbox package) | Yes (SQL, NHibernate, EF) |
| Saga support | State machine + orchestration | Yes, with persistence | Yes (Saga base class) | Yes (most mature) |
| Boilerplate per consumer | Medium | Low | Low | High |
| Learning curve | Medium | Low-medium | Low | High |
| Best fit | Large .NET codebases | Greenfield services | Small/medium fleets | Regulated enterprise |
Decision Matrix: Match the Bus to Your Situation
| Your situation | Recommended | Why |
|---|---|---|
| 3 services, one team, RabbitMQ already running | Rebus | One registration call, no framework lock-in, trivial to reason about |
| 15+ consumers, sagas, existing ASP.NET Core app | MassTransit | Mature endpoint configuration, huge pattern library, easy hiring |
| Greenfield modular monolith that may split later | Wolverine | Handlers work in-process today and over a broker tomorrow without rewrites |
| Regulated workload needing vendor support and audits | NServiceBus | Commercial backing, strict saga persistence, supported upgrade path |
| You need to move 50k messages/min on a budget | Wolverine or MassTransit | Both batch and prefetch aggressively; Rebus needs more manual tuning |
| Team has zero messaging experience | Rebus, then grow | The smallest concept count — bus, handler, transport |
MassTransit — The Ecosystem Default
MassTransit’s model is explicit: you register consumers, then bind them to receive endpoints. That explicitness is why it scales to large systems — every queue in your broker maps to a line you can point at in code.
| |
The consumer itself is a class with a typed Consume method:
| |
Where it wins: endpoint-level configuration is a first-class concept. Retry policies, prefetch, concurrency limits, and dead-letter behaviour are all per-queue. The state machine saga (MassTransitStateMachine<T>) is expressive enough that most teams never hand-write a saga.
Where it costs you: the abstraction stack is deep. When a message vanishes, you debug through IBus, IBusControl, receive endpoints, and the broker — four layers. New developers also need to internalize the difference between Send (queue-targeted) and Publish (fan-out), which is the single most common source of “why did only one service get it?” bugs.
Wolverine — Conventions Over Ceremony
Wolverine starts from a different premise: a message handler is just a method, and the framework infers everything else. The same handler can be invoked in-process, over RabbitMQ, or as an HTTP endpoint.
| |
That return value is the interesting part: Wolverine treats returned objects as cascading messages and routes them automatically. No publisher injection, no explicit Send call. It also generates the routing for you — AutoProvision() creates queues and bindings on startup, which removes an entire class of “the queue did not exist yet” incidents.
Where it wins: the lowest concept count of any full-featured bus. AutoApplyTransactions() and the durable inbox/outbox are opt-in one-liners rather than multi-part configuration. Startup provisioning means a fresh environment converges without manual broker setup.
Where it costs you: conventions are invisible. When routing goes wrong, the fix is often understanding an inferred rule rather than editing an explicit binding. The ecosystem is smaller (2,398 stars vs MassTransit’s 7,812), so fewer Stack Overflow answers exist for edge cases.
Rebus — The Lean Service Bus
Rebus does the minimum a service bus must do, and does it predictably. One registration, explicit routing, handlers found by interface.
| |
Where it wins: you can read the entire configuration of a service in twenty lines. Routing is explicit — TypeBased() mapping tells you exactly which queue receives which message, which makes production debugging dramatically faster than tracing inferred conventions. Rebus also has unusually good support for non-broker transports (SQL Server, file system), useful for air-gapped environments.
Where it costs you: fewer batteries. Retry, outbox, and saga support exist but require installing and wiring separate packages, and there is no state-machine DSL — sagas are hand-written classes. For a two-service system that is a feature; for forty consumers it becomes work you repeat.
NServiceBus — The Enterprise Option
NServiceBus is the oldest and most opinionated of the four. It assumes you want a message-driven architecture with strict persistence, and it enforces that shape.
| |
Note QueueType.Quorum — NServiceBus defaults to durable, replicated quorum queues rather than classic queues. That single default tells you who the target customer is: teams that cannot afford to lose a message and will pay to avoid designing that guarantee themselves.
Where it wins: sagas, persistence, and upgrade tooling are the most rigorous here. EnableInstallers() provisions queues, tables, and subscriptions at startup. Commercial support means an escalation path when a broker-level bug eats your weekend.
Where it costs you: licensing. It is free for development and small deployments but requires a paid license at scale — a real budget line that the other three do not have. The API is also the most ceremonious: an endpoint is an object you start and stop yourself, which is awkward inside the generic host if you are used to AddMassTransit.
The Broker Underneath: Docker Compose You Can Actually Run
All four buses are only as reliable as the broker. This compose file gives you RabbitMQ 4.1 with the management UI and a healthcheck — enough to reproduce the retry and dead-letter behaviour described above:
| |
Then verify a queue is actually consuming:
| |
Pitfalls That Bite Every Team Once
Shared contract assemblies are a trap at scale. All four buses encourage putting message types in a shared NuGet package. That works until two services need different versions of the same event. Prefer additive contracts — add properties, never rename or remove — and treat the contract package as a public API with its own versioning.
Prefetch is not a performance dial, it is a fairness dial. Setting PrefetchCount to 500 makes one consumer hoover up the queue; the other three sit idle. Start at 16-32 and raise it only after measuring handler latency.
None of these give you exactly-once delivery. They give you at-least-once plus an outbox. If a handler is not idempotent, a retry will double-charge a customer. Every consumer needs an idempotency key (usually the message ID) written in the same transaction as its side effect.
Poison messages must have an owner. Configure a dead-letter or error queue explicitly and alert on its depth. A silently growing orders_error queue is the most common failure in .NET messaging, and it fails quietly.
Do not do work in Task.Run inside a consumer. The bus will consider the message handled when the method returns. Background work gets lost on scale-in, and retries will not help because the message is already acked.
Quorum queues cost disk. Replicated queues triple storage and add latency. Use them for financial or state-transition messages, and classic queues for high-volume telemetry where losing a message is acceptable.
FAQ
Which .NET message bus is fastest to get running?
Rebus. A single AddRebus call with one transport and one routing rule gives you a working producer and consumer. MassTransit needs consumers plus endpoint configuration; Wolverine needs a host policy block; NServiceBus needs an endpoint object you start and stop.
Can I use these without RabbitMQ?
Yes, all four abstract the transport. MassTransit supports Azure Service Bus, Amazon SQS, Kafka, SQL, and in-memory. Wolverine adds PostgreSQL and SQL Server queues plus durable local queues. Rebus supports SQL Server, Azure Service Bus, Kafka, and a file-system transport. NServiceBus supports RabbitMQ, Azure Service Bus, SQS, and MSMQ. In-memory transports are for tests only — they hide ordering and retry behaviour.
Do I need sagas, or is a database transaction enough?
If a business process spans more than one service and can pause (waiting for payment, for stock, for a human), you need a saga or process manager. MassTransit’s state machine and NServiceBus’s saga persistence are the most capable. Rebus and Wolverine support sagas too, with less tooling around them. If everything fits in one transaction, skip sagas entirely — they are operational overhead.
How do I migrate from MassTransit to Wolverine?
Both use typed message classes and DI-injected handlers, so the migration is mechanical: replace IConsumer<T> classes with handler methods, replace AddMassTransit with UseWolverine, and convert ReceiveEndpoint configuration into ListenToRabbitQueue calls. Run both buses in parallel against separate queues during the cutover so neither side loses messages.
Which one handles 50,000 messages per minute?
Wolverine and MassTransit both reach that comfortably with batching and prefetch tuning on RabbitMQ, provided handlers do not block on synchronous I/O. Rebus can too, but expect to tune SetNumberOfWorkers and SetMaxParallelism manually. At that volume the broker, not the library, is usually the bottleneck — measure queue depth before rewriting anything.
Is the free tier of NServiceBus enough for production?
For small deployments it can be, but the license terms escalate with endpoints and environments, so budget for it before you commit. If a license is not in the budget, MassTransit (Apache 2.0), Wolverine (MIT), and Rebus (MIT) impose no such constraint.
For the surrounding architecture, see our ASP.NET Core vs ServiceStack vs Carter web framework comparison, the .NET dependency injection container shootout, and our Hangfire vs Quartz.NET vs Coravel job scheduling guide — scheduled jobs and message handlers solve overlapping problems and are often deployed together.
💰 想测试你的市场判断力?我用 Polymarket 做预测市场交易——这是全球最大的预测市场平台,从大选结果到技术监管时间线,什么都可以押注。和赌博不同,这是真正的信息市场:你懂的信息越多,胜率越高。我靠预测技术相关事件的走向已经赚了不少。用我的邀请链接注册:Polymarket.com