Load testing the Azure IoT Hub

This past weekend I was playing with the Azure IoT hub and decided to load test it to see how much throughput can be achieved.





On this install I ran 6 tests of 500 clients with 200 messages each and was able to get 107 Messages per second through put. I then decided to test it out by increasing the Device to Cloud partitions to see if it makes any difference. I added a new hub which has 32 partitions.


I ran a few more tests with 500 devices with 200 messages each and got about 109 Messages per second throughput. I decided to reduce the number of messages being transmitted to see if we get a better throughput and indeed we did. I tested with 500 devices with 20 messages each and I was able to achieve 294 Messages per second.

Depending on what you are trying to do, you might find a suitable scaling option. The results I got satisfied my need for the project I am working on.

    1. // Copyright (c) Microsoft. All rights reserved.
    2. // Licensed under the MIT license. See LICENSE file in the project root for full license information.
    3.  
    4. using System;
    5. using System.Collections.Generic;
    6. using System.Linq;
    7. using System.Text;
    8. using System.Threading.Tasks;
    9. using Microsoft.Azure.Devices.Client;
    10. using System.Threading;
    11. using Microsoft.Azure.Devices;
    12.  
    13. namespace Microsoft.Azure.Devices.Client.Samples
    14. {
    15. class Program
    16. {
    17. // needed when creating devices
    18. // Example "HostName=myhub.azure-devices.net;SharedAccessKeyName=iothubowner;SharedAccessKey=0U1REMOVEDvrfUDo=";
    19. static string iotHubConnectionString = "Your Connection String";
    20. static string IoTHub = iotHubConnectionString.Split(';')[0].Split('=')[1];
    21. static string IoTDevicePrefix = "loadTest";
    22. static string commonKey = "Your Key";
    23. static int deviceCount = 500; // how many devices we will create and clients we will launch
    24. static int maxMessages = 20; // once this count of messages are sent, the client shuts down
    25. static int messageDelaySeconds = 0; // how long between each message, honoring IoT Hub Quotas
    26. static int runningDevices = 0;
    27. static bool allDevicesDeleted = false;
    28. static int totalMessageSent = 0;
    29. static int allClientStarted = 0;
    30.  
    31. static void Main(string[] args)
    32. {
    33. // if the devices already exist, you can comment out this line
    34. createDevices(deviceCount);
    35. Console.ReadKey();
    36. DateTime startTime = DateTime.Now;
    37. for (int deviceNumber = 1; deviceNumber <= deviceCount; deviceNumber++)
    38. {
    39. startClient(IoTHub, IoTDevicePrefix, deviceNumber, commonKey, maxMessages, messageDelaySeconds);
    40. }
    41.  
    42. // wait for the first few to start
    43. Thread.Sleep(1000 * 30);
    44.  
    45. while (runningDevices != 0)
    46. {
    47. // if we still have devices running, wait
    48. Thread.Sleep(3000);
    49. }
    50. DateTime endTime = DateTime.Now;
    51. Console.WriteLine("Total Client: " + deviceCount);
    52. Console.WriteLine("Total Messages Sent: " + deviceCount * maxMessages);
    53. Console.WriteLine("Total Execution Time: " + (endTime - startTime).TotalSeconds + " seconds");
    54. Console.WriteLine("Messages Per Second: " + totalMessageSent / (endTime - startTime).TotalSeconds);
    55. Thread.Sleep(7000);
    56. deleteDevices(deviceCount);
    57. Thread.Sleep(1000 * 30);
    58. while (allDevicesDeleted != true)
    59. {
    60. // if we still have devices being deleted, wait
    61. Thread.Sleep(1000);
    62. }
    63. Console.WriteLine("Total Client: " + deviceCount);
    64. Console.WriteLine("Total Messages Sent: " + deviceCount * maxMessages);
    65. Console.WriteLine("Total Execution Time: " + (endTime - startTime).TotalSeconds + " seconds");
    66. Console.WriteLine("Messages Per Second: " + totalMessageSent / (endTime - startTime).TotalSeconds);
    67.  
    68. Console.WriteLine("Press any key to end");
    69. Console.ReadKey();
    70. }
    71.  
    72. static async Task startClient(string IoTHub, string IoTDevicePrefix, int deviceNumber, string commonKey, int maxMessages, int messageDelaySeconds)
    73. {
    74. allClientStarted++;
    75. runningDevices++;
    76. string connectionString = "HostName=" + IoTHub + ";DeviceId=" + IoTDevicePrefix + deviceNumber + ";SharedAccessKey=" + commonKey;
    77. DeviceClient device = DeviceClient.CreateFromConnectionString(connectionString, Microsoft.Azure.Devices.Client.TransportType.Mqtt);
    78. await device.OpenAsync();
    79. Random rnd = new Random();
    80. int mycounter = 1;
    81. Console.WriteLine("Device " + IoTDevicePrefix + deviceNumber + " started");
    82.  
    83. while (mycounter <= maxMessages)
    84. {
    85. Thread.Sleep((messageDelaySeconds * 1000) + rnd.Next(1, 100));
    86. string message = "{ \'deviceId\': \'" + string.Concat(IoTDevicePrefix, deviceNumber) + "\',\'message\':\'Be My Friend!\', 'sequenceNumber': " + mycounter + ", \'submitTime\': \'" + DateTime.UtcNow + "\'}";
    87. Console.WriteLine(message);
    88. Message IoTMessage = new Microsoft.Azure.Devices.Client.Message(Encoding.UTF8.GetBytes(message));
    89. await device.SendEventAsync(IoTMessage);
    90. totalMessageSent++;
    91. mycounter++;
    92. }
    93. await device.CloseAsync();
    94. Console.WriteLine("Device " + IoTDevicePrefix + deviceNumber + " ended");
    95. runningDevices--;
    96. }
    97.  
    98. static void createDevices(int number)
    99. {
    100. for (int i = 1; i <= number; i++)
    101. {
    102. var registryManager = RegistryManager.CreateFromConnectionString(iotHubConnectionString);
    103. Device mydevice = new Device(IoTDevicePrefix + i.ToString());
    104. mydevice.Authentication = new AuthenticationMechanism();
    105. mydevice.Authentication.SymmetricKey.PrimaryKey = commonKey;
    106. mydevice.Authentication.SymmetricKey.SecondaryKey = commonKey;
    107. try
    108. {
    109. registryManager.AddDeviceAsync(mydevice).Wait();
    110. Console.WriteLine("Adding device: " + IoTDevicePrefix + i.ToString());
    111. }
    112. catch (Exception er)
    113. {
    114. Console.WriteLine(" Error adding device: " + IoTDevicePrefix + i.ToString() + " error: " + er.InnerException.Message);
    115. }
    116. }
    117.  
    118. }
    119. static async void deleteDevices(int number)
    120. {
    121. for (int i = 1; i <= number; i++)
    122. {
    123. var registryManager = RegistryManager.CreateFromConnectionString(iotHubConnectionString);
    124.  
    125. try
    126. {
    127. Device mydevice = await registryManager.GetDeviceAsync(IoTDevicePrefix + i.ToString());
    128. registryManager.RemoveDeviceAsync(mydevice).Wait();
    129. Console.WriteLine("Deleting device " + IoTDevicePrefix + i.ToString());
    130. }
    131. catch (Exception er) { }
    132.  
    133. }
    134. allDevicesDeleted = true;
    135.  
    136. }
    137. }
    138. }

    1. // Copyright (c) Microsoft. All rights reserved.
    2. // Licensed under the MIT license. See LICENSE file in the project root for full license information.
    3.  
    4. // This application uses the Microsoft Azure Event Hubs Client for .NET
    5. // For samples see: https://github.com/Azure/azure-event-hubs/tree/master/samples/DotNet
    6. // For documenation see: https://docs.microsoft.com/azure/event-hubs/
    7. using System;
    8. using Microsoft.Azure.EventHubs;
    9. using System.Threading.Tasks;
    10. using System.Threading;
    11. using System.Text;
    12. using System.Collections.Generic;
    13. using Newtonsoft.Json;
    14. using System.Linq;
    15.  
    16. namespace read_d2c_messages
    17. {
    18. class ReadDeviceToCloudMessages
    19. {
    20. private static int noOfMessages = 0;
    21. // az iot hub show --query properties.eventHubEndpoints.events.endpoint --name {your IoT Hub name}
    22. private readonly static string s_eventHubsCompatibleEndpoint = "Your End Point";
    23.  
    24. // Event Hub-compatible name
    25. // az iot hub show --query properties.eventHubEndpoints.events.path --name {your IoT Hub name}
    26. private readonly static string s_eventHubsCompatiblePath = "bemyfriendiothub";
    27.  
    28. // az iot hub policy show --name iothubowner --query primaryKey --hub-name {your IoT Hub name}
    29. private readonly static string s_iotHubSasKey = "Your Key";
    30. private readonly static string s_iotHubSasKeyName = "iothubowner";
    31. private static EventHubClient s_eventHubClient;
    32.  
    33. // Asynchronously create a PartitionReceiver for a partition and then start
    34. // reading any messages sent from the simulated client.
    35. private static async Task ReceiveMessagesFromDeviceAsync(string partition, CancellationToken ct)
    36. {
    37. //messages.Clear();
    38. // Create the receiver using the default consumer group.
    39. // For the purposes of this sample, read only messages sent since
    40. // the time the receiver is created. Typically, you don't want to skip any messages.
    41. var eventHubReceiver = s_eventHubClient.CreateReceiver("$Default", partition, EventPosition.FromEnqueuedTime(DateTime.Now));
    42. Console.WriteLine("Create receiver on partition: " + partition);
    43. while (true)
    44. {
    45. if (ct.IsCancellationRequested) break;
    46. //Console.WriteLine("Listening for messages on: " + partition);
    47. // Check for EventData - this methods times out if there is nothing to retrieve.
    48. var events = await eventHubReceiver.ReceiveAsync(100);
    49.  
    50. // If there is data in the batch, process it.
    51. if (events == null) continue;
    52.  
    53. foreach(EventData eventData in events)
    54. {
    55. string message = Encoding.UTF8.GetString(eventData.Body.Array);
    56. noOfMessages++;
    57. Console.WriteLine("Message: {0} Messages Received:{1}", message, noOfMessages.ToString());
    58. }
    59. }
    60. }
    61.  
    62. private static async Task Main(string[] args)
    63. {
    64. noOfMessages = 0;
    65. Console.WriteLine("IoT Hub Quickstarts - Read device to cloud messages. Ctrl-C to exit.n");
    66.  
    67. // Create an EventHubClient instance to connect to the
    68. // IoT Hub Event Hubs-compatible endpoint.
    69. var connectionString = new EventHubsConnectionStringBuilder(new Uri(s_eventHubsCompatibleEndpoint), s_eventHubsCompatiblePath, s_iotHubSasKeyName, s_iotHubSasKey);
    70. s_eventHubClient = EventHubClient.CreateFromConnectionString(connectionString.ToString());
    71.  
    72. // Create a PartitionReciever for each partition on the hub.
    73. var runtimeInfo = await s_eventHubClient.GetRuntimeInformationAsync();
    74. var d2cPartitions = runtimeInfo.PartitionIds;
    75.  
    76. CancellationTokenSource cts = new CancellationTokenSource();
    77.  
    78. Console.CancelKeyPress += (s, e) =>
    79. {
    80. Console.WriteLine("Number of Messages Received: {0}", noOfMessages.ToString());
    81. };
    82.  
    83. var tasks = new List<Task>();
    84. foreach (string partition in d2cPartitions)
    85. {
    86. tasks.Add(ReceiveMessagesFromDeviceAsync(partition, cts.Token));
    87. }
    88.  
    89. // Wait for all the PartitionReceivers to finsih.
    90. Task.WaitAll(tasks.ToArray());
    91. }
    92. }
    93. }

Useful Links:

*Load Testing Azure IoT Hub with simulated client
*Choose the right IoT Hub tier for your solution
*Tutorial: Use a simulated device to test connectivity with your IoT hub
*Azure IoT Samples for Csharp (.NET)
*Tutorial: Develop a C# IoT Edge module and deploy to your simulated device

If you have any questions or concerns, please get in touch with me. (@akshaysura13 on twitter or on Slack).