Akshay Sura - Partner
12 Nov 2018
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.
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using
System
;
using
System
.
Collections
.
Generic
;
using
System
.
Linq
;
using
System
.
Text
;
using
System
.
Threading
.
Tasks
;
using
Microsoft
.
Azure
.
Devices
.
Client
;
using
System
.
Threading
;
using
Microsoft
.
Azure
.
Devices
;
namespace
Microsoft
.
Azure
.
Devices
.
Client
.
Samples
{
class
Program
{
// needed when creating devices
// Example "HostName=myhub.azure-devices.net;SharedAccessKeyName=iothubowner;SharedAccessKey=0U1REMOVEDvrfUDo=";
static
string
iotHubConnectionString
=
"Your Connection String"
;
static
string
IoTHub
=
iotHubConnectionString
.
Split
(
';'
)[
0
].
Split
(
'='
)[
1
];
static
string
IoTDevicePrefix
=
"loadTest"
;
static
string
commonKey
=
"Your Key"
;
static
int
deviceCount
=
500
;
// how many devices we will create and clients we will launch
static
int
maxMessages
=
20
;
// once this count of messages are sent, the client shuts down
static
int
messageDelaySeconds
=
0
;
// how long between each message, honoring IoT Hub Quotas
static
int
runningDevices
=
0
;
static
bool
allDevicesDeleted
=
false
;
static
int
totalMessageSent
=
0
;
static
int
allClientStarted
=
0
;
static
void
Main
(
string
[]
args
)
{
// if the devices already exist, you can comment out this line
createDevices
(
deviceCount
);
Console
.
ReadKey
();
DateTime
startTime
=
DateTime
.
Now
;
for
(
int
deviceNumber
=
1
;
deviceNumber
<=
deviceCount
;
deviceNumber
++)
{
startClient
(
IoTHub
,
IoTDevicePrefix
,
deviceNumber
,
commonKey
,
maxMessages
,
messageDelaySeconds
);
}
// wait for the first few to start
Thread
.
Sleep
(
1000
*
30
);
while
(
runningDevices
!=
0
)
{
// if we still have devices running, wait
Thread
.
Sleep
(
3000
);
}
DateTime
endTime
=
DateTime
.
Now
;
Console
.
WriteLine
(
"Total Client: "
+
deviceCount
);
Console
.
WriteLine
(
"Total Messages Sent: "
+
deviceCount
*
maxMessages
);
Console
.
WriteLine
(
"Total Execution Time: "
+
(
endTime
-
startTime
).
TotalSeconds
+
" seconds"
);
Console
.
WriteLine
(
"Messages Per Second: "
+
totalMessageSent
/
(
endTime
-
startTime
).
TotalSeconds
);
Thread
.
Sleep
(
7000
);
deleteDevices
(
deviceCount
);
Thread
.
Sleep
(
1000
*
30
);
while
(
allDevicesDeleted
!=
true
)
{
// if we still have devices being deleted, wait
Thread
.
Sleep
(
1000
);
}
Console
.
WriteLine
(
"Total Client: "
+
deviceCount
);
Console
.
WriteLine
(
"Total Messages Sent: "
+
deviceCount
*
maxMessages
);
Console
.
WriteLine
(
"Total Execution Time: "
+
(
endTime
-
startTime
).
TotalSeconds
+
" seconds"
);
Console
.
WriteLine
(
"Messages Per Second: "
+
totalMessageSent
/
(
endTime
-
startTime
).
TotalSeconds
);
Console
.
WriteLine
(
"Press any key to end"
);
Console
.
ReadKey
();
}
static
async
Task
startClient
(
string
IoTHub
,
string
IoTDevicePrefix
,
int
deviceNumber
,
string
commonKey
,
int
maxMessages
,
int
messageDelaySeconds
)
{
allClientStarted
++;
runningDevices
++;
string
connectionString
=
"HostName="
+
IoTHub
+
";DeviceId="
+
IoTDevicePrefix
+
deviceNumber
+
";SharedAccessKey="
+
commonKey
;
DeviceClient
device
=
DeviceClient
.
CreateFromConnectionString
(
connectionString
,
Microsoft
.
Azure
.
Devices
.
Client
.
TransportType
.
Mqtt
);
await device
.
OpenAsync
();
Random
rnd
=
new
Random
();
int
mycounter
=
1
;
Console
.
WriteLine
(
"Device "
+
IoTDevicePrefix
+
deviceNumber
+
" started"
);
while
(
mycounter
<=
maxMessages
)
{
Thread
.
Sleep
((
messageDelaySeconds
*
1000
)
+
rnd
.
Next
(
1
,
100
));
string
message
=
"{ \'deviceId\': \'"
+
string
.
Concat
(
IoTDevicePrefix
,
deviceNumber
)
+
"\',\'message\':\'Be My Friend!\', 'sequenceNumber': "
+
mycounter
+
", \'submitTime\': \'"
+
DateTime
.
UtcNow
+
"\'}"
;
Console
.
WriteLine
(
message
);
Message
IoTMessage
=
new
Microsoft
.
Azure
.
Devices
.
Client
.
Message
(
Encoding
.
UTF8
.
GetBytes
(
message
));
await device
.
SendEventAsync
(
IoTMessage
);
totalMessageSent
++;
mycounter
++;
}
await device
.
CloseAsync
();
Console
.
WriteLine
(
"Device "
+
IoTDevicePrefix
+
deviceNumber
+
" ended"
);
runningDevices
--;
}
static
void
createDevices
(
int
number
)
{
for
(
int
i
=
1
;
i
<=
number
;
i
++)
{
var
registryManager
=
RegistryManager
.
CreateFromConnectionString
(
iotHubConnectionString
);
Device
mydevice
=
new
Device
(
IoTDevicePrefix
+
i
.
ToString
());
mydevice
.
Authentication
=
new
AuthenticationMechanism
();
mydevice
.
Authentication
.
SymmetricKey
.
PrimaryKey
=
commonKey
;
mydevice
.
Authentication
.
SymmetricKey
.
SecondaryKey
=
commonKey
;
try
{
registryManager
.
AddDeviceAsync
(
mydevice
).
Wait
();
Console
.
WriteLine
(
"Adding device: "
+
IoTDevicePrefix
+
i
.
ToString
());
}
catch
(
Exception
er
)
{
Console
.
WriteLine
(
" Error adding device: "
+
IoTDevicePrefix
+
i
.
ToString
()
+
" error: "
+
er
.
InnerException
.
Message
);
}
}
}
static
async
void
deleteDevices
(
int
number
)
{
for
(
int
i
=
1
;
i
<=
number
;
i
++)
{
var
registryManager
=
RegistryManager
.
CreateFromConnectionString
(
iotHubConnectionString
);
try
{
Device
mydevice
=
await registryManager
.
GetDeviceAsync
(
IoTDevicePrefix
+
i
.
ToString
());
registryManager
.
RemoveDeviceAsync
(
mydevice
).
Wait
();
Console
.
WriteLine
(
"Deleting device "
+
IoTDevicePrefix
+
i
.
ToString
());
}
catch
(
Exception
er
)
{
}
}
allDevicesDeleted
=
true
;
}
}
}
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
// This application uses the Microsoft Azure Event Hubs Client for .NET
// For samples see: https://github.com/Azure/azure-event-hubs/tree/master/samples/DotNet
// For documenation see: https://docs.microsoft.com/azure/event-hubs/
using
System
;
using
Microsoft
.
Azure
.
EventHubs
;
using
System
.
Threading
.
Tasks
;
using
System
.
Threading
;
using
System
.
Text
;
using
System
.
Collections
.
Generic
;
using
Newtonsoft
.
Json
;
using
System
.
Linq
;
namespace
read_d2c_messages
{
class
ReadDeviceToCloudMessages
{
private
static
int
noOfMessages
=
0
;
// az iot hub show --query properties.eventHubEndpoints.events.endpoint --name {your IoT Hub name}
private
readonly
static
string
s_eventHubsCompatibleEndpoint
=
"Your End Point"
;
// Event Hub-compatible name
// az iot hub show --query properties.eventHubEndpoints.events.path --name {your IoT Hub name}
private
readonly
static
string
s_eventHubsCompatiblePath
=
"bemyfriendiothub"
;
// az iot hub policy show --name iothubowner --query primaryKey --hub-name {your IoT Hub name}
private
readonly
static
string
s_iotHubSasKey
=
"Your Key"
;
private
readonly
static
string
s_iotHubSasKeyName
=
"iothubowner"
;
private
static
EventHubClient
s_eventHubClient
;
// Asynchronously create a PartitionReceiver for a partition and then start
// reading any messages sent from the simulated client.
private
static
async
Task
ReceiveMessagesFromDeviceAsync
(
string
partition
,
CancellationToken
ct
)
{
//messages.Clear();
// Create the receiver using the default consumer group.
// For the purposes of this sample, read only messages sent since
// the time the receiver is created. Typically, you don't want to skip any messages.
var
eventHubReceiver
=
s_eventHubClient
.
CreateReceiver
(
"$Default"
,
partition
,
EventPosition
.
FromEnqueuedTime
(
DateTime
.
Now
));
Console
.
WriteLine
(
"Create receiver on partition: "
+
partition
);
while
(
true
)
{
if
(
ct
.
IsCancellationRequested
)
break
;
//Console.WriteLine("Listening for messages on: " + partition);
// Check for EventData - this methods times out if there is nothing to retrieve.
var
events
=
await eventHubReceiver
.
ReceiveAsync
(
100
);
// If there is data in the batch, process it.
if
(
events
==
null
)
continue
;
foreach
(
EventData
eventData
in
events
)
{
string
message
=
Encoding
.
UTF8
.
GetString
(
eventData
.
Body
.
Array
);
noOfMessages
++;
Console
.
WriteLine
(
"Message: {0} Messages Received:{1}"
,
message
,
noOfMessages
.
ToString
());
}
}
}
private
static
async
Task
Main
(
string
[]
args
)
{
noOfMessages
=
0
;
Console
.
WriteLine
(
"IoT Hub Quickstarts - Read device to cloud messages. Ctrl-C to exit.n"
);
// Create an EventHubClient instance to connect to the
// IoT Hub Event Hubs-compatible endpoint.
var
connectionString
=
new
EventHubsConnectionStringBuilder
(
new
Uri
(
s_eventHubsCompatibleEndpoint
),
s_eventHubsCompatiblePath
,
s_iotHubSasKeyName
,
s_iotHubSasKey
);
s_eventHubClient
=
EventHubClient
.
CreateFromConnectionString
(
connectionString
.
ToString
());
// Create a PartitionReciever for each partition on the hub.
var
runtimeInfo
=
await s_eventHubClient
.
GetRuntimeInformationAsync
();
var
d2cPartitions
=
runtimeInfo
.
PartitionIds
;
CancellationTokenSource
cts
=
new
CancellationTokenSource
();
Console
.
CancelKeyPress
+=
(
s
,
e
)
=>
{
Console
.
WriteLine
(
"Number of Messages Received: {0}"
,
noOfMessages
.
ToString
());
};
var
tasks
=
new
List
<
Task
>();
foreach
(
string
partition
in
d2cPartitions
)
{
tasks
.
Add
(
ReceiveMessagesFromDeviceAsync
(
partition
,
cts
.
Token
));
}
// Wait for all the PartitionReceivers to finsih.
Task
.
WaitAll
(
tasks
.
ToArray
());
}
}
}
Useful Links:
If you have any questions or concerns, please get in touch with me. (@akshaysura13 on Twitter or on Slack).
Akshay is a nine-time Sitecore MVP and a two-time Kontent.ai. In addition to his work as a solution architect, Akshay is also one of the founders of SUGCON North America 2015, SUGCON India 2018 & 2019, Unofficial Sitecore Training, and Sitecore Slack.
Akshay founded and continues to run the Sitecore Hackathon. As one of the founding partners of Konabos Consulting, Akshay will continue to work with clients to lead projects and mentor their existing teams.