Trying Out Azure Service Bus Relay Load Balancing

As you might have noticed from previous posts, I am somewhat interested in the Azure Service Bus. Why? Well, why not? To be honest, I don’t actually know exactly why, but I feel that it offers something very flexible out of the box, and without to much hassle.

One of the later feature that was added to it is the support for load balancing when using message relaying. (You can read more about message relaying here)

It is pretty cool, and just works… And by just works, I mean it really just works. If you have a service using message relaying today, adding another instance will automatically enable the feature. But remember, the messages are delivered to ONE of the services, not both. So if your service cannot handle that, make sure you change the implementation to make sure that only one instance is running at any time.

In previous versions of the bus, adding a second service would throw an exception, which is obviously not the case. So if you were depending on this to make sure only one instance was ever running, you will have to revisit that code and make some changes…

I have decided to crate a tiny sample to show off the feature… So let’s have a look!

I started by creating 3 projects, two console applications, and one class library. I think this would be the most common way to do it. Not using 2 console apps (!), but having a server project, a client project and a class library with the service contract.

Let’s start with the contract. I have decided to create one of those contracts that really shouldn’t ever exist, but still works for demos. It is called INullService and has a single method called DoNothing(), which takes no parameters, and returns void. Like this

[ServiceContract(Name="NullService", Namespace="https://chris.59north.com/azure/relaydemo")]
public interface INullService
{
[OperationContract(IsOneWay = true)]
void DoNothing();
}

public interface INullServiceChannel : INullService, IChannel { }

As you can see, the service contract is defined as an interface, which is the way it should be. Do not adorn classes with ServiceContractAttribute…

I have also marked the method as being one-way and added an extra interface extending the INullService interface by implementing IChannel as well… Just as I have done in previous demos…

Now that the contract is done, I will move to the server project. I start off by adding references to the class library project, Microsoft.ServiceBus and System.ServiceModel.

Actually I only add a reference to the ServiceBus assembly as it is the only one only being used from config, but if you don’t have ReSharper, it is easier to do it up front. With ReSharper, you can just type the name of the type you need, press Alt-Enter and select “Reference assembly…”. (Did I mention that I have started to like ReSharper a LOT? Smile

Once the references have been added, I add a new class called NullService, which implements INullService in the most simple way possible

internal class NullService : INullService
{
public void DoNothing()
{
Console.WriteLine("Doing nothing...");
}
}

That’s all there is to the actual service. Next up is the Main method, inside which I start off by creating a new ServiceHost instance. I do this passing in a Type referring to the NullService class I just created, and getting the rest of the information needed from the app.config file by automagic. I then open the host, write a message to the screen and wait for a key to be pressed. Once the key is pressed, I close the host, and let the program close…

static void Main(string[] args)
{
var host = new ServiceHost(typeof(NullService));

host.Open();

Console.WriteLine("Service listening at: " + host.Description.Endpoints.First().Address);
Console.WriteLine("Press any key to exit...");
Console.ReadKey();

host.Close();
}

Once again, very simple! Unfortunately, there is as mentioned a bit of config to go with it. The config is however very standard, so I won’t cover it here, but it is available in the download at the bottom of the page…

Ok, server done, time to look at the client!

The client is a bit more complicated. I started out by creating a very simple client, unfortunately, it kept calling the same service instance all the time, which doesn’t really show off the load balancing very well. To get around this, I made it a bit more complicated by making sure that all the calls were being made in parallel using Tasks.

Let’s go through it one step at the time, once again leaving out the config…

The Main method just makes a single call to a method called ExecuteCallBatch(). The method is named like this because each time the method is run, it will call the service 50 times. Remember, we need a least a little load for this to work…

The ExecuteCallBatch() method starts by asking the user if he/she wants to use the same channel for all calls (and also if he/she wants to quit the application).

Console.Write("Do you want to keep the channel? (y/n/c) ");

var key = Console.ReadKey().Key;

if (key == ConsoleKey.C)
return;

var keepChannel = Console.ReadKey().Key == ConsoleKey.Y;

Ok, now we know that the users wants to go on, and whether or not he/she wants to keep the channel alive for all calls.

Next I create a couple of variables, one Task variable, and one INullServiceChannel, and if the channel should be kept alive, I set the channel variable to a channel.

var channel = keepChannel ? GetNewChannel() : null;
Task t = null;

The GetNewChannel() method really just instantiates a new channel using a ChannelFactory<> and opens it

private static INullServiceChannel GetNewChannel()
{
var channel = new ChannelFactory<INullServiceChannel>("RelayEndpoint").CreateChannel();
channel.Open();
return channel;
}

Now that I have the variables I need, it is time to create the loop that calls the service. All it does is that it creates a new Task, passing in an anonymous method, and starts it. The anonymous method is responsible for calling the service. It also checks whether or not the channel should be re-used. If not, it creates its own channel, which it also closes when done.

for (int i = 0; i < 50; i++)
{
t = new Task(() =>
{
var c = channel;
if (!keepChannel)
c = GetNewChannel();

c.DoNothing();

if (!keepChannel)
c.Close();
});
t.Start();
}

The reason that I keep a reference to the Task, is that at the end of the loop, I use that reference to wait for the last Task to complete before carrying on.

The way I do it in this sample isn’t the best idea, as it assumes that the last Task to be created, will also be the last one to finish. This isn’t necessarily true, but for this demo it is an assumption I can live with. The app might crash if I am unlucky, but it is unlikely enough for me to ignore…

Once the last Task has finished, I close the channel if it has been kept throughout the call batch, and then finally call ExecuteCallBatch() again, causing a loop that can only be stopped by pressing “c” at he prompt.

t.Wait();

if (keepChannel)
channel.Close();

ExecuteCallBatch();

Ok, that is all there is to it. To try it out, I start 2 or 3 instances of the server application, and let them all connect and get up and running before I start the client. I then let the client run a couple of batches, looking at the nice little printouts I get in all of the server windows, proving the that the calls are being nicely load balanced…

If you run through the demo a couple of times, varying between keeping the same channel and not, you will see that the result is the same. The messages are being load balanced in both scenarios, which I assume means that utilizing sessions in the services might cause some problems. But I still haven’t looked into this so I might be wrong… Feel free to tell me if you know!

That was it for this time!

Download code here: DarksideCookie.Azure.ServiceBusDemo.zip (142.84 kb)

Comments are closed