Skip to content

Commit 7e9680a

Browse files
authored
Performance Improvements (#133)
1 parent 5c0dbfb commit 7e9680a

File tree

9 files changed

+358
-281
lines changed

9 files changed

+358
-281
lines changed

Benchmarks/ClientBenchmarkApp/ClientBenchmark.cs

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,19 @@ namespace ClientBenchmarkApp;
1212
using HiveMQtt.MQTT5.ReasonCodes;
1313
using HiveMQtt.MQTT5.Types;
1414

15-
[SimpleJob(RunStrategy.Monitoring, iterationCount: 10, id: "MonitoringJob")]
15+
[SimpleJob(RunStrategy.Monitoring, iterationCount: 100, id: "MonitoringJob")]
1616
public class ClientBenchmarks : IDisposable
1717
{
1818
private readonly string smallPayload = new string(/*lang=json,strict*/ "{\"interference\": \"1029384\"}");
1919

2020
private HiveMQClient client;
2121

22+
public ClientBenchmarks()
23+
{
24+
Console.WriteLine("Starting HiveMQ client benchmarks...");
25+
this.client = null!;
26+
}
27+
2228
[GlobalSetup]
2329
public async Task SetupAsync()
2430
{
@@ -29,6 +35,7 @@ public async Task SetupAsync()
2935
};
3036

3137
this.client = new HiveMQClient(options);
38+
3239
Console.WriteLine($"Connecting to {options.Host} on port {options.Port}...");
3340
await this.client.ConnectAsync().ConfigureAwait(false);
3441

@@ -50,22 +57,24 @@ public async Task CleanUpAsync()
5057
}
5158

5259
[Benchmark(Description = "Publish a QoS 0 messages to the broker.")]
53-
public async Task PublishQoS0MessageAsync()
54-
{
55-
await this.client.PublishAsync("benchmarks/PublishQoS0Messages", this.smallPayload).ConfigureAwait(false);
56-
}
60+
public async Task PublishQoS0MessageAsync() =>
61+
await this.client.PublishAsync(
62+
"benchmarks/PublishQoS0Messages",
63+
this.smallPayload).ConfigureAwait(false);
5764

5865
[Benchmark(Description = "Publish a QoS 1 messages to the broker.")]
59-
public async Task PublishQoS1MessageAsync()
60-
{
61-
await this.client.PublishAsync("benchmarks/PublishQoS1Messages", this.smallPayload, QualityOfService.AtLeastOnceDelivery).ConfigureAwait(false);
62-
}
66+
public async Task PublishQoS1MessageAsync() =>
67+
await this.client.PublishAsync(
68+
"benchmarks/PublishQoS1Messages",
69+
this.smallPayload,
70+
QualityOfService.AtLeastOnceDelivery).ConfigureAwait(false);
6371

6472
[Benchmark(Description = "Publish a QoS 2 messages to the broker.")]
65-
public async Task PublishQoS2MessageAsync()
66-
{
67-
await this.client.PublishAsync("benchmarks/PublishQoS1Messages", this.smallPayload, QualityOfService.ExactlyOnceDelivery).ConfigureAwait(false);
68-
}
73+
public async Task PublishQoS2MessageAsync() =>
74+
await this.client.PublishAsync(
75+
"benchmarks/PublishQoS2Messages",
76+
this.smallPayload,
77+
QualityOfService.ExactlyOnceDelivery).ConfigureAwait(false);
6978

7079
public void Dispose() => GC.SuppressFinalize(this);
7180
}

Documentation/docs/how-to/configure-logging.md

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,29 @@ The HiveMQtt package uses [NLog](https://github.com/NLog/NLog) and can be config
2323
Setting `minlevel` to `Trace` will output all activity in the HiveMQtt package down to packet and event handling. Using this level will produce a lot of output such as the following:
2424

2525
```log
26-
2023-10-04 16:56:54.9373|TRACE|HiveMQtt.Client.HiveMQClient|BeforeConnectEventLauncher
27-
2023-10-04 16:56:55.0081|TRACE|HiveMQtt.Client.HiveMQClient|7: TrafficInflowProcessor Starting...Connecting
28-
2023-10-04 16:56:55.0081|TRACE|HiveMQtt.Client.HiveMQClient|9: TrafficOutflowProcessor Starting...Connecting
29-
2023-10-04 16:56:55.0081|TRACE|HiveMQtt.Client.HiveMQClient|--> ConnectPacket
30-
2023-10-04 16:56:55.0128|TRACE|HiveMQtt.Client.HiveMQClient|OnConnectSentEventLauncher
31-
2023-10-04 16:56:55.0374|TRACE|HiveMQtt.Client.HiveMQClient|<-- ConnAck
32-
2023-10-04 16:56:55.0374|TRACE|HiveMQtt.Client.HiveMQClient|OnConnAckReceivedEventLauncher
33-
2023-10-04 16:56:55.0379|TRACE|HiveMQtt.Client.HiveMQClient|AfterConnectEventLauncher
26+
2024-03-14 15:40:18.2252|TRACE|HiveMQtt.Client.HiveMQClient|Trace Level Logging Legend:
27+
2024-03-14 15:40:18.2312|TRACE|HiveMQtt.Client.HiveMQClient| -(W)- == ConnectionWriter
28+
2024-03-14 15:40:18.2312|TRACE|HiveMQtt.Client.HiveMQClient| -(R)- == ConnectionReader
29+
2024-03-14 15:40:18.2312|TRACE|HiveMQtt.Client.HiveMQClient| -(RPH)- == ReceivedPacketsHandler
30+
2024-03-14 15:40:18.2320|INFO|HiveMQtt.Client.HiveMQClient|Connecting to broker at 127.0.0.1:1883
31+
2024-03-14 15:40:18.2343|TRACE|HiveMQtt.Client.HiveMQClient|BeforeConnectEventLauncher
32+
2024-03-14 15:40:18.2460|TRACE|HiveMQtt.Client.HiveMQClient|Socket connected to 127.0.0.1:1883
33+
2024-03-14 15:40:18.2464|TRACE|HiveMQtt.Client.HiveMQClient|Queuing packet for send: HiveMQtt.MQTT5.Packets.ConnectPacket
34+
2024-03-14 15:40:18.2464|TRACE|HiveMQtt.Client.HiveMQClient|-(RPH)- Starting...Connecting
35+
2024-03-14 15:40:18.2464|TRACE|HiveMQtt.Client.HiveMQClient|-(R)- ConnectionReader Starting...Connecting
36+
2024-03-14 15:40:18.2464|TRACE|HiveMQtt.Client.HiveMQClient|5: ConnectionMonitor Starting...Connecting
37+
2024-03-14 15:40:18.2464|TRACE|HiveMQtt.Client.HiveMQClient|-(RPH)- 0 received packets currently waiting to be processed.
38+
2024-03-14 15:40:18.2464|TRACE|HiveMQtt.Client.HiveMQClient|-(W)- ConnectionWriter Starting...Connecting
39+
2024-03-14 15:40:18.2464|TRACE|HiveMQtt.Client.HiveMQClient|-(W)- ConnectionWriter: 1 packets waiting to be sent.
40+
2024-03-14 15:40:18.2476|TRACE|HiveMQtt.Client.HiveMQClient|-(W)- --> Sending ConnectPacket id=0
41+
2024-03-14 15:40:18.2529|TRACE|HiveMQtt.Client.HiveMQClient|OnConnectSentEventLauncher
42+
2024-03-14 15:40:18.2529|TRACE|HiveMQtt.Client.HiveMQClient|-(W)- ConnectionWriter: 0 packets waiting to be sent.
43+
2024-03-14 15:40:18.2732|TRACE|HiveMQtt.Client.HiveMQClient|ReadAsync: Read Buffer Length 11
44+
2024-03-14 15:40:18.2765|TRACE|HiveMQtt.MQTT5.PacketDecoder|PacketDecoder: Decoded Packet: consumed=11, packet=HiveMQtt.MQTT5.Packets.ConnAckPacket id=0
45+
2024-03-14 15:40:18.2765|TRACE|HiveMQtt.Client.HiveMQClient|-(R)- <-- Received ConnAckPacket. Adding to receivedQueue.
46+
2024-03-14 15:40:18.2765|TRACE|HiveMQtt.Client.HiveMQClient|-(RPH)- <-- Received ConnAck id=0
47+
2024-03-14 15:40:18.2765|TRACE|HiveMQtt.Client.HiveMQClient|OnConnAckReceivedEventLauncher
48+
2024-03-14 15:40:18.2775|TRACE|HiveMQtt.Client.HiveMQClient|AfterConnectEventLauncher
3449
```
3550

3651
## See Also

Source/HiveMQtt/Client/HiveMQClient.cs

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,16 @@ public HiveMQClient(HiveMQClientOptions? options = null)
4444
options ??= new HiveMQClientOptions();
4545
options.Validate();
4646

47+
Logger.Trace($"New client created: Client ID: {options.ClientId}");
48+
49+
Logger.Trace("Trace Level Logging Legend:");
50+
Logger.Trace(" -(W)- == ConnectionWriter");
51+
Logger.Trace(" -(R)- == ConnectionReader");
52+
Logger.Trace(" -(CM)- == ConnectionMonitor");
53+
Logger.Trace(" -(RPH)- == ReceivedPacketsHandler");
54+
4755
this.Options = options;
48-
this.cancellationSource = new CancellationTokenSource();
56+
this.cancellationTokenSource = new CancellationTokenSource();
4957
}
5058

5159
/// <inheritdoc />
@@ -80,7 +88,8 @@ public async Task<ConnectResult> ConnectAsync()
8088

8189
// Construct the MQTT Connect packet and queue to send
8290
var connPacket = new ConnectPacket(this.Options);
83-
this.sendQueue.Enqueue(connPacket);
91+
Logger.Trace($"Queuing packet for send: {connPacket}");
92+
this.sendQueue.Add(connPacket);
8493

8594
// FIXME: Cancellation token and better timeout value
8695
ConnAckPacket connAck;
@@ -126,6 +135,7 @@ public async Task<bool> DisconnectAsync(DisconnectOptions? options = null)
126135
{
127136
if (this.connectState != ConnectState.Connected)
128137
{
138+
Logger.Warn("DisconnectAsync: Client is not connected.");
129139
return false;
130140
}
131141

@@ -149,7 +159,8 @@ public async Task<bool> DisconnectAsync(DisconnectOptions? options = null)
149159
EventHandler<OnDisconnectSentEventArgs> eventHandler = TaskHandler;
150160
this.OnDisconnectSent += eventHandler;
151161

152-
this.sendQueue.Enqueue(disconnectPacket);
162+
Logger.Trace($"Queuing packet for send: {disconnectPacket}");
163+
this.sendQueue.Add(disconnectPacket);
153164

154165
try
155166
{
@@ -173,8 +184,17 @@ public async Task<bool> DisconnectAsync(DisconnectOptions? options = null)
173184

174185
this.connectState = ConnectState.Disconnected;
175186

176-
// Clear the send queue
177-
this.sendQueue.Clear();
187+
// FIXME
188+
if (this.sendQueue.Count > 0)
189+
{
190+
Logger.Warn("Disconnect: Send queue not empty. Packets pending but we are disconnecting.");
191+
}
192+
193+
// We only clear the send queue on explicit disconnect
194+
while (this.sendQueue.TryTake(out _))
195+
{
196+
}
197+
178198
return true;
179199
}
180200

@@ -189,7 +209,8 @@ public async Task<PublishResult> PublishAsync(MQTT5PublishMessage message)
189209
// QoS 0: Fast Service
190210
if (message.QoS == QualityOfService.AtMostOnceDelivery)
191211
{
192-
this.sendQueue.Enqueue(publishPacket);
212+
Logger.Trace($"Queuing packet for send: {publishPacket}");
213+
this.sendQueue.Add(publishPacket);
193214
return new PublishResult(publishPacket.Message);
194215
}
195216
else if (message.QoS == QualityOfService.AtLeastOnceDelivery)
@@ -201,7 +222,8 @@ public async Task<PublishResult> PublishAsync(MQTT5PublishMessage message)
201222
publishPacket.OnPublishQoS1Complete += eventHandler;
202223

203224
// Construct the MQTT Connect packet and queue to send
204-
this.sendQueue.Enqueue(publishPacket);
225+
Logger.Trace($"Queuing packet for send: {publishPacket}");
226+
this.sendQueue.Add(publishPacket);
205227

206228
var pubAckPacket = await taskCompletionSource.Task.WaitAsync(TimeSpan.FromSeconds(120)).ConfigureAwait(false);
207229

@@ -217,7 +239,7 @@ public async Task<PublishResult> PublishAsync(MQTT5PublishMessage message)
217239
publishPacket.OnPublishQoS2Complete += eventHandler;
218240

219241
// Construct the MQTT Connect packet and queue to send
220-
this.sendQueue.Enqueue(publishPacket);
242+
this.sendQueue.Add(publishPacket);
221243

222244
// Wait on the QoS 2 handshake
223245
var packetList = await taskCompletionSource.Task.WaitAsync(TimeSpan.FromSeconds(120)).ConfigureAwait(false);
@@ -300,7 +322,7 @@ public async Task<SubscribeResult> SubscribeAsync(SubscribeOptions options)
300322
this.OnSubAckReceived += eventHandler;
301323

302324
// Queue the constructed packet to be sent on the wire
303-
this.sendQueue.Enqueue(subscribePacket);
325+
this.sendQueue.Add(subscribePacket);
304326

305327
SubAckPacket subAck;
306328
SubscribeResult subscribeResult;
@@ -404,7 +426,7 @@ public async Task<UnsubscribeResult> UnsubscribeAsync(UnsubscribeOptions unsubOp
404426
EventHandler<OnUnsubAckReceivedEventArgs> eventHandler = TaskHandler;
405427
this.OnUnsubAckReceived += eventHandler;
406428

407-
this.sendQueue.Enqueue(unsubscribePacket);
429+
this.sendQueue.Add(unsubscribePacket);
408430

409431
// FIXME: Cancellation token and better timeout value
410432
UnsubAckPacket unsubAck;

Source/HiveMQtt/Client/HiveMQClientOptionsBuilder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ public HiveMQClientOptionsBuilder WithClientCertificate(string clientCertificate
192192
}
193193
else
194194
{
195-
Logger.Error("File does not exist.");
195+
Logger.Error("WithClientCertificate: The specified client certificate file does not exist.");
196196
throw new FileNotFoundException();
197197
}
198198
}

Source/HiveMQtt/Client/HiveMQClientSocket.cs

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,13 @@ public partial class HiveMQClient : IDisposable, IHiveMQClient
3434
private Stream? stream;
3535
private PipeReader? reader;
3636
private PipeWriter? writer;
37-
private CancellationTokenSource cancellationSource;
38-
private CancellationToken outFlowCancellationToken;
39-
private CancellationToken inFlowCancellationToken;
40-
private CancellationToken receivedPacketsCancellationToken;
37+
private CancellationTokenSource cancellationTokenSource;
4138

4239
#pragma warning disable IDE0052
43-
private Task? trafficOutflowProcessorTask;
44-
private Task? trafficInflowProcessorTask;
45-
private Task? receivedPacketsProcessorAsync;
40+
private Task? connectionWriterTask;
41+
private Task? connectionReaderTask;
42+
private Task? receivedPacketsHandlerAsync;
43+
private Task? connectionMonitorTask;
4644
#pragma warning restore IDE0052
4745

4846
/// <summary>
@@ -173,18 +171,14 @@ internal async Task<bool> ConnectSocketAsync()
173171
this.writer = PipeWriter.Create(this.stream);
174172

175173
// Reset the CancellationTokenSource in case this is a reconnect
176-
this.cancellationSource.Dispose();
177-
this.cancellationSource = new CancellationTokenSource();
178-
179-
// Setup the cancellation tokens
180-
this.outFlowCancellationToken = this.cancellationSource.Token;
181-
this.inFlowCancellationToken = this.cancellationSource.Token;
182-
this.receivedPacketsCancellationToken = this.cancellationSource.Token;
174+
this.cancellationTokenSource.Dispose();
175+
this.cancellationTokenSource = new CancellationTokenSource();
183176

184177
// Start the traffic processors
185-
this.trafficOutflowProcessorTask = this.TrafficOutflowProcessorAsync(this.outFlowCancellationToken);
186-
this.trafficInflowProcessorTask = this.TrafficInflowProcessorAsync(this.inFlowCancellationToken);
187-
this.receivedPacketsProcessorAsync = this.ReceivedPacketsProcessorAsync(this.receivedPacketsCancellationToken);
178+
this.connectionWriterTask = this.ConnectionWriterAsync(this.cancellationTokenSource.Token);
179+
this.connectionReaderTask = this.ConnectionReaderAsync(this.cancellationTokenSource.Token);
180+
this.receivedPacketsHandlerAsync = this.ReceivedPacketsHandlerAsync(this.cancellationTokenSource.Token);
181+
this.connectionMonitorTask = this.ConnectionMonitorAsync(this.cancellationTokenSource.Token);
188182

189183
Logger.Trace($"Socket connected to {this.socket.RemoteEndPoint}");
190184
return socketConnected;
@@ -254,6 +248,9 @@ private async Task<bool> CreateTLSConnectionAsync(Stream stream)
254248

255249
internal bool CloseSocket(bool? shutdownPipeline = true)
256250
{
251+
// Cancel the background traffic processing tasks
252+
this.cancellationTokenSource.Cancel();
253+
257254
if (shutdownPipeline == true)
258255
{
259256
// Shutdown the pipeline
@@ -265,8 +262,6 @@ internal bool CloseSocket(bool? shutdownPipeline = true)
265262
this.socket?.Shutdown(SocketShutdown.Both);
266263
this.socket?.Close();
267264

268-
this.cancellationSource.Cancel();
269-
270265
return true;
271266
}
272267
}

0 commit comments

Comments
 (0)