Azure Service Bus - Round Robin Topic to Multiple Services
Asked Answered
E

2

9

Here's the scenario:

Publisher #1 ═══╗             ╔═══ Round Robin ═══╦═══ Subscriber #1 (Service 1)
                ║             ║                   ╚═══ Subscriber #2 (Service 1)
                ╠═══ Topic ═══╣
                ║             ║                   ╔═══ Subscriber #3 (Service 2)
Publisher #2 ═══╝             ╚═══ Round Robin ═══╩═══ Subscriber #4 (Service 2)

I have one message that needs to be processed by multiple subscribers, BUT only one per service (There will be multiple instances running for each service).

Message #1, needs to be processed by Subscribers #1 and #3. Message #2, needs to be processed by Subscribers #2 and #4. Message #3, subscribers #1 and #3 again. Basically, each message should round robin to each of the load balanced services that are subscribing to each message, grouped by each service that is connecting. Is this possible without creating multiple topics?

Even if it's not round robin per-say, I'd like to use best effort to load balance across multiple services. Is this possible?

Expropriate answered 26/3, 2017 at 0:33 Comment(2)
What is the problem you're trying to solve? ASB clients operate as competing consumers​.Sepulchral
@SeanFeldman The problem I'm trying to solve, is I only want 1 consumer in each service consuming messages on a single topic. Is this possible with ASB?Expropriate
O
8

1. Topics

Topics are a publishing/distribution mechanism that will send the message once per subscription (subscriber).

A topic subscription resembles a virtual queue that receives copies of the messages that are sent to the topic. Messages are received from a subscription identically to the way they are received from a queue...

Subscriptions support the same patterns described earlier in this section with regard to queues: competing consumer, temporal decoupling, load leveling, and load balancing.

Source: MSDN Article

You need to re-use the topic subscription among competing consumers (service instances) to achieve your scenario.

Publisher #1 ═══╗             ╔═══ Subscription 1 ═══╦═══ Service 1-instance 1
                ║             ║                      ╚═══ Service 1-instance 2
                ╠═══ Topic ═══╣
                ║             ║                      ╔═══ Service 2-instance 1
Publisher #2 ═══╝             ╚═══ Subscription 2 ═══╩═══ Service 2-instance 2

A. Creating a Topic subscription

string connectionString = "<Secret>"
var namespaceManager =
    NamespaceManager.CreateFromConnectionString(connectionString);

if (!namespaceManager.SubscriptionExists("TestTopic", "Inventory"))
{
    namespaceManager.CreateSubscription("TestTopic", "Inventory");
}

B. Listen to an existing subscription

MessagingFactory factory = MessagingFactory.Create(uri, tokenProvider);

MessageReceiver receiver = factory.CreateMessageReceiver("TestTopic/subscriptions/Inventory");

2. Queues

Using multiple Queues could also fit your specific scenario. Each Queue having multiple competing consumers (instances) will deliver the message only once to the first client that request and process it successfully.

The design then becomes:

Publisher #1 ═══╗         ╔═══ Service 1 Queue ═══╦═══ Subscriber #1 (Service 1)
                ║         ║                       ╚═══ Subscriber #2 (Service 1)
                ╠═══ASB═══╣
                ║         ║                       ╔═══ Subscriber #3 (Service 2)
Publisher #2 ═══╝         ╚═══ Service 2 Queue ═══╩═══ Subscriber #4 (Service 2)
Olwen answered 26/3, 2017 at 22:29 Comment(8)
I'm trying to move my monolith to a bunch of microservices. What I'd like to avoid is multiple queues that need to process messages, as now if I add more microservices that need to listen for publisher messages, I need to create another queue and send messages to. This doesn't seem like the best approach to take. I have a message service, which is publishing messages from irc. I have a spam service (Ban spambots), trivia service (To check for answers if one is running), command service (To reply to !messages), and a few more. Is this the approach you would take?Expropriate
Right now, I'm trying to implement this on Service Fabric, but I'm trying to do this in a way where I don't have to call out each service I want to pass this message to. I'd like the subscribers be the ones who will say "hey, I want to see messages and process them". If you can help me with a solution, I'll be happy to add a bounty to this question, and toss you some extra rep for the help.Expropriate
I've edited the answer to include a solution based on topics.Olwen
Queues don't process messages, consumers of those queue do. If you want to have microservices, I'd expect to see resources not shared by those. Think of queues as "mailboxes". If you have a building with apartments, each apartment has an inbox. They don't share a single inbox. What you might be looking is a an abstraction on top of your messaging service of choice.Sepulchral
How do I reuse the subscription across multiple machines? (The instances)?Expropriate
Answer edited to add sample code for listening to an existing topic subscription from competing consumers. Please keep in mind I have not tested the code myself and is only an example.Olwen
Thanks for the help @Bishoy. Creating multiple subscriptions on each topic, worked like a charm <3Expropriate
@Olwen If you're interested, I'm having some trouble deciding on the route to finalize my part of the application. There's currently a 100 bounty reward here to help me decide on the path I should take for azure service fabric. Once I get down the basics, and get an app going, I'm looking to build a lot more on fabric. Just hoping to figure out these roadblocks I'm having.Expropriate
S
1

I'd like to use best effort to load balance across multiple services. Is this possible?

From your description, it seems you're trying to load balance between multiple instances of any given service, not a a single service. You get that out of the box with competing consumer pattern that ASB supports. Not really something you need to work for.

The problem I'm trying to solve, is I only want 1 consumer in each service consuming messages on a single topic. Is this possible with ASB?

Yes, that is possible. If you have a shared topic with subscription per service (not instance) and containing a rule that always evaluates to true, SqlFilter (1 = 1). Then each subscriber will get a copy of that message. In essence you'll be broadcasting the message to all of your services. Thanks to competing consumer, only one instance of each service will get that message.

To target a specific service, you'd create an additional rule that would filter out message on a property (a standard property or a custom one). For example, it could be a service name.

If you don't want to focus on the the middleware for your microservices, you can take a peek at frameworks that do it for you. Such frameworks usually tend to provide additional features as well. Have a look at NServiceBus or MassTransit for example.

Full disclaimer - I'm working on NServiceBus and its ASB transport.

Sepulchral answered 27/3, 2017 at 3:46 Comment(3)
At no point will I need to target specific instaces. The idea is, each service is redundant, so as long as one instance is getting the messages, then I'm happy. You mention in the second half of your answer "If you have a shared topic with subscription per service (Not instance)"...how would I accomplish that? If each service is subscribing once, then wouldn't it mean only one instance would be getting the message?Expropriate
This is a very obvious queuing UC, Sean. There's absolutely no need to put any further abstraction on topZahara
Service is a logical concept at this point because subscriptions are used by the instances. Assuming you have a service X that runs with 2 instances. Each instance is going to create a subscription client to listen to the messages designated for the service X. Both instances will be competing over the messages on that subscription. In your original question, "Round Robin" on the diagram is the subscription entity.Sepulchral

© 2022 - 2024 — McMap. All rights reserved.