sse: improve reliability

This commit is contained in:
Abdullah Atta
2026-04-23 09:54:59 +05:00
parent 33413b0a5c
commit 31c57f95b2
3 changed files with 56 additions and 19 deletions
+31 -10
View File
@@ -18,28 +18,49 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using System.Linq;
using System;
using System.Threading;
using System.Threading.Tasks;
using Lib.AspNetCore.ServerSentEvents;
using System.Security.Claims;
using System.Collections.Generic;
namespace Streetwriters.Messenger.Helpers
{
public class SSEHelper
{
public static async Task SendEventToUserAsync(string data, IServerSentEventsService sseService, string userId, string? originTokenId = null)
public static async Task SendEventToUserAsync(string data, IServerSentEventsService sseService, string userId, string? originTokenId = null, CancellationToken cancellationToken = default)
{
var clients = sseService.GetClients().Where(c => c.User.FindFirstValue("sub") == userId);
foreach (var client in clients)
{
if (originTokenId != null && client.User.FindFirstValue("jti") == originTokenId) continue;
if (!client.IsConnected) continue;
await client.SendEventAsync(data);
}
var clients = sseService.GetClients()
.Where(c => c.User?.FindFirstValue("sub") == userId)
.Where(c => originTokenId == null || c.User?.FindFirstValue("jti") != originTokenId);
await SendEventToClientsAsync(clients, data, cancellationToken);
}
public static async Task SendEventToAllUsersAsync(string data, IServerSentEventsService sseService)
public static async Task SendEventToAllUsersAsync(string data, IServerSentEventsService sseService, CancellationToken cancellationToken = default)
{
await sseService.SendEventAsync(data);
await SendEventToClientsAsync(sseService.GetClients(), data, cancellationToken);
}
private static async Task SendEventToClientsAsync(IEnumerable<IServerSentEventsClient> clients, string data, CancellationToken cancellationToken)
{
foreach (var client in clients)
{
if (!client.IsConnected) continue;
try
{
await client.SendEventAsync(data, cancellationToken);
}
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
{
break;
}
catch
{
}
}
}
}
}
@@ -21,6 +21,7 @@ using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Lib.AspNetCore.ServerSentEvents;
using Streetwriters.Messenger.Helpers;
using System.Text.Json;
@@ -33,12 +34,14 @@ namespace Streetwriters.Messenger.Services
private const string HEARTBEAT_MESSAGE_FORMAT = "Streetwriters Heartbeat ({0} UTC)";
private readonly IServerSentEventsService _serverSentEventsService;
private readonly ILogger<HeartbeatService> _logger;
#endregion
#region Constructor
public HeartbeatService(IServerSentEventsService serverSentEventsService)
public HeartbeatService(IServerSentEventsService serverSentEventsService, ILogger<HeartbeatService> logger)
{
_serverSentEventsService = serverSentEventsService;
_logger = logger;
}
#endregion
@@ -47,15 +50,28 @@ namespace Streetwriters.Messenger.Services
{
while (!stoppingToken.IsCancellationRequested)
{
var message = JsonSerializer.Serialize(new
try
{
type = "heartbeat",
data = JsonSerializer.Serialize(new
var message = JsonSerializer.Serialize(new
{
t = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
})
});
await SSEHelper.SendEventToAllUsersAsync(message, _serverSentEventsService);
type = "heartbeat",
data = JsonSerializer.Serialize(new
{
t = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
})
});
await SSEHelper.SendEventToAllUsersAsync(message, _serverSentEventsService, stoppingToken);
}
catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested)
{
break;
}
catch (Exception ex)
{
_logger.LogWarning(ex, "Failed to send SSE heartbeat to one or more clients.");
}
await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken);
}
}
@@ -8,7 +8,7 @@
<ItemGroup>
<PackageReference Include="DotNetEnv" Version="2.3.0" />
<PackageReference Include="Lib.AspNetCore.ServerSentEvents" Version="6.0.0" />
<PackageReference Include="Lib.AspNetCore.ServerSentEvents" Version="9.1.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="5.0.0"
NoWarn="NU1605" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="5.0.0"