Service Bus Traffic Test

July 18, 2023

This post is basically a write up of an experiment. Not sure if you remember chemistry experiments from school - but they never really went to plan; the write up was never very honest; otherwise it would have read like this:

We started with vials of red and blue liquid - since we weren’t paying much attention, we don’t really know what they were. We poured one into the other and it turned green. Since there was a Bunsen Burner on the bench, we assumed we’re probably supposed to boil the mixture, so we did. I’m writing this with my remaining arm - three of the class are in hospital with third degree burns, and the chemistry teacher is being interviewed by counter-terrorism.

This did go off track, but that’s kind of why I ended up with a blog post. I could have simply deployed this and scaled it, but I ended up doing everything locally.

Creating the Function

The first step is to create an Azure function - my app has two functions in it: a Read and a Write.

Create Function

The Write is just a timer function to send messages in bulk:

[Function("Function1")]
public async Task Run([TimerTrigger("* * * * * *")] MyInfo myTimer)
{
    _logger.LogInformation($"C# Timer trigger function executed at: {DateTime.Now}");

    using ServiceBusMessageBatch messageBatch = await sender.CreateMessageBatchAsync();

    for (int i = 1; i <= numOfMessages; i++)
    {
        if (!messageBatch.TryAddMessage(new ServiceBusMessage($"{DateTime.Now} Message {i}")))
        {                    
            throw new Exception($"The message {i} is too large to fit in the batch.");
        }
    }

    try
    {                
        await sender.SendMessagesAsync(messageBatch);
        Console.WriteLine($"A batch of {numOfMessages} messages has been published to the queue.");
    }
    finally
    {
        await sender.DisposeAsync();
        await client.DisposeAsync();
    }
}

If you want more information about the schedule - I wrote this some time ago.

If you’re wondering, most of the code that you see here was taken from the MS articles listen at the bottom of this post - this is an experiment, so most of the code is taken from wherever I could find it (which, all credit to MS, is mostly on their site).

The second function was a Service Bus Trigger to read the messages - since I don’t have anything to do when they are read, that’s all it does:

[Function("Function1")]
public void Run([ServiceBusTrigger("HighTrafficTopic", "ReadMessages", Connection = "SBConnectionString")] string mySbMsg)
{
    _logger.LogInformation($"C# ServiceBus topic trigger function processed message: {mySbMsg}");
}

This function, by default, will consume the messages as they are read (you can change that if you so choose). The connection string is in the local.settings.json:

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
    "FUNCTIONS_WORKER_RUNTIME": "dotnet-isolated",
    "SBConnectionString": "Endpoint=sb://test-sb-throughput.servicebus.windows.net/;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=ABCDEmJQo3330li7gI9crQqQtTtTrzdfkdfjkfdf"
  }
}

Okay, so we’re now ready to run!

Actually, there’s basically where this post starts…

You can set multiple projects to start at the same time. That will start the Read and Write functions together - and you’ll get an error because they’re both using the same port. This can be easily changed, though:

Setup debug

Setting the port in the launch profile (command line arguments) to:

host start --pause-on-error --port 5800

Will allow you to run the Read and Write projects on separate ports.

In my tests, a function that uses the SB trigger simply can’t keep up with a batch send (If you’re interested in how it could be made to, have a look at this previous post).

If starting one Read project isn’t enough, can’t we just start a second?

No - they would both be listening on the same port (the option above changes the port for the project, so each new instance is on the same port.)

Command Line

At this stage, the answer is to drop to the command line. You’ll first need to install the Azure Function Core Tools. You’ll need to pre-build the project (in VS if you wish) and you can (must) run it from the release directory (/bin/debug/net6):

func host start --no-build --pause-on-error --port 5801

Probably worth noting here, before someone else does, that, yes - I am running a performance test locally while using a debug compilation, but at this point, I’m more interested in the mechanics.

Now you can run as many as you wish in different terminal windows!

Run Command

Not really relevant to this post, but locally, it took two clients to keep up with 200 messages per second:

Function Statistics

References

Microsoft: Quickstart - send and receive ASB Messages

Microsoft: Work with Azure Functions Core Tools

Stack Overflow: Port Conflict



Profile picture

A blog about one man's journey through code… and some pictures of the Peak District
Twitter

© Paul Michaels 2024