Skip to content
This repository was archived by the owner on Aug 14, 2024. It is now read-only.

Commit

Permalink
Initial
Browse files Browse the repository at this point in the history
  • Loading branch information
volkanalkilic committed Feb 15, 2023
0 parents commit ea42ed2
Show file tree
Hide file tree
Showing 5 changed files with 346 additions and 0 deletions.
197 changes: 197 additions & 0 deletions Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
using System.Diagnostics;
using MQTTnet;
using MQTTnet.Client;
using MQTTnet.Formatter;
using MQTTnet.Protocol;
using Nett;

namespace ThingsOn_MQTT_Bench {
internal class Program {

// Entry point of the program
private static async Task Main() {

// Prompt the user to start a new benchmark
Console.Write("Do you want to start a new benchmark? (y/n): ");
var answer = Console.ReadLine();

// Run the benchmark if the user answers "y"
if (answer?.ToLower() == "y") {
try {
await RunBenchmark();
}
catch (Exception ex) {
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"An error occurred: {ex.Message}");
Console.ResetColor();
}
}

// Main logic of the benchmark
async Task RunBenchmark() {
// Load benchmark settings
var (settings, serverUri, port, cleanSession, userName, password, keepAlivePeriod, connectionTimeOut,
mqttVersion, clientCount, messageCount, messageSize, qos, retain) = LoadSettings();

// Create MQTT clients
var factory = new MqttFactory();
var clients = Enumerable.Range(0, clientCount).Select(_ => factory.CreateMqttClient()).ToList();

// Connect to MQTT broker and measure connect time
var stopwatchConnect = Stopwatch.StartNew();
var connectTasks = clients.Select(c => c.ConnectAsync(CreateMqttClientOptions(serverUri, port, cleanSession, userName, password, keepAlivePeriod, connectionTimeOut, mqttVersion))).ToList();
await Task.WhenAll(connectTasks);
stopwatchConnect.Stop();
var elapsedConnect = stopwatchConnect.Elapsed;

// Generate messages
var messages = Enumerable.Range(1, messageCount)
.Select(i => new MqttApplicationMessageBuilder()
.WithTopic($"test/{i}")
.WithPayload(new byte[messageSize])
.WithQualityOfServiceLevel((MqttQualityOfServiceLevel) qos)
.WithRetainFlag(retain)
.Build())
.ToList();

// Start benchmark
var stopwatch = Stopwatch.StartNew();

// Send messages
var totalMessagesSent = clientCount * messageCount;
var sent = 0;
var progressReport = 0;
var sendTasks = clients.Select(client => Task.Run(async () => {
var results = new List<MqttClientPublishResult>();
foreach (var message in messages) {
// Publish message and store the result
var result = await client.PublishAsync(message);
results.Add(result);

// Update progress
sent++;
var newProgressReport = sent * 100 / totalMessagesSent;
if (newProgressReport >= progressReport + 10) {
progressReport = newProgressReport / 10 * 10;
Console.CursorLeft = 0;
Console.Write($"\rProgress: {progressReport}%");
}
}

return results;
})).ToList();

// Wait for all messages to be sent
var sendResults = await Task.WhenAll(sendTasks);
Console.Write("\rProgress: 100%");
Console.WriteLine();
Console.WriteLine();

// End benchmark
stopwatch.Stop();
var elapsed = stopwatch.Elapsed;
var messagesSent = clientCount * messageCount;
var throughput = messagesSent / elapsed.TotalSeconds;
var lossRate = (double) sendResults.SelectMany(r => r).Count(r => r.ReasonCode != MqttClientPublishReasonCode.Success) / messagesSent;
var successRate = 1.0 - lossRate;

// Disconnect from MQTT broker and measure disconnect time
var stopwatchDisconnect = Stopwatch.StartNew();
var disconnectTasks = clients.Select(c => c.DisconnectAsync()).ToList();
await Task.WhenAll(disconnectTasks);
stopwatchDisconnect.Stop();
var elapsedDisconnect = stopwatchDisconnect.Elapsed;

// Print results
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("Benchmark completed 🚀");
Console.WriteLine("=======================================");
Console.WriteLine($"{"Messages sent",-20} {messagesSent:N0}");
Console.WriteLine($"{"Elapsed time",-20} {elapsed.TotalSeconds:F3}");
Console.WriteLine($"{"Throughput",-20} {throughput:N0} messages/second");
Console.WriteLine($"{"Connect time",-20} {elapsedConnect.TotalSeconds:F3} seconds");
Console.WriteLine($"{"Disconnect time",-20} {elapsedDisconnect.TotalSeconds:F3} seconds");
Console.WriteLine($"{"Success rate",-20} {successRate:P0}");
Console.WriteLine($"{"Loss rate",-20} {lossRate:P0}");

// Convert data size to appropriate units
var dataSize = (double) (totalMessagesSent * settings.Get<int>("MessageSize"));
var dataUnits = new[] {"B", "KB", "MB", "GB", "TB"};
var dataUnitIndex = 0;
while (dataSize >= 1024 && dataUnitIndex < dataUnits.Length - 1) {
dataSize /= 1024;
dataUnitIndex++;
}

Console.WriteLine($"{"Data sent",-20} {dataSize:N3} {dataUnits[dataUnitIndex]}");
Console.WriteLine("=======================================");
Console.ResetColor();

// Prompt the user to run the benchmark again
await Main();
}
}

private static (TomlTable settings, string serverUri, int port, bool cleanSession, string userName, string password, TimeSpan keepAlivePeriod, TimeSpan connectionTimeOut, MqttProtocolVersion mqttVersion, int clientCount, int messageCount, int messageSize, int qos, bool retain) LoadSettings() {
// Load settings from config.toml
var settings = Toml.ReadFile(Path.Combine(Directory.GetCurrentDirectory(), "config.toml"));

// Parse benchmark settings
var serverUri = settings.Get<string>("ServerUri");
var port = settings.Get<int>("Port");
var cleanSession = settings.Get<bool>("CleanSession");
var userName = settings.Get<string>("Username");
var password = settings.Get<string>("Password");
var keepAlivePeriod = TimeSpan.FromSeconds(settings.Get<int>("KeepAlivePeriod"));
var connectionTimeOut = TimeSpan.FromSeconds(settings.Get<int>("ConnectionTimeOut"));
var mqttVersion = ParseMqttProtocolVersion(settings.Get<string>("MqttVersion"));
var clientCount = settings.Get<int>("ClientCount");
var messageCount = settings.Get<int>("MessageCount");
var messageSize = settings.Get<int>("MessageSize");
var qos = settings.Get<int>("Qos");
var retain = settings.Get<bool>("Retain");

// Print benchmark settings
Console.ForegroundColor = ConsoleColor.Blue;
Console.WriteLine($"Benchmark settings: {clientCount} clients, {messageCount} messages/client, {messageSize:N0} bytes/message, QoS {qos}, retain {retain}, MQTT version {mqttVersion}.");
Console.WriteLine($"Server URI: {serverUri}");
Console.WriteLine($"Port: {port}");
Console.WriteLine($"Clean session: {cleanSession}");
Console.WriteLine($"User name: {userName}");
Console.WriteLine(!string.IsNullOrEmpty(password) ? $"Password: {password[0]}{new string('*', password.Length - 1)}" : "Password: (empty)");
Console.WriteLine($"Keep alive period: {keepAlivePeriod}");
Console.WriteLine($"Connection timeout: {connectionTimeOut}");
Console.ResetColor();
return (settings, serverUri, port, cleanSession, userName, password, keepAlivePeriod, connectionTimeOut, mqttVersion, clientCount, messageCount, messageSize, qos, retain);
}

// Create MQTT client options from parsed settings
private static MqttClientOptions CreateMqttClientOptions(string serverUri, int port, bool cleanSession, string userName, string password, TimeSpan keepAlivePeriod, TimeSpan connectionTimeOut, MqttProtocolVersion mqttVersion) {
var options = new MqttClientOptionsBuilder()
.WithClientId(Guid.NewGuid().ToString())
.WithTcpServer(serverUri, port)
.WithCleanSession(cleanSession)
.WithCredentials(userName, password)
.WithKeepAlivePeriod(keepAlivePeriod)
.WithTimeout(connectionTimeOut)
.WithProtocolVersion(mqttVersion)
.Build();
return options;
}

// Parse MQTT protocol version from a string
private static MqttProtocolVersion ParseMqttProtocolVersion(string value) {
switch (value.ToLowerInvariant()) {
case "v310":
return MqttProtocolVersion.V310;
case "v311":
return MqttProtocolVersion.V311;
case "v500":
return MqttProtocolVersion.V500;
default:
throw new ArgumentException($"Invalid MQTT version '{value}'.");
}
}

}
}
92 changes: 92 additions & 0 deletions Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# ThingsOn MQTT Bench

ThingsOn MQTT Bench is a benchmark tool for MQTT brokers. It measures the maximum number of messages that can be sent by the broker in a specified amount of time.

## Features

* Measures the maximum number of messages that can be sent by an MQTT broker in a specified amount of time.
* Supports MQTT brokers running MQTT protocol versions 3.1.1 (MQTTv3.1.1) and 5.0 (MQTTv5.0).
* Allows customization of the following benchmark settings:
* Number of MQTT clients to use for benchmarking
* Number of messages to send per client
* Message size in bytes
* Quality of Service (QoS) level for messages (0, 1, or 2)
* Whether messages should be retained by the broker
* Outputs progress information to the console during benchmarking.
* Outputs the following information upon benchmark completion:
* Number of messages sent
* Total time taken to send messages
* Message throughput (messages per second)
* Time taken to connect to the MQTT broker
* Time taken to disconnect from the MQTT broker
* Success rate (percentage of messages that were successfully sent)
* Loss rate (percentage of messages that were not successfully sent)

## Usage

To use ThingsOn MQTT Bench, first make sure that you have .NET 7 installed on your system. Then, download the latest release of ThingsOn MQTT Bench for your operating system from the [releases page](https://github.com/yourusername/ThingsOn-MQTT-Bench/releases).

Once you have downloaded the tool, you can run it from the command line by navigating to the directory where the tool is located and running the following command:

<pre><div class="bg-black mb-4 rounded-md"><div class="flex items-center relative text-gray-200 bg-gray-800 px-4 py-2 text-xs font-sans"><button class="flex ml-auto gap-2"><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="h-4 w-4" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"></path><rect x="8" y="2" width="8" height="4" rx="1" ry="1"></rect></svg>Copy code</button></div><div class="p-4 overflow-y-auto"><code class="!whitespace-pre hljs">dotnet ThingsOnMqttBench.dll
</code></div></div></pre>

By default, the tool will read its settings from a TOML file named `config.toml` in the same directory as the tool. You can customize the settings by editing this file.

## Settings

The following settings are available in the `config.toml` file:

* `Mqtt`: MQTT broker settings
* `ServerUri`: URI of the MQTT broker
* `Port`: Port number of the MQTT broker
* `MqttVersion`: MQTT protocol version (either "V311" or "V500")
* `Username`: Username for connecting to the MQTT broker
* `Password`: Password for connecting to the MQTT broker
* `ClientIdPrefix`: Prefix for MQTT client IDs
* `KeepAlivePeriod`: Keep-alive period in seconds
* `CleanSession`: Whether to use a clean session when connecting to the MQTT broker
* `MaximumQoS`: Maximum quality of service level for messages
* `Benchmark`: Benchmark settings
* `ClientCount`: Number of MQTT clients to use for benchmarking
* `MessageCount`: Number of messages to send per client
* `MessageSize`: Size of messages in bytes
* `Qos`: Quality of service level for messages (0, 1, or 2)
* `Retain`: Whether messages should be retained by the broker


## Building from Code

To build ThingsOn MQTT Bench from code, follow these steps:

1. Install [.NET 7](https://dotnet.microsoft.com/download/dotnet/7.0).
2. Clone the GitHub repository:

<pre><div class="bg-black mb-4 rounded-md"><div class="flex items-center relative text-gray-200 bg-gray-800 px-4 py-2 text-xs font-sans"><span class="">bash</span><button class="flex ml-auto gap-2"><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="h-4 w-4" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"></path><rect x="8" y="2" width="8" height="4" rx="1" ry="1"></rect></svg>Copy code</button></div><div class="p-4 overflow-y-auto"><code class="!whitespace-pre hljs language-bash">git clone https://github.com/your-username/ThingsOn-MQTT-Bench.git
</code></div></div></pre>

3. Navigate to the cloned repository directory:

<pre><div class="bg-black mb-4 rounded-md"><div class="flex items-center relative text-gray-200 bg-gray-800 px-4 py-2 text-xs font-sans"><span class="">bash</span><button class="flex ml-auto gap-2"><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="h-4 w-4" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"></path><rect x="8" y="2" width="8" height="4" rx="1" ry="1"></rect></svg>Copy code</button></div><div class="p-4 overflow-y-auto"><code class="!whitespace-pre hljs language-bash">cd ThingsOn-MQTT-Bench
</code></div></div></pre>

4. Build the project using the following command:

<pre><div class="bg-black mb-4 rounded-md"><div class="flex items-center relative text-gray-200 bg-gray-800 px-4 py-2 text-xs font-sans"><button class="flex ml-auto gap-2"><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="h-4 w-4" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"></path><rect x="8" y="2" width="8" height="4" rx="1" ry="1"></rect></svg>Copy code</button></div><div class="p-4 overflow-y-auto"><code class="!whitespace-pre hljs">dotnet build
</code></div></div></pre>

5. Once the project has built successfully, you can run the benchmark using the following command:

<pre><div class="bg-black mb-4 rounded-md"><div class="flex items-center relative text-gray-200 bg-gray-800 px-4 py-2 text-xs font-sans"><span class="">css</span><button class="flex ml-auto gap-2"><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" stroke-linecap="round" stroke-linejoin="round" class="h-4 w-4" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"></path><rect x="8" y="2" width="8" height="4" rx="1" ry="1"></rect></svg>Copy code</button></div><div class="p-4 overflow-y-auto"><code class="!whitespace-pre hljs language-css">dotnet run --project ThingsOnMqttBench
</code></div></div></pre>


## Contributing

Contributions to ThingsOn MQTT Bench are welcome! If you find a bug or would like to suggest a new feature, please open an issue on the [GitHub repository](https://github.com/yourusername/ThingsOn-MQTT-Bench).

If you would like to contribute code to ThingsOn MQTT Bench, please fork the repository and submit a pull request.

## License

ThingsOn MQTT Bench is licensed under the MIT license. See the [LICENSE](https://chat.openai.com/chat/LICENSE) file for more information.
23 changes: 23 additions & 0 deletions ThingsOn.MQTT.Bench.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<RootNamespace>ThingsOn_MQTT_Bench</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="MQTTnet" Version="4.1.4.563" />
<PackageReference Include="Nett" Version="0.15.0" />
</ItemGroup>

<ItemGroup>
<None Remove="config.toml" />
<Content Include="config.toml">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>

</Project>
16 changes: 16 additions & 0 deletions ThingsOn.MQTT.Bench.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@

Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ThingsOn.MQTT.Bench", "ThingsOn.MQTT.Bench.csproj", "{5512F858-5184-4530-95F7-26D8ED9BD26F}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{5512F858-5184-4530-95F7-26D8ED9BD26F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5512F858-5184-4530-95F7-26D8ED9BD26F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5512F858-5184-4530-95F7-26D8ED9BD26F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5512F858-5184-4530-95F7-26D8ED9BD26F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
18 changes: 18 additions & 0 deletions config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
ServerUri = "127.0.0.1"
Port = 1883
# MQTT protocol version to use ("V311", "V310" or "V500")
MqttVersion = "V500"
Username = ""
Password = ""
ConnectionTimeOut = 1
KeepAlivePeriod = 60
CleanSession = true
# number of clients to use for the benchmark
ClientCount = 50
# number of messages to send from each client
MessageCount = 10000
# size of each message in bytes
MessageSize = 100
# quality of service level to use for message delivery (0, 1 or 2)
Qos = 0
Retain = false

0 comments on commit ea42ed2

Please sign in to comment.