Introducing Rebus 2

small-bus-logo-1I’ve been meaning to do this for a while, and while I am aware of the danger of doing it, I’ve done it anyways: I’ve rewritten Rebus!

Why would you do that?

Rebus was initially made to make me happy. I made it as an implementation of the parts of NServiceBus that I liked best in order to use it on a couple of projects, and thus I did not put too much effort into making a long-lasting design. Also, I aligned all APIs very closely to their NServiceBus counterparts, although I did loosen up on this as time went by.

In addition to this, Rebus was my first open-source project, and to this date, more than 20 besides me have contributed code to it, and many more are using it. It’s been fun to manage a project with actual contributors, and at times it’s been challenging to govern the project with regards to features, quality, ease-of-use, responsiveness, etc.

To sum it up: I have learned a lot since I started the Rebus project!

And usually, when you learn things, you want to put those things to use. At this point, I feel I’ve learned so much that I needed to put those things back into Rebus,

Why would you do it like that?

Rebus has – since its inception almost 5 years ago – done a good job of keeping message flowing and developers happy, so why would I rewrite it from scratch?

I’ve been meaning to improve a lot of things in Rebus for quite a while now, like reducing complexity, introduce message pipelines for incoming and outgoing messages, etc. I could probably have undertaken this as an incremental task, but my feeling is that the end result would not be nearly as clean as it will be with a rewrite.

Therefore, my conclusion was this: Rebus is small enough that a rewrite was the best way to refactor it (given the changes I wanted to make, obviously)!

Also, I did not actually throw old Rebus away. Rebus 2 is async to the core, and the OWIN-pipeliney design is heavily inspired by my (very brief) work with the NServiceBus team, but I kept everything aligned with old Rebus as far as it made sense, thus allowing me to port most of the integration implementations forward with very few modifications.

What has changed?
  • Rebus 2 is async to the core. All APIs are, in their most basic incarnation, async. Therefore, you await bus.Send(yourMessages) now. In other words, one single physical thread can do a lot of work, while consuming almost no memory, and almost no CPU.
  • Rebus has pipelines. Incoming and outgoing messages are passed through pipelines, which allows for extreme flexibility in how Rebus behaves. Several of Rebus’ “built-in” functionalities like e.g. compression and encryption are simply extra pipeline steps that are applied to incoming and outgoing messages.
  • Routing is based on topics. Topics based on .NET type names (which is how old Rebus worked) is thus just one case of a more general scheme where any string can be a topic.
  • There’s no batching. Batching (i.e. the fact that each and every transport message was actually a container of zero or more logical messages) provided almost no benefit, considering the amount of code it took to implement it. You can easily implement batching yourself in those few cases where you want it.
  • There’s an in-memory transport, an Amazon SQS transport, an Azure Storage Queues transport, a Jil serializer, and more cool stuff to come!
  • Idempotent sagas can be had by deriving your handler off of IdempotentSaga instead of Saga.
  • …and much more πŸ™‚
Show me the code!

Chances are that many Rebus users will not experience too many differences. Check this out – here’s how I start the usual MSMQ-enabled endpoint, capable of receiving requests and delivering replies:

and then, in order to send SomeMessage to the endpoint, do this:

Get started

Rebus 2 is initially released as Rebus 0.90.0, along with all of its integration packages. You can install-package rebus to get started right away.

How to upgrade

Because of the many changes, upgrading from Rebus 0.84.0 (the last “Rebus 1” version) to 0.90.0 (the first “Rebus 2 beta” version) may not be trivial.

Rebus 1 endpoints do not readily work with Rebus 2 endpoints, so you will need to upgrade all endpoints at the same time or manually bridge things while you upgrade.

Bugfixes might still be released for the 0.84.0 version of Rebus, but if you have the opportunity to pick a version, I recommend you go with 0.90.0.

As always, I’ll be very grateful to hear about your experiences with it – good or bad!

Happy messaging πŸ˜€

8 thoughts on “Introducing Rebus 2

  1. I love the sound of a fully async Rebus but what on earth possessed you to version it as if it was a continuation of the previous version? This should be Rebus 2.0.0-b1 so people can easily upgrade without too many issues.

    You’ve also changed the target framework to 4.5 without any warning, so that’s us out for now πŸ™

    1. Hello there Rebus User,

      I versioned it as 0.90.* because everything below 1.0.0, as far as I’m informed, is potentially allowed to contain breaking changes (according to Semantic Versioning). Also, “Rebus 2” IS actually just “Rebus” going forward, so I expect it to be the natural upgrade path anyway.

      The target framework was updated to .NET 4.5 (in order to support the initial IHandleMessagesAsync<>) in version 0.74.0 back in November 2014, so it’s actually been like that for a while.

      I’m sorry if this causes problems for you. Let me know if I can somehow help you get on board with Rebus 0.90.0 to future-proof your things πŸ™‚

  2. Good news. Its always a good idea to improve code while there is a chance. I forget this project isnt even V1 πŸ™‚ I put off my contributie. That could post and hanlde messeages through SQL code. For to get my git skills sharpenend and try a PR. Thanks for Rebus! In I introduced me to the service is world.

  3. I really shouldn’t post feedback on a mobile device with a Dutch autocorrect πŸ™‚ Sorry for the typos

  4. Hi Mogens,

    Dude, regarding the new MessageContext, if I want to had headers (metadata) to a message before publishing, should I just use:

    MessageContext.Current.Message.Headers.Add($”Aenima-{header.Key}”, header.Value.ToString())

    Before doing:

    bus.Publish(evt);

    Thanks πŸ™‚

    1. No, it’s even more simple πŸ™‚

      All methods that send messages have an optional Dictionary<string, string> argument which will become custom headers, so you should just do this: await bus.Publish(someEvent, new Dictionary<string, string>{{"custom-header", "custom-value"}});

  5. Ahahahaha Just saw the Rebus2 code!
    That’s just perfect for my cqrs framework πŸ™‚

    By the way, I like D60 Circus but there is stuff that I don’t agree with or maybe I just don’t understand.
    Mine is more of a pluggable thing. It NEEDS implementations to do stuff.
    It still in it’s infancy…

    Thanks dude!

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.