rebus-org / Rebus.RabbitMq

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

Possibility to send a message directly on an alternative exchange #33

Closed hansehe closed 5 years ago

hansehe commented 5 years ago

Is it possible to extend the Rebus library to allow for sending messages on alternative exchanges, other then the default exchanges?

Example: Bus.Advanced.Routing.Send("alternativeExchange", "queue", message) Bus.Advanced.Topics("alternativeExchange", "topic", message)

Another alternative would be to use existing interface, but allow for setting which exchange to use with a simple routing topologi in the topic or destination address:

Example: Bus.Advanced.Routing.Send("queue@exchange", message) Bus.Advanced.Topics("topic@exchange", message)

Maybe it could be possible to use this topology for overriding which exchange to bind the topic?

Example: Bus.Advanced.Topics.Subscribe("topic@exchange")

hansehe commented 5 years ago

It would be possible to allow for alternative exchanges if the GetSubscriberAddresses() could be changed to the following:

public async Task<string[]> GetSubscriberAddresses(string topic) { if (topic.Contains("@")) { return new[] {topic}; } return new[] { $"{topic}@{_topicExchangeName}" }; }

Simple, but important

mookid8000 commented 5 years ago

Looking at your PR, and thinking some more about this particular issue, I have these thoughts:

  1. Since queue names are global, and await bus.Advanced.Routing.Send("my-queue", message) is expected to always send to a queue named "my-queue", it doesn't make sense to have separate exchanged of type=DIRECT – there should be one single exchange of type DIRECT, and it should have exactly one binding per queue that binds to routing keys with the same names
  2. Only topics can exist within exchanges, and thus it makes sense to qualify them with an exchange name
  3. Rebus should simply be configured with a default topic exchange name, and then it should subscribe/publish with that exchange, unless the topic has been qualified with an exchange name by using the '@' syntax
  4. Publishing to multiple exchanges should not be support by adding multiple '@' characters in the topic, because Rebus doesn't work like that in any other place. If this behavior is desired, it would be fairly easy to add it in some other way (e.g. by decorating ISubscriptionStorage, being called just before RabbitMqTransport's GetSubscriberAddresses method)
mookid8000 commented 5 years ago

Regarding (1): Although, one might want to customize the exchange name, because of – who knows? – to avoid conflicts with existing stuff? Taste, even?

The current behavior (which I actually changed slightly in Rebus.RabbitMq 5.0.0-b06) is to treat

await bus.Advanced.Routing.Send("my-queue@random-exchange", someMessage);

as a genuine wish to send the message to the queue my-queue, so the transport will check if the queue "my-queue" exists (with QueueDeclarePassive), and then check if the exchange "random-exchange" exists (with ExchangeDeclarePassive), and then lastly bind the topic "my-queue" to the queue "my-queue" (i.e. actually BIND the topic – it's not possible to check the existence of a binding with the C# driver).... this way, a sent message will not be lost if either queue, exchange, or binding has not been created beforehand.

This is done once for each queue/exchange combination that the bus instance sends to in its lifetime, so the time it takes is negligible.

mookid8000 commented 5 years ago

Regarding (2): The reason it makes sense, is it makes it possible for exchanges to work as namespaces thus making is possible e.g. for different applications to do pub/sub without interfering with eachothers' events.

One could imagine something like

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

happening in many types of otherwise unrelated applications, so I guess that's a good feature. One could even

await bus.Publish("an ordinary string!");

so supporting pub/sub with a global message broker could be messy, if exchanges could not be used to implement a scope for subscriptions.

QUEUE NAMES are global though, so if a broker instance is used for multiple applications, great care should be taken to ensure that queue names are unique across the entire world.

mookid8000 commented 5 years ago

I think (2), (3), and (4) can be implemented simply by using the FullyQualifiedRoutingKey class whenever a queue name/topic is to be handled, passing the default exchange name when the queue name/topic has not been exchange-qualified.

So... would you like to maybe make the adjustments to your PR?

hansehe commented 5 years ago

Point 1 is already solved, as your saying, so yes please, I'll do the update to fulfill point 2, 3 and 4.

If I understand your opinion, I'll just simplify the code allowing for publishing to only one alternate exchange at a time.

And I gotta say, Rebus is great open source library!

hansehe commented 5 years ago

Please have a look at the update to my PR. Simplified the code so that it is only possible to publish to one exchange at a time.

mookid8000 commented 5 years ago

Fixed by #36 , which is out in Rebus.RabbitMq 5.0.0-b08 now 👍