rebus-org / Rebus.RabbitMq

:bus: RabbitMQ transport for Rebus
https://mookid.dk/category/rebus
Other
65 stars 45 forks source link

Multiple queues per Consumer #38

Closed ghhat closed 5 years ago

ghhat commented 5 years ago

Hello,

im wondering why its not possible to create an IBus with a transport thats supports message for more than one queue with the follwoing setup.

At first whats working with one IBus is sendign to different queues:

// serving four queues with one IBus/transport
// note: simpleinjector adapter is used
     configurer => configurer
                   .Transport(t => t.UseRabbitMqAsOneWayClient(rabbitPath))
                   .Routing(r => r.TypeBased()
                      .Map<TypeA>("queueA")
                      .Map<TypeB>("queueB")
                      .Map<TypeC>("queueC")
                      .Map<TypeD>("queueD")
                )
                .Start()
    );

All runs well; every queue is populable.

Now I need a service thats receives the messages I cannot configure one IBus/Transport for the four queues queueA, queueB, queueD and queueD because only one queue-name is possible while configering the transport.

// can only configure IBus/transport for one queue :-|
container.ConfigureRebus(
     configurer =>
         configurer
             .Transport(t => t.UseRabbitMq(rabbitPath, "queueA"))
             .Routing(r => r.TypeBased().Map<TypeA>(""queueA")) 

             .Start()
    );

Is there any way to achieve to have one IBus/transport who is getting messages from all four queues? No would mean to maintain a single DI-container instance per queue, which would be very cumbersome. Especially since we have to serve more virtual routes where every virtual route has the same queue-setup... It is understandable that one bus is required per virtual host. But also per queue?
Thanks for any hint on this!

mookid8000 commented 5 years ago

Rebus supports receiving from a single queue, because that's enough 😄 this design decision is so fundamental that explaining it would require me to write several pages of text here, so instead I'll ask: What is it you want to do with those queues?

I.e. why do you want to send TypeA messages to the "queueA" queue, TypeB messages to "queueB", etc.?

If they're commands of some kind (i.e. the messages represent work to be carried out), I would say that you should simply put them all into the same queue.

If they're events of some kind, you should not even bother to map them to anything, as the endpoint mappings are ignored with events. "Events" are messages that represent stuff that has happened, and you publish those by doing this:

await bus.Publish(new TypeA(..));

And then, anyone other Rebus endpoint that wants to listen to these events can then

await bus.Subscribe<TypeA>();

at startup (which will create a persistent RabbitMQ binding for that particular message type), and then all events of that type will be received by that endpoint until it unsubscribes again (which it might never do).

I'm closing this issue now (so it doesn't look like an actual issue), but please continue by replying in here 😄

ghhat commented 5 years ago

Thank you for your reply.

Why four queues? We receive four distinct types of input files (text files, less than 1kb content). These files are each deserialized and converted into a specific type and send to a queue. In the planning of our application it seemed reasonable to us to use a queue for each file type in order to be able to deal with each type more finely granularly. For example, to see immediately which file type runs up and make problems.

Currently we have set it up so that there is only one queue that accepts all four message types. And we user bus.Send(...), because its not en event.

The real problem is the handling in combination with a DI container and the impossibility to register the IBus interface more than once (which is of course obvious). We really do not care if there are several IBus instances (one singleton per queue). But one would also have to create four DI containers. The whole thing is made worse by the fact that we want to map different tenants via virtual hosts on the RabbitMQ. That would mean number of DI containers = 4 queues multiplied with number of tenants. This is at least necessary if you want to get the dependencies into the message-handler (IHandleMessage<>) via constructor injection. Theoretically it would be possible to get the dependencies via a static service locator within message-handler, but that doesn't make one happy.

Because we are also quite new thematically, we just wanted to ask if there is really no other way. If that is the case (because of by design), then it is the case. We just have to come up with something for DI containers. Thank you.