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?

Blue
  • 22,608
  • 7
  • 62
  • 92
  • What is the problem you're trying to solve? ASB clients operate as competing consumers​. – Sean Feldman Mar 26 '17 at 02:34
  • @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? – Blue Mar 26 '17 at 11:03

2 Answers2

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)
Bishoy
  • 3,915
  • 29
  • 37
  • 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? – Blue Mar 27 '17 at 01:28
  • 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. – Blue Mar 27 '17 at 01:38
  • I've edited the answer to include a solution based on topics. – Bishoy Mar 27 '17 at 02:04
  • 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. – Sean Feldman Mar 27 '17 at 03:32
  • How do I reuse the subscription across multiple machines? (The instances)? – Blue Mar 27 '17 at 09:05
  • 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. – Bishoy Mar 27 '17 at 10:03
  • Thanks for the help @Bishoy. Creating multiple subscriptions on each topic, worked like a charm <3 – Blue Mar 27 '17 at 15:48
  • @Bishoy 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](https://softwareengineering.stackexchange.com/questions/347086/actor-pattern-in-azure-service-fabric) 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. – Blue Apr 19 '17 at 11:39
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.

Sean Feldman
  • 23,443
  • 7
  • 55
  • 80
  • 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? – Blue Mar 27 '17 at 09:04
  • 1
    This is a very obvious queuing UC, Sean. There's absolutely no need to put any further abstraction on top – Clemens Vasters Mar 27 '17 at 09:06
  • 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. – Sean Feldman Mar 27 '17 at 15:47