From 17f26714ce167c7eb5c5422be84737b8db546f33 Mon Sep 17 00:00:00 2001 From: rootdarkarchon Date: Wed, 5 Oct 2022 23:10:36 +0200 Subject: [PATCH] Switch to GrpcClientIdentificationService and abolish Redis for Idents (#12) * add GrpcClientIdentificationService * remove unnecessary gauges * set to no retry policy * initialize metrics Co-authored-by: Stanley Dimant --- .../MareSynchronosServer/Hubs/MareHub.cs | 12 +- .../GrpcClientIdentificationService.cs | 158 ++++++++++++++++++ .../Services/SystemInfoService.cs | 7 +- .../MareSynchronosServer/Startup.cs | 39 +++-- .../Discord/DiscordBot.cs | 13 +- .../Identity/IdentityHandler.cs | 62 +++++++ .../Identity/IdentityService.cs | 69 ++++++++ .../MareSynchronosServices/Startup.cs | 22 +-- .../Protos/mareservices.proto | 42 +++++ .../BaseClientIdentificationService.cs | 65 ------- .../DistributedClientIdentificationService.cs | 71 -------- .../Services/IClientIdentificationService.cs | 12 -- .../LocalClientIdentificationService.cs | 10 -- 13 files changed, 373 insertions(+), 209 deletions(-) create mode 100644 MareSynchronosServer/MareSynchronosServer/Services/GrpcClientIdentificationService.cs create mode 100644 MareSynchronosServer/MareSynchronosServices/Identity/IdentityHandler.cs create mode 100644 MareSynchronosServer/MareSynchronosServices/Identity/IdentityService.cs delete mode 100644 MareSynchronosServer/MareSynchronosShared/Services/BaseClientIdentificationService.cs delete mode 100644 MareSynchronosServer/MareSynchronosShared/Services/DistributedClientIdentificationService.cs delete mode 100644 MareSynchronosServer/MareSynchronosShared/Services/IClientIdentificationService.cs delete mode 100644 MareSynchronosServer/MareSynchronosShared/Services/LocalClientIdentificationService.cs diff --git a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.cs b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.cs index 0f7d125..acec7d0 100644 --- a/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.cs +++ b/MareSynchronosServer/MareSynchronosServer/Hubs/MareHub.cs @@ -8,9 +8,7 @@ using MareSynchronosServer.Utils; using MareSynchronosShared.Authentication; using MareSynchronosShared.Data; using MareSynchronosShared.Metrics; -using MareSynchronosShared.Models; using MareSynchronosShared.Protos; -using MareSynchronosShared.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.SignalR; @@ -27,7 +25,7 @@ public partial class MareHub : Hub private readonly FileService.FileServiceClient _fileServiceClient; private readonly SystemInfoService _systemInfoService; private readonly IHttpContextAccessor _contextAccessor; - private readonly IClientIdentificationService _clientIdentService; + private readonly GrpcClientIdentificationService _clientIdentService; private readonly MareHubLogger _logger; private readonly MareDbContext _dbContext; private readonly Uri _cdnFullUri; @@ -38,7 +36,7 @@ public partial class MareHub : Hub public MareHub(MareMetrics mareMetrics, AuthService.AuthServiceClient authServiceClient, FileService.FileServiceClient fileServiceClient, MareDbContext mareDbContext, ILogger logger, SystemInfoService systemInfoService, IConfiguration configuration, IHttpContextAccessor contextAccessor, - IClientIdentificationService clientIdentService) + GrpcClientIdentificationService clientIdentService) { _mareMetrics = mareMetrics; _authServiceClient = authServiceClient; @@ -118,9 +116,11 @@ public partial class MareHub : Hub [Authorize(AuthenticationSchemes = SecretKeyGrpcAuthenticationHandler.AuthScheme)] public async Task CheckClientHealth() { - var needsReconnect = string.IsNullOrEmpty(await _clientIdentService.GetCharacterIdentForUid(AuthenticatedUserId).ConfigureAwait(false)); - if (needsReconnect) + var serverId = await _clientIdentService.GetServerForUid(AuthenticatedUserId).ConfigureAwait(false); + bool needsReconnect = false; + if (string.IsNullOrEmpty(serverId) || !string.Equals(serverId, _shardName, StringComparison.Ordinal)) { + needsReconnect = true; _logger.LogCallWarning(Api.InvokeCheckClientHealth, needsReconnect); } return needsReconnect; diff --git a/MareSynchronosServer/MareSynchronosServer/Services/GrpcClientIdentificationService.cs b/MareSynchronosServer/MareSynchronosServer/Services/GrpcClientIdentificationService.cs new file mode 100644 index 0000000..5135fc1 --- /dev/null +++ b/MareSynchronosServer/MareSynchronosServer/Services/GrpcClientIdentificationService.cs @@ -0,0 +1,158 @@ +using Grpc.Core; +using MareSynchronosShared.Metrics; +using MareSynchronosShared.Protos; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Concurrent; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace MareSynchronosServer.Services; + +public class GrpcClientIdentificationService : IHostedService +{ + private readonly string _shardName; + private readonly ILogger _logger; + private readonly IdentificationService.IdentificationServiceClient _grpcIdentClient; + private readonly MareMetrics _metrics; + protected ConcurrentDictionary OnlineClients = new(StringComparer.Ordinal); + private bool _grpcIsFaulty = false; + + public GrpcClientIdentificationService(ILogger logger, IdentificationService.IdentificationServiceClient gprcIdentClient, MareMetrics metrics, IConfiguration configuration) + { + var config = configuration.GetSection("MareSynchronos"); + _shardName = config.GetValue("ServerName", "Main"); + _logger = logger; + _grpcIdentClient = gprcIdentClient; + _metrics = metrics; + } + + public async Task GetCharacterIdentForUid(string uid) + { + if (OnlineClients.TryGetValue(uid, out string ident)) + { + return ident; + } + + var result = await InvokeOnGrpc(_grpcIdentClient.GetIdentForUidAsync(new UidMessage { Uid = uid })).ConfigureAwait(false); + if (result == default(CharacterIdentMessage)) return null; + return result.Ident; + } + + public async Task GetServerForUid(string uid) + { + if (OnlineClients.ContainsKey(uid)) + { + return _shardName; + } + + var result = await InvokeOnGrpc(_grpcIdentClient.GetIdentForUidAsync(new UidMessage { Uid = uid })).ConfigureAwait(false); + if (result == default(CharacterIdentMessage)) return null; + return result.ServerId; + } + + public async Task GetOnlineUsers() + { + var result = await InvokeOnGrpc(_grpcIdentClient.GetOnlineUserCountAsync(new ServerMessage())).ConfigureAwait(false); + if (result == default(OnlineUserCountResponse)) return OnlineClients.Count; + return result.Count; + } + + public async Task GetUidForCharacterIdent(string characterIdent) + { + bool existsLocal = OnlineClients.Any(o => string.Equals(o.Value, characterIdent, StringComparison.Ordinal)); + if (existsLocal) + { + return OnlineClients.First(c => string.Equals(c.Value, characterIdent, StringComparison.Ordinal)).Key; + } + + var result = await InvokeOnGrpc(_grpcIdentClient.GetUidForCharacterIdentAsync(new CharacterIdentMessage { Ident = characterIdent, ServerId = string.Empty })).ConfigureAwait(false); + if (result == default(UidMessage)) return null; + return result.Uid; + } + + public async Task MarkUserOffline(string uid) + { + OnlineClients.TryRemove(uid, out _); + _metrics.SetGaugeTo(MetricsAPI.GaugeAuthorizedConnections, OnlineClients.Count); + await ExecuteOnGrpc(_grpcIdentClient.RemoveIdentForUidAsync(new RemoveIdentMessage() { ServerId = _shardName, Uid = uid })).ConfigureAwait(false); + } + + public async Task MarkUserOnline(string uid, string charaIdent) + { + OnlineClients[uid] = charaIdent; + _metrics.SetGaugeTo(MetricsAPI.GaugeAuthorizedConnections, OnlineClients.Count); + await ExecuteOnGrpc(_grpcIdentClient.SetIdentForUidAsync(new SetIdentMessage() { Ident = charaIdent, ServerId = _shardName, Uid = uid })).ConfigureAwait(false); + } + + public async Task StartAsync(CancellationToken cancellationToken) + { + await ExecuteOnGrpc(_grpcIdentClient.ClearIdentsForServerAsync(new ServerMessage() { ServerId = _shardName })).ConfigureAwait(false); + } + + public async Task StopAsync(CancellationToken cancellationToken) + { + await ExecuteOnGrpc(_grpcIdentClient.ClearIdentsForServerAsync(new ServerMessage() { ServerId = _shardName })).ConfigureAwait(false); + } + + private async Task InvokeOnGrpc(AsyncUnaryCall toExecute) + { + try + { + var result = await toExecute.ConfigureAwait(false); + + await CheckFaultStateAndResend().ConfigureAwait(false); + + return result; + } + catch + { + SetGrpcFaulty(); + + return default; + } + } + + private async Task ExecuteOnGrpc(AsyncUnaryCall toExecute) + { + try + { + await toExecute.ConfigureAwait(false); + await CheckFaultStateAndResend().ConfigureAwait(false); + } + catch + { + SetGrpcFaulty(); + } + } + + private async Task CheckFaultStateAndResend() + { + if (_grpcIsFaulty) + { + _logger.LogInformation("GRPC connection is restored, sending current server idents"); + await _grpcIdentClient.ClearIdentsForServerAsync(new ServerMessage() { ServerId = _shardName }).ConfigureAwait(false); + var msg = new ServerIdentMessage(); + msg.Idents.AddRange(OnlineClients.Select(c => new SetIdentMessage() + { + Ident = c.Value, + Uid = c.Key, + ServerId = _shardName + })); + await _grpcIdentClient.RecreateServerIdentsAsync(msg).ConfigureAwait(false); + _grpcIsFaulty = false; + } + } + + private void SetGrpcFaulty() + { + if (!_grpcIsFaulty) + { + _grpcIsFaulty = true; + _logger.LogWarning("GRPC connection is faulty"); + } + } +} diff --git a/MareSynchronosServer/MareSynchronosServer/Services/SystemInfoService.cs b/MareSynchronosServer/MareSynchronosServer/Services/SystemInfoService.cs index eae24eb..36e669e 100644 --- a/MareSynchronosServer/MareSynchronosServer/Services/SystemInfoService.cs +++ b/MareSynchronosServer/MareSynchronosServer/Services/SystemInfoService.cs @@ -6,7 +6,6 @@ using MareSynchronos.API; using MareSynchronosServer.Hubs; using MareSynchronosShared.Data; using MareSynchronosShared.Metrics; -using MareSynchronosShared.Services; using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -19,14 +18,14 @@ public class SystemInfoService : IHostedService, IDisposable { private readonly MareMetrics _mareMetrics; private readonly IServiceProvider _services; - private readonly IClientIdentificationService _clientIdentService; + private readonly GrpcClientIdentificationService _clientIdentService; private readonly ILogger _logger; private readonly IHubContext _hubContext; private Timer _timer; private string _shardName; public SystemInfoDto SystemInfoDto { get; private set; } = new(); - public SystemInfoService(MareMetrics mareMetrics, IConfiguration configuration, IServiceProvider services, IClientIdentificationService clientIdentService, ILogger logger, IHubContext hubContext) + public SystemInfoService(MareMetrics mareMetrics, IConfiguration configuration, IServiceProvider services, GrpcClientIdentificationService clientIdentService, ILogger logger, IHubContext hubContext) { _mareMetrics = mareMetrics; _services = services; @@ -56,7 +55,7 @@ public class SystemInfoService : IHostedService, IDisposable { SystemInfoDto = new SystemInfoDto() { - OnlineUsers = _clientIdentService.GetOnlineUsers().Result, + OnlineUsers = (int)_clientIdentService.GetOnlineUsers().Result, }; _hubContext.Clients.All.SendAsync(Api.OnUpdateSystemInfo, SystemInfoDto); diff --git a/MareSynchronosServer/MareSynchronosServer/Startup.cs b/MareSynchronosServer/MareSynchronosServer/Startup.cs index 8cf0884..2b72f0f 100644 --- a/MareSynchronosServer/MareSynchronosServer/Startup.cs +++ b/MareSynchronosServer/MareSynchronosServer/Startup.cs @@ -20,7 +20,6 @@ using Prometheus; using MareSynchronosShared.Metrics; using System.Collections.Generic; using MareSynchronosServer.Services; -using MareSynchronosShared.Services; using System.Net.Http; using MareSynchronosServer.Utils; @@ -64,6 +63,12 @@ public class Startup } }; + var identMethodConfig = new MethodConfig + { + Names = { MethodName.Default }, + RetryPolicy = null + }; + services.AddSingleton(new MareMetrics(new List { MetricsAPI.CounterInitializedConnections, @@ -77,7 +82,10 @@ public class Startup MetricsAPI.GaugePairs, MetricsAPI.GaugePairsPaused, MetricsAPI.GaugeAvailableIOWorkerThreads, - MetricsAPI.GaugeAvailableWorkerThreads + MetricsAPI.GaugeAvailableWorkerThreads, + MetricsAPI.GaugeGroups, + MetricsAPI.GaugeGroupPairs, + MetricsAPI.GaugeGroupPairsPaused })); services.AddGrpcClient(c => @@ -98,6 +106,20 @@ public class Startup { c.ServiceConfig = new ServiceConfig { MethodConfigs = { defaultMethodConfig } }; }); + services.AddGrpcClient(c => + { + c.Address = new Uri(mareConfig.GetValue("ServiceAddress")); + }).ConfigureChannel(c => + { + c.ServiceConfig = new ServiceConfig { MethodConfigs = { identMethodConfig } }; + c.HttpHandler = new SocketsHttpHandler() + { + EnableMultipleHttp2Connections = true + }; + }); + + services.AddSingleton(); + services.AddHostedService(p => p.GetService()); services.AddDbContextPool(options => { @@ -135,19 +157,6 @@ public class Startup { options.Configuration.ChannelPrefix = "MareSynchronos"; }); - - services.AddStackExchangeRedisCache(opt => - { - opt.Configuration = redis; - opt.InstanceName = "MareSynchronosCache:"; - }); - services.AddSingleton(); - services.AddHostedService(p => p.GetService()); - } - else - { - services.AddSingleton(); - services.AddHostedService(p => p.GetService()); } services.AddHostedService(provider => provider.GetService()); diff --git a/MareSynchronosServer/MareSynchronosServices/Discord/DiscordBot.cs b/MareSynchronosServer/MareSynchronosServices/Discord/DiscordBot.cs index fbc943b..659a63a 100644 --- a/MareSynchronosServer/MareSynchronosServices/Discord/DiscordBot.cs +++ b/MareSynchronosServer/MareSynchronosServices/Discord/DiscordBot.cs @@ -12,10 +12,10 @@ using Discord; using Discord.Rest; using Discord.WebSocket; using MareSynchronosServices.Authentication; +using MareSynchronosServices.Identity; using MareSynchronosShared.Data; using MareSynchronosShared.Metrics; using MareSynchronosShared.Models; -using MareSynchronosShared.Services; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -24,11 +24,11 @@ using Microsoft.Extensions.Logging; namespace MareSynchronosServices.Discord; -public class DiscordBot : IHostedService +internal class DiscordBot : IHostedService { private readonly CleanupService cleanupService; private readonly MareMetrics metrics; - private readonly IClientIdentificationService clientService; + private readonly IdentityHandler identityHandler; private readonly IServiceProvider services; private readonly IConfiguration _configuration; private readonly ILogger logger; @@ -44,16 +44,15 @@ public class DiscordBot : IHostedService private ConcurrentDictionary LastVanityChange = new(); private ConcurrentDictionary LastVanityGidChange = new(); private ulong vanityCommandId; - private ulong vanityGidCommandId; private Task cleanUpUserTask = null; private SemaphoreSlim semaphore; - public DiscordBot(CleanupService cleanupService, MareMetrics metrics, IClientIdentificationService clientService, IServiceProvider services, IConfiguration configuration, ILogger logger) + public DiscordBot(CleanupService cleanupService, MareMetrics metrics, IdentityHandler identityHandler, IServiceProvider services, IConfiguration configuration, ILogger logger) { this.cleanupService = cleanupService; this.metrics = metrics; - this.clientService = clientService; + this.identityHandler = identityHandler; this.services = services; _configuration = configuration.GetRequiredSection("MareSynchronos"); this.logger = logger; @@ -804,7 +803,7 @@ public class DiscordBot : IHostedService updateStatusCts = new(); while (!updateStatusCts.IsCancellationRequested) { - var onlineUsers = await clientService.GetOnlineUsers(); + var onlineUsers = identityHandler.GetOnlineUsers(string.Empty); logger.LogInformation("Users online: " + onlineUsers); await discordClient.SetActivityAsync(new Game("Mare for " + onlineUsers + " Users")).ConfigureAwait(false); await Task.Delay(TimeSpan.FromSeconds(15)).ConfigureAwait(false); diff --git a/MareSynchronosServer/MareSynchronosServices/Identity/IdentityHandler.cs b/MareSynchronosServer/MareSynchronosServices/Identity/IdentityHandler.cs new file mode 100644 index 0000000..0ee24e9 --- /dev/null +++ b/MareSynchronosServer/MareSynchronosServices/Identity/IdentityHandler.cs @@ -0,0 +1,62 @@ +using System.Collections.Concurrent; +using System.Linq; +using System.Threading.Tasks; + +namespace MareSynchronosServices.Identity; + +internal class IdentityHandler +{ + private readonly ConcurrentDictionary cachedIdentities = new(); + + internal Task GetUidForCharacterIdent(string ident, string serverId) + { + var exists = cachedIdentities.Any(f => f.Value.CharacterIdent == ident && f.Value.ServerId == serverId); + return Task.FromResult(exists ? cachedIdentities.FirstOrDefault(f => f.Value.CharacterIdent == ident && f.Value.ServerId == serverId).Key : string.Empty); + } + + internal Task GetIdentForuid(string uid) + { + ServerIdentity result; + if (!cachedIdentities.TryGetValue(uid, out result)) + { + result = new ServerIdentity(); + } + + return Task.FromResult(result); + } + + internal void SetIdent(string uid, string serverId, string ident) + { + cachedIdentities[uid] = new ServerIdentity() { ServerId = serverId, CharacterIdent = ident }; + } + + internal void RemoveIdent(string uid, string serverId) + { + if (cachedIdentities.ContainsKey(uid) && cachedIdentities[uid].ServerId == serverId) + { + cachedIdentities.TryRemove(uid, out _); + } + } + + internal int GetOnlineUsers(string serverId) + { + if (string.IsNullOrEmpty(serverId)) + return cachedIdentities.Count; + return cachedIdentities.Count(c => c.Value.ServerId == serverId); + } + + internal void ClearIdentsForServer(string serverId) + { + var serverIdentities = cachedIdentities.Where(i => i.Value.ServerId == serverId); + foreach (var identity in serverIdentities) + { + cachedIdentities.TryRemove(identity.Key, out _); + } + } +} + +internal record ServerIdentity +{ + public string ServerId { get; set; } = string.Empty; + public string CharacterIdent { get; set; } = string.Empty; +} diff --git a/MareSynchronosServer/MareSynchronosServices/Identity/IdentityService.cs b/MareSynchronosServer/MareSynchronosServices/Identity/IdentityService.cs new file mode 100644 index 0000000..54fefa4 --- /dev/null +++ b/MareSynchronosServer/MareSynchronosServices/Identity/IdentityService.cs @@ -0,0 +1,69 @@ +using Grpc.Core; +using MareSynchronosShared.Protos; +using Microsoft.Extensions.Logging; +using System.Threading.Tasks; + +namespace MareSynchronosServices.Identity; + +internal class IdentityService : IdentificationService.IdentificationServiceBase +{ + private readonly ILogger _logger; + private readonly IdentityHandler _handler; + + public IdentityService(ILogger logger, IdentityHandler handler) + { + _logger = logger; + _handler = handler; + } + + public override Task RemoveIdentForUid(RemoveIdentMessage request, ServerCallContext context) + { + _handler.RemoveIdent(request.Uid, request.ServerId); + return Task.FromResult(new Empty()); + } + + public override Task SetIdentForUid(SetIdentMessage request, ServerCallContext context) + { + _handler.SetIdent(request.Uid, request.ServerId, request.Ident); + return Task.FromResult(new Empty()); + } + + public override async Task GetIdentForUid(UidMessage request, ServerCallContext context) + { + var result = await _handler.GetIdentForuid(request.Uid); + return new CharacterIdentMessage() + { + Ident = result.CharacterIdent, + ServerId = result.ServerId + }; + } + + public override async Task GetUidForCharacterIdent(CharacterIdentMessage request, ServerCallContext context) + { + var result = await _handler.GetUidForCharacterIdent(request.Ident, request.ServerId); + return new UidMessage() + { + Uid = result + }; + } + + public override Task GetOnlineUserCount(ServerMessage request, ServerCallContext context) + { + return Task.FromResult(new OnlineUserCountResponse() { Count = _handler.GetOnlineUsers(request.ServerId) }); + } + + public override Task ClearIdentsForServer(ServerMessage request, ServerCallContext context) + { + _handler.ClearIdentsForServer(request.ServerId); + return Task.FromResult(new Empty()); + } + + public override Task RecreateServerIdents(ServerIdentMessage request, ServerCallContext context) + { + foreach (var identMsg in request.Idents) + { + _handler.SetIdent(identMsg.Uid, identMsg.ServerId, identMsg.Ident); + } + return Task.FromResult(new Empty()); + } +} \ No newline at end of file diff --git a/MareSynchronosServer/MareSynchronosServices/Startup.cs b/MareSynchronosServer/MareSynchronosServices/Startup.cs index a8aa0ae..5e18db4 100644 --- a/MareSynchronosServer/MareSynchronosServices/Startup.cs +++ b/MareSynchronosServer/MareSynchronosServices/Startup.cs @@ -10,7 +10,7 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Prometheus; using System.Collections.Generic; -using MareSynchronosShared.Services; +using MareSynchronosServices.Identity; namespace MareSynchronosServices; @@ -45,29 +45,12 @@ public class Startup })); services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.AddTransient(_ => Configuration); services.AddHostedService(provider => provider.GetService()); services.AddHostedService(); services.AddGrpc(); - - // add redis related options - var redis = Configuration.GetSection("MareSynchronos").GetValue("RedisConnectionString", string.Empty); - if (!string.IsNullOrEmpty(redis)) - { - services.AddStackExchangeRedisCache(opt => - { - opt.Configuration = redis; - opt.InstanceName = "MareSynchronosCache:"; - }); - services.AddSingleton(); - services.AddHostedService(p => p.GetService()); - } - else - { - services.AddSingleton(); - services.AddHostedService(p => p.GetService()); - } } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) @@ -80,6 +63,7 @@ public class Startup app.UseEndpoints(endpoints => { endpoints.MapGrpcService(); + endpoints.MapGrpcService(); }); } } \ No newline at end of file diff --git a/MareSynchronosServer/MareSynchronosShared/Protos/mareservices.proto b/MareSynchronosServer/MareSynchronosShared/Protos/mareservices.proto index 09d2f06..ad1c562 100644 --- a/MareSynchronosServer/MareSynchronosShared/Protos/mareservices.proto +++ b/MareSynchronosServer/MareSynchronosShared/Protos/mareservices.proto @@ -16,8 +16,50 @@ service FileService { rpc DeleteFiles (DeleteFilesRequest) returns (Empty); } +service IdentificationService { + rpc GetOnlineUserCount (ServerMessage) returns (OnlineUserCountResponse); + rpc RemoveIdentForUid (RemoveIdentMessage) returns (Empty); + rpc SetIdentForUid (SetIdentMessage) returns (Empty); + rpc GetUidForCharacterIdent (CharacterIdentMessage) returns (UidMessage); + rpc GetIdentForUid (UidMessage) returns (CharacterIdentMessage); + rpc ClearIdentsForServer (ServerMessage) returns (Empty); + rpc RecreateServerIdents (ServerIdentMessage) returns (Empty); +} + message Empty { } +message ServerIdentMessage { + repeated SetIdentMessage idents = 1; +} + +message ServerMessage { + string server_id = 1; +} + +message OnlineUserCountResponse { + int64 count = 1; +} + +message RemoveIdentMessage { + string uid = 1; + string server_id = 2; +} + +message SetIdentMessage { + string uid = 1; + string server_id = 2; + string ident = 3; +} + +message CharacterIdentMessage { + string server_id = 1; + string ident = 2; +} + +message UidMessage { + string uid = 1; +} + message UploadFileRequest { string hash = 1; string uploader = 2; diff --git a/MareSynchronosServer/MareSynchronosShared/Services/BaseClientIdentificationService.cs b/MareSynchronosServer/MareSynchronosShared/Services/BaseClientIdentificationService.cs deleted file mode 100644 index ec6ac87..0000000 --- a/MareSynchronosServer/MareSynchronosShared/Services/BaseClientIdentificationService.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System.Collections.Concurrent; -using MareSynchronosShared.Metrics; - -namespace MareSynchronosShared.Services; - -public abstract class BaseClientIdentificationService : IClientIdentificationService -{ - private readonly MareMetrics metrics; - protected ConcurrentDictionary OnlineClients = new(); - protected BaseClientIdentificationService(MareMetrics metrics) - { - this.metrics = metrics; - } - - public virtual Task GetOnlineUsers() - { - return Task.FromResult(OnlineClients.Count); - } - - public Task GetUidForCharacterIdent(string characterIdent) - { - var result = OnlineClients.SingleOrDefault(u => - string.Compare(u.Value, characterIdent, StringComparison.InvariantCultureIgnoreCase) == 0); - return Task.FromResult(result.Equals(new KeyValuePair()) ? null : result.Key); - } - - public virtual Task GetCharacterIdentForUid(string uid) - { - if (!OnlineClients.TryGetValue(uid, out var result)) - { - return Task.FromResult((string?)null); - } - - return Task.FromResult(result); - } - - public virtual Task MarkUserOnline(string uid, string charaIdent) - { - OnlineClients[uid] = charaIdent; - metrics.SetGaugeTo(MetricsAPI.GaugeAuthorizedConnections, OnlineClients.Count); - return Task.CompletedTask; - } - - public virtual Task MarkUserOffline(string uid) - { - if (OnlineClients.TryRemove(uid, out _)) - { - metrics.SetGaugeTo(MetricsAPI.GaugeAuthorizedConnections, OnlineClients.Count); - } - - return Task.CompletedTask; - } - - public Task StartAsync(CancellationToken cancellationToken) - { - return Task.CompletedTask; - } - - public virtual Task StopAsync(CancellationToken cancellationToken) - { - metrics.SetGaugeTo(MetricsAPI.GaugeAuthorizedConnections, 0); - OnlineClients = new(); - return Task.CompletedTask; - } -} \ No newline at end of file diff --git a/MareSynchronosServer/MareSynchronosShared/Services/DistributedClientIdentificationService.cs b/MareSynchronosServer/MareSynchronosShared/Services/DistributedClientIdentificationService.cs deleted file mode 100644 index 958e1e2..0000000 --- a/MareSynchronosServer/MareSynchronosShared/Services/DistributedClientIdentificationService.cs +++ /dev/null @@ -1,71 +0,0 @@ -using System.Text; -using MareSynchronosShared.Metrics; -using Microsoft.Extensions.Caching.Distributed; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; -using StackExchange.Redis; - -namespace MareSynchronosShared.Services; - -public class DistributedClientIdentificationService : BaseClientIdentificationService -{ - private readonly IDistributedCache distributedCache; - private readonly ILogger logger; - private readonly IConfiguration configuration; - private const string RedisPrefix = "uidcache:"; - - public DistributedClientIdentificationService(MareMetrics metrics, IDistributedCache distributedCache, IConfiguration configuration, ILogger logger) : base(metrics) - { - this.distributedCache = distributedCache; - this.logger = logger; - this.configuration = configuration.GetSection("MareSynchronos"); - } - - public override async Task GetOnlineUsers() - { - try - { - var redis = configuration.GetValue("RedisConnectionString"); - var conn = await ConnectionMultiplexer.ConnectAsync(redis).ConfigureAwait(false); - var endpoint = conn.GetEndPoints().First(); - return await conn.GetServer(endpoint).KeysAsync(pattern: "*" + RedisPrefix + "*").CountAsync().ConfigureAwait(false); - } - catch (Exception ex) - { - logger.LogError(ex, "Error during GetOnlineUsers"); - return 0; - } - } - - public override async Task GetCharacterIdentForUid(string uid) - { - var localIdent = await base.GetCharacterIdentForUid(uid).ConfigureAwait(false); - if (localIdent != null) return localIdent; - var cachedIdent = await distributedCache.GetStringAsync(RedisPrefix + uid).ConfigureAwait(false); - return cachedIdent ?? null; - } - - public override async Task MarkUserOffline(string uid) - { - await base.MarkUserOffline(uid).ConfigureAwait(false); - await distributedCache.RemoveAsync(RedisPrefix + uid).ConfigureAwait(false); - } - - public override async Task MarkUserOnline(string uid, string charaIdent) - { - await base.MarkUserOnline(uid, charaIdent).ConfigureAwait(false); - await distributedCache.SetAsync(RedisPrefix + uid, Encoding.UTF8.GetBytes(charaIdent), new DistributedCacheEntryOptions() - { - AbsoluteExpiration = DateTime.Now.AddDays(7) - }).ConfigureAwait(false); - } - - public override Task StopAsync(CancellationToken cancellationToken) - { - foreach (var uid in OnlineClients) - { - distributedCache.Remove(RedisPrefix + uid.Key); - } - return base.StopAsync(cancellationToken); - } -} \ No newline at end of file diff --git a/MareSynchronosServer/MareSynchronosShared/Services/IClientIdentificationService.cs b/MareSynchronosServer/MareSynchronosShared/Services/IClientIdentificationService.cs deleted file mode 100644 index 8f0b04d..0000000 --- a/MareSynchronosServer/MareSynchronosShared/Services/IClientIdentificationService.cs +++ /dev/null @@ -1,12 +0,0 @@ -using Microsoft.Extensions.Hosting; - -namespace MareSynchronosShared.Services; - -public interface IClientIdentificationService : IHostedService -{ - Task GetOnlineUsers(); - Task GetUidForCharacterIdent(string characterIdent); - Task GetCharacterIdentForUid(string uid); - Task MarkUserOnline(string uid, string charaIdent); - Task MarkUserOffline(string uid); -} \ No newline at end of file diff --git a/MareSynchronosServer/MareSynchronosShared/Services/LocalClientIdentificationService.cs b/MareSynchronosServer/MareSynchronosShared/Services/LocalClientIdentificationService.cs deleted file mode 100644 index 2572f55..0000000 --- a/MareSynchronosServer/MareSynchronosShared/Services/LocalClientIdentificationService.cs +++ /dev/null @@ -1,10 +0,0 @@ -using MareSynchronosShared.Metrics; - -namespace MareSynchronosShared.Services; - -public class LocalClientIdentificationService : BaseClientIdentificationService -{ - public LocalClientIdentificationService(MareMetrics metrics) : base(metrics) - { - } -} \ No newline at end of file