rebus-org / Rebus

:bus: Simple and lean service bus implementation for .NET
https://mookid.dk/category/rebus
Other
2.31k stars 359 forks source link

[Question] What does IBus.Start really affect? #1176

Closed srogovtsev closed 1 week ago

srogovtsev commented 2 weeks ago

Hello,

we're working at integrating Rebus in our system, which has somewhat complicated lifetime and whatnot, and so I've been digging into the implementation details for the Rebus IBus, and I have a couple of questions about when some things should occur.

  1. What can and cannot be done before IBus.Start is called? From my experiments it seems that Start affects only the receiving of the messages, and at least some transports will happily send out messages before it is called.
  2. That being said, what is the proper time for subscribing to events (i.e., calling IBus.Subscribe)? To clarify, we only need subscription when the app is up, so we're calling Unsubscribe at bus disposal. The examples in Rebus.ServiceProvider seem to recommend placing the subscription calls in onCreated, which to me implies having them after bus creation but before it is started - do I miss something here?
mookid8000 commented 1 week ago

"Starting" the bus is, as you've correctly observed, a matter of cranking up the number of workers (i.e. Rebus' message receivers) from 0 to whichever number of workers you're running with (1 by default*).

Back when configuring Rebus like this:

Configure.WIth(containerAdapter)
    .Transport(...)
    .(...)

was the norm, the configuration could be either finished by calling .Start()

Configure.WIth(containerAdapter)
    .Transport(...)
    .Start();

to create and return a started IBus immediately, or starting it could be postponed until later by calling `.Create()´ like this:

var starter = Configure.WIth(containerAdapter)
    .Transport(...)
    .Create();

// do stuff

// after a while: start it
starter.Start();

Now that most apps are happy with using Micrsoft's generic host and the Rebus.ServiceProvider package, Rebus provides a modern, idiomatic configuration API that enables Rebus usage for most scenarios to be as simple as

// your own configuration extensions here
services.AddThis();
services.AddThat();

// Rebus-related configurtion extensions from Rebus.ServiceProvider
services.AddRebusHandler<YourHandler>();
services.AddRebus(
    configure => configure
        .Transport(...)
);

which makes the starting time a little more opaque. It's still how stuff works underneath the covers though, and the onCreated callback is provided to enable "doing stuff with the bus" before it starts receiving messages.

So...

TL;DR: The onCreated callback is the right place to establish subscriptions. Rebus ensures that it's executed at the right time.

If you need to unsubscribe when shutting down, you're basically on your own. There's no way Rebus can help guarantee that subscriptions are in fact removed again (at least not in the general case), but I am sure you know what you're doing. 🙂

I hope that answers your questions🙂


(*) please note that Rebus defaults to 1 worker but a PARALLELISM of 5, i.e. up to 5 messages can be processed in parallel by default

srogovtsev commented 1 week ago

Thank you for your answer, but unfortunately I still have a few questions.

var starter = Configure.WIth(containerAdapter)
    .Transport(...)
    .Create();

// do stuff

// after a while: start it
starter.Start();

This is re 1, i.e. "what can and cannot be done": I'm observing that Rebus is perfectly happy if I send messages after the bus is created, but before it is started, i.e, in the //do stuff above. Is that a valid and expected behavior, or should we avoid it by all means?

TL;DR: The onCreated callback is the right place to establish subscriptions. Rebus ensures that it's executed at the right time.

...and just to verify my understanding, if we're not using Rebus.ServiceProvider, the proper time would be at the same //do stuff line above, which is where onCreated is called, is that right?

mookid8000 commented 1 week ago

This is re 1, i.e. "what can and cannot be done": I'm observing that Rebus is perfectly happy if I send messages after the bus is created, but before it is started, i.e, in the //do stuff above. Is that a valid and expected behavior, or should we avoid it by all means?

Sorry for not being entirely clear about that. This is valid and expected behavior, and it is guaranteed to work like that across all transports and whatever additional technology choices you've made. 🙂

...and just to verify my understanding, if we're not using Rebus.ServiceProvider, the proper time would be at the same //do stuff line above, which is where onCreated is called, is that right?

Yes 👍