Skip to content

Commit

Permalink
General performance improvments
Browse files Browse the repository at this point in the history
  • Loading branch information
tpeczek committed Mar 27, 2018
1 parent a51cf66 commit e08be52
Show file tree
Hide file tree
Showing 7 changed files with 149 additions and 107 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,16 @@ public class ServerSentEventsServiceBenchmarks
#region Fields
private const int MULTIPLE_CLIENTS_COUNT = 10000;

private const string EVENT_TYPE = "Benchmark";
private const string EVENT_DATA = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";

private readonly ServerSentEventsClient _serverSentEventsClient;
private readonly ServerSentEventsService _serverSentEventsService;
private readonly ServerSentEvent _event = new ServerSentEvent
{
Id = Guid.NewGuid().ToString(),
Type = "Benchmark",
Data = new List<string> { "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." }
Type = EVENT_TYPE,
Data = new List<string> { EVENT_DATA }
};
#endregion

Expand All @@ -40,9 +43,27 @@ public ServerSentEventsServiceBenchmarks()

#region Benchmarks
[Benchmark]
public void SendEventAsync_SingleEvent_SingleClient()
public Task SendEventAsync_SingleData_SingleClient()
{
return _serverSentEventsClient.SendEventAsync(EVENT_DATA);
}

[Benchmark]
public Task SendEventAsync_SingleEvent_SingleClient()
{
return _serverSentEventsClient.SendEventAsync(_event);
}

[Benchmark]
public Task ChangeReconnectIntervalAsync_SingleClient()
{
return _serverSentEventsClient.ChangeReconnectIntervalAsync(5000);
}

[Benchmark]
public Task SendEventAsync_SingleData_MultipleClients()
{
_serverSentEventsClient.SendEvent(_event);
return _serverSentEventsService.SendEventAsync(EVENT_DATA);
}

[Benchmark]
Expand Down
48 changes: 0 additions & 48 deletions Lib.AspNetCore.ServerSentEvents/Internals/RawServerSentEvent.cs

This file was deleted.

20 changes: 20 additions & 0 deletions Lib.AspNetCore.ServerSentEvents/Internals/ServerSentEventBytes.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
namespace Lib.AspNetCore.ServerSentEvents.Internals
{
internal readonly struct ServerSentEventBytes
{
#region Properties
internal byte[] Bytes { get; }

internal int BytesCount { get; }
#endregion

#region Constructor
internal ServerSentEventBytes(byte[] bytes, int bytesCount)
: this()
{
Bytes = bytes;
BytesCount = bytesCount;
}
#endregion
}
}
117 changes: 92 additions & 25 deletions Lib.AspNetCore.ServerSentEvents/Internals/ServerSentEventsHelper.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System.Text;
using System;
using System.Text;
using System.Globalization;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;

Expand All @@ -7,65 +9,130 @@ namespace Lib.AspNetCore.ServerSentEvents.Internals
internal static class ServerSentEventsHelper
{
#region Fields
private const byte CR = 13;
private const byte LF = 10;
private const int CRLF_LENGTH = 2;

private static byte[] _sseRetryField = Encoding.UTF8.GetBytes(Constants.SSE_RETRY_FIELD);
private static byte[] _sseIdField = Encoding.UTF8.GetBytes(Constants.SSE_ID_FIELD);
private static byte[] _sseEventField = Encoding.UTF8.GetBytes(Constants.SSE_EVENT_FIELD);
private static byte[] _sseDataField = Encoding.UTF8.GetBytes(Constants.SSE_DATA_FIELD);
private static byte[] _endOfLine = new byte[] { 13, 10 };
#endregion

#region HttpResponse Extensions
internal static async Task AcceptSse(this HttpResponse response)
#region Methods
internal static Task AcceptAsync(this HttpResponse response)
{
response.ContentType = Constants.SSE_CONTENT_TYPE;
await response.Body.FlushAsync();
return response.Body.FlushAsync();
}

internal static Task WriteAsync(this HttpResponse response, ServerSentEventBytes serverSentEvent)
{
return response.Body.WriteAsync(serverSentEvent.Bytes, 0, serverSentEvent.BytesCount);
}

internal static ServerSentEventBytes GetReconnectIntervalBytes(uint reconnectInterval)
{
string reconnectIntervalStringified = reconnectInterval.ToString(CultureInfo.InvariantCulture);

byte[] bytes = new byte[GetFieldMaxBytesCount(_sseRetryField, reconnectIntervalStringified) + CRLF_LENGTH];
int bytesCount = GetFieldBytes(_sseRetryField, reconnectIntervalStringified, bytes, 0);

bytes[bytesCount++] = CR;
bytes[bytesCount++] = LF;

return new ServerSentEventBytes(bytes, bytesCount);
}

internal static async Task WriteSseRetryAsync(this HttpResponse response, byte[] reconnectInterval)
internal static ServerSentEventBytes GetEventBytes(string text)
{
await response.WriteSseEventFieldAsync(_sseRetryField, reconnectInterval);
await response.WriteSseEventBoundaryAsync();
byte[] bytes = new byte[GetFieldMaxBytesCount(_sseDataField, text) + CRLF_LENGTH];
int bytesCount = GetFieldBytes(_sseDataField, text, bytes, 0);

bytes[bytesCount++] = CR;
bytes[bytesCount++] = LF;

return new ServerSentEventBytes(bytes, bytesCount);
}

internal static async Task WriteSseEventAsync(this HttpResponse response, byte[] data)
internal static ServerSentEventBytes GetEventBytes(ServerSentEvent serverSentEvent)
{
await response.WriteSseEventFieldAsync(_sseDataField, data);
await response.WriteSseEventBoundaryAsync();
int bytesCount = 0;
byte[] bytes = new byte[GetEventMaxBytesCount(serverSentEvent)];

if (!String.IsNullOrWhiteSpace(serverSentEvent.Id))
{
bytesCount = GetFieldBytes(_sseIdField, serverSentEvent.Id, bytes, bytesCount);
}

if (!String.IsNullOrWhiteSpace(serverSentEvent.Type))
{
bytesCount = GetFieldBytes(_sseEventField, serverSentEvent.Type, bytes, bytesCount);
}

if (serverSentEvent.Data != null)
{
for (int dataItemIndex = 0; dataItemIndex < serverSentEvent.Data.Count; dataItemIndex++)
{
if (serverSentEvent.Data[dataItemIndex] != null)
{
bytesCount = GetFieldBytes(_sseDataField, serverSentEvent.Data[dataItemIndex], bytes, bytesCount);
}
}
}

bytes[bytesCount++] = CR;
bytes[bytesCount++] = LF;

return new ServerSentEventBytes(bytes, bytesCount);
}

internal static async Task WriteSseEventAsync(this HttpResponse response, RawServerSentEvent serverSentEvent)
private static int GetEventMaxBytesCount(ServerSentEvent serverSentEvent)
{
if (serverSentEvent.Id != null)
int bytesCount = CRLF_LENGTH;

if (!String.IsNullOrWhiteSpace(serverSentEvent.Id))
{
await response.WriteSseEventFieldAsync(_sseIdField, serverSentEvent.Id);
bytesCount += GetFieldMaxBytesCount(_sseIdField, serverSentEvent.Id);
}

if (serverSentEvent.Type != null)
if (!String.IsNullOrWhiteSpace(serverSentEvent.Type))
{
await response.WriteSseEventFieldAsync(_sseEventField, serverSentEvent.Type);
bytesCount += GetFieldMaxBytesCount(_sseEventField, serverSentEvent.Type);
}

if (serverSentEvent.Data != null)
{
for (int i = 0; i < serverSentEvent.Data.Count; i++)
for (int dataItemIndex = 0; dataItemIndex < serverSentEvent.Data.Count; dataItemIndex++)
{
await response.WriteSseEventFieldAsync(_sseDataField, serverSentEvent.Data[i]);
if (serverSentEvent.Data[dataItemIndex] != null)
{
bytesCount += GetFieldMaxBytesCount(_sseDataField, serverSentEvent.Data[dataItemIndex]);
}
}
}

await response.WriteSseEventBoundaryAsync();
return bytesCount;
}

private static async Task WriteSseEventFieldAsync(this HttpResponse response, byte[] field, byte[] data)
private static int GetFieldBytes(byte[] field, string data, byte[] bytes, int bytesCount)
{
await response.Body.WriteAsync(field, 0, field.Length);
await response.Body.WriteAsync(data, 0, data.Length);
await response.Body.WriteAsync(_endOfLine, 0, _endOfLine.Length);
for (int fieldIndex = 0; fieldIndex < field.Length; fieldIndex++)
{
bytes[bytesCount++] = field[fieldIndex];
}

bytesCount += Encoding.UTF8.GetBytes(data, 0, data.Length, bytes, bytesCount);

bytes[bytesCount++] = CR;
bytes[bytesCount++] = LF;

return bytesCount;
}

private static Task WriteSseEventBoundaryAsync(this HttpResponse response)
private static int GetFieldMaxBytesCount(byte[] field, string data)
{
return response.Body.WriteAsync(_endOfLine, 0, _endOfLine.Length);
return field.Length + Encoding.UTF8.GetMaxByteCount(data.Length) + CRLF_LENGTH;
}
#endregion
}
Expand Down
26 changes: 5 additions & 21 deletions Lib.AspNetCore.ServerSentEvents/ServerSentEventsClient.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using System;
using System.Text;
using System.Globalization;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
Expand Down Expand Up @@ -52,7 +50,7 @@ internal ServerSentEventsClient(Guid id, ClaimsPrincipal user, HttpResponse resp
/// <returns>The task object representing the asynchronous operation.</returns>
public Task SendEventAsync(string text)
{
return SendEventAsync(Encoding.UTF8.GetBytes(text));
return SendAsync(ServerSentEventsHelper.GetEventBytes(text));
}

/// <summary>
Expand All @@ -62,33 +60,19 @@ public Task SendEventAsync(string text)
/// <returns>The task object representing the asynchronous operation.</returns>
public Task SendEventAsync(ServerSentEvent serverSentEvent)
{
return SendEventAsync(new RawServerSentEvent(serverSentEvent));
return SendAsync(ServerSentEventsHelper.GetEventBytes(serverSentEvent));
}

internal Task SendEventAsync(byte[] data)
internal Task SendAsync(ServerSentEventBytes serverSentEvent)
{
CheckIsConnected();

return _response.WriteSseEventAsync(data);
}

internal Task SendEventAsync(RawServerSentEvent serverSentEvent)
{
CheckIsConnected();

return _response.WriteSseEventAsync(serverSentEvent);
return _response.WriteAsync(serverSentEvent);
}

internal Task ChangeReconnectIntervalAsync(uint reconnectInterval)
{
return ChangeReconnectIntervalAsync(Encoding.UTF8.GetBytes(reconnectInterval.ToString(CultureInfo.InvariantCulture)));
}

internal Task ChangeReconnectIntervalAsync(byte[] reconnectInterval)
{
CheckIsConnected();

return _response.WriteSseRetryAsync(reconnectInterval);
return SendAsync(ServerSentEventsHelper.GetReconnectIntervalBytes(reconnectInterval));
}

private void CheckIsConnected()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public async Task Invoke(HttpContext context)

HandleContentEncoding(context);

await context.Response.AcceptSse();
await context.Response.AcceptAsync();

ServerSentEventsClient client = new ServerSentEventsClient(Guid.NewGuid(), context.User, context.Response);

Expand Down
14 changes: 6 additions & 8 deletions Lib.AspNetCore.ServerSentEvents/ServerSentEventsService.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
using System;
using System.Linq;
using System.Text;
using System.Globalization;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Collections.Concurrent;
Expand Down Expand Up @@ -58,9 +56,9 @@ public Task ChangeReconnectIntervalAsync(uint reconnectInterval)
{
ReconnectInterval = reconnectInterval;

byte[] reconnectIntervalBytes = Encoding.UTF8.GetBytes(reconnectInterval.ToString(CultureInfo.InvariantCulture));
ServerSentEventBytes reconnectIntervalBytes = ServerSentEventsHelper.GetReconnectIntervalBytes(reconnectInterval);

return ForAllClientsAsync(client => client.ChangeReconnectIntervalAsync(reconnectIntervalBytes));
return ForAllClientsAsync(client => client.SendAsync(reconnectIntervalBytes));
}

/// <summary>
Expand All @@ -70,9 +68,9 @@ public Task ChangeReconnectIntervalAsync(uint reconnectInterval)
/// <returns>The task object representing the asynchronous operation.</returns>
public Task SendEventAsync(string text)
{
byte[] data = Encoding.UTF8.GetBytes(text);
ServerSentEventBytes serverSentEventBytes = ServerSentEventsHelper.GetEventBytes(text);

return ForAllClientsAsync(client => client.SendEventAsync(data));
return ForAllClientsAsync(client => client.SendAsync(serverSentEventBytes));
}

/// <summary>
Expand All @@ -82,9 +80,9 @@ public Task SendEventAsync(string text)
/// <returns>The task object representing the asynchronous operation.</returns>
public Task SendEventAsync(ServerSentEvent serverSentEvent)
{
RawServerSentEvent rawServerSentEvent = new RawServerSentEvent(serverSentEvent);
ServerSentEventBytes serverSentEventBytes = ServerSentEventsHelper.GetEventBytes(serverSentEvent);

return ForAllClientsAsync(client => client.SendEventAsync(rawServerSentEvent));
return ForAllClientsAsync(client => client.SendAsync(serverSentEventBytes));
}

/// <summary>
Expand Down

0 comments on commit e08be52

Please sign in to comment.