From f307c9c486c9772b0b757e7b180bcd82a7b0aff7 Mon Sep 17 00:00:00 2001 From: Stanley Dimant Date: Wed, 6 Jul 2022 11:37:06 +0200 Subject: [PATCH] add SystemInfoService, increase API to 3 --- .../MareSynchronos.API/API.cs | 5 +- .../MareSynchronos.API/CharacterCacheDto.cs | 1 - .../MareSynchronos.API/SystemInfoDto.cs | 19 +++ .../Hubs/ConnectionHub.cs | 18 ++- .../MareSynchronosServer/Hubs/UserHub.cs | 26 ++-- .../MareSynchronosServer/Startup.cs | 2 + .../MareSynchronosServer/SystemInfoService.cs | 133 ++++++++++++++++++ 7 files changed, 184 insertions(+), 20 deletions(-) create mode 100644 MareSynchronosServer/MareSynchronos.API/SystemInfoDto.cs create mode 100644 MareSynchronosServer/MareSynchronosServer/SystemInfoService.cs diff --git a/MareSynchronosServer/MareSynchronos.API/API.cs b/MareSynchronosServer/MareSynchronos.API/API.cs index 990f9af..78e1a65 100644 --- a/MareSynchronosServer/MareSynchronos.API/API.cs +++ b/MareSynchronosServer/MareSynchronos.API/API.cs @@ -8,7 +8,7 @@ namespace MareSynchronos.API { public class API { - public const int Version = 2; + public const int Version = 3; } public class FilesHubAPI @@ -28,6 +28,8 @@ namespace MareSynchronos.API { public const string Path = "/heartbeat"; public const string InvokeHeartbeat = "Heartbeat"; + public const string InvokeGetSystemInfo = "GetSystemInfo"; + public const string OnUpdateSystemInfo = "OnUpdateSystemInfo"; } public class AdminHubAPI @@ -64,7 +66,6 @@ namespace MareSynchronos.API public const string InvokeGetPairedClients = "GetPairedClients"; public const string SendDeleteAccount = "DeleteAccount"; - public const string OnUsersOnline = "UsersOnline"; public const string OnUpdateClientPairs = "UpdateClientPairs"; public const string OnReceiveCharacterData = "ReceiveCharacterData"; public const string OnRemoveOnlinePairedPlayer = "RemoveOnlinePairedPlayer"; diff --git a/MareSynchronosServer/MareSynchronos.API/CharacterCacheDto.cs b/MareSynchronosServer/MareSynchronos.API/CharacterCacheDto.cs index 2873c32..ce13618 100644 --- a/MareSynchronosServer/MareSynchronos.API/CharacterCacheDto.cs +++ b/MareSynchronosServer/MareSynchronos.API/CharacterCacheDto.cs @@ -8,6 +8,5 @@ namespace MareSynchronos.API public string GlamourerData { get; set; } public string ManipulationData { get; set; } public string Hash { get; set; } - public int JobId { get; set; } } } diff --git a/MareSynchronosServer/MareSynchronos.API/SystemInfoDto.cs b/MareSynchronosServer/MareSynchronos.API/SystemInfoDto.cs new file mode 100644 index 0000000..2688193 --- /dev/null +++ b/MareSynchronosServer/MareSynchronos.API/SystemInfoDto.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MareSynchronos.API +{ + public record SystemInfoDto + { + public double CpuUsage { get; set; } + public long CacheUsage { get; set; } + public int UploadedFiles { get; set; } + public double NetworkIn { get; set; } + public double NetworkOut { get; set; } + public int OnlineUsers { get; set; } + public long RAMUsage { get; set; } + } +} diff --git a/MareSynchronosServer/MareSynchronosServer/Hubs/ConnectionHub.cs b/MareSynchronosServer/MareSynchronosServer/Hubs/ConnectionHub.cs index 5d175ce..5ee7e90 100644 --- a/MareSynchronosServer/MareSynchronosServer/Hubs/ConnectionHub.cs +++ b/MareSynchronosServer/MareSynchronosServer/Hubs/ConnectionHub.cs @@ -1,6 +1,8 @@ -using System.Linq; +using System; +using System.Linq; using System.Runtime.ConstrainedExecution; using System.Security.Claims; +using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; using MareSynchronos.API; using MareSynchronosServer.Data; @@ -12,15 +14,19 @@ namespace MareSynchronosServer.Hubs { public class ConnectionHub : BaseHub { - public ConnectionHub(MareDbContext mareDbContext, ILogger logger) : base(mareDbContext, logger) + private readonly SystemInfoService _systemInfoService; + + public ConnectionHub(MareDbContext mareDbContext, ILogger logger, SystemInfoService systemInfoService) : base(mareDbContext, logger) { + _systemInfoService = systemInfoService; } [HubMethodName(ConnectionHubAPI.InvokeHeartbeat)] public async Task Heartbeat() { var userId = Context.User!.Claims.SingleOrDefault(c => c.Type == ClaimTypes.NameIdentifier)?.Value; - + + await Clients.Caller.SendAsync(ConnectionHubAPI.OnUpdateSystemInfo, _systemInfoService.SystemInfoDto); if (userId != null) { @@ -37,5 +43,11 @@ namespace MareSynchronosServer.Hubs return new ConnectionDto(); } + + [HubMethodName(ConnectionHubAPI.InvokeGetSystemInfo)] + public async Task GetSystemInfo() + { + return _systemInfoService.SystemInfoDto; + } } } diff --git a/MareSynchronosServer/MareSynchronosServer/Hubs/UserHub.cs b/MareSynchronosServer/MareSynchronosServer/Hubs/UserHub.cs index 57b2393..95b83e6 100644 --- a/MareSynchronosServer/MareSynchronosServer/Hubs/UserHub.cs +++ b/MareSynchronosServer/MareSynchronosServer/Hubs/UserHub.cs @@ -65,8 +65,6 @@ namespace MareSynchronosServer.Hubs .Where(u => otherUsers.Any(e => e == u.User) && u.OtherUser == ownUser && !u.IsPaused).ToListAsync(); await Clients.Users(otherEntries.Select(e => e.User.UID)).SendAsync(UserHubAPI.OnAddOnlinePairedPlayer, ownUser.CharacterIdentification); - await Clients.All.SendAsync(UserHubAPI.OnUsersOnline, - await DbContext.Users.CountAsync(u => !string.IsNullOrEmpty(u.CharacterIdentification))); return otherEntries.Select(e => e.User.CharacterIdentification).Distinct().ToList(); } @@ -82,22 +80,22 @@ namespace MareSynchronosServer.Hubs { string userid = AuthenticatedUserId; var user = GetAuthenticatedUser(); - return DbContext.ClientPairs + var pairs = await DbContext.ClientPairs .Include(u => u.OtherUser) .Include(u => u.User) .Where(w => w.User.UID == userid) - .ToList() - .Select(w => + .ToListAsync(); + return pairs.Select(w => + { + var otherEntry = OppositeEntry(w.OtherUser.UID); + return new ClientPairDto { - var otherEntry = OppositeEntry(w.OtherUser.UID); - return new ClientPairDto - { - IsPaused = w.IsPaused, - OtherUID = w.OtherUser.UID, - IsSynced = otherEntry != null, - IsPausedFromOthers = otherEntry?.IsPaused ?? false, - }; - }).ToList(); + IsPaused = w.IsPaused, + OtherUID = w.OtherUser.UID, + IsSynced = otherEntry != null, + IsPausedFromOthers = otherEntry?.IsPaused ?? false, + }; + }).ToList(); } public override async Task OnDisconnectedAsync(Exception exception) diff --git a/MareSynchronosServer/MareSynchronosServer/Startup.cs b/MareSynchronosServer/MareSynchronosServer/Startup.cs index 50dee85..07fbcc0 100644 --- a/MareSynchronosServer/MareSynchronosServer/Startup.cs +++ b/MareSynchronosServer/MareSynchronosServer/Startup.cs @@ -38,6 +38,7 @@ namespace MareSynchronosServer hubOptions.StreamBufferCapacity = 200; }); + services.AddSingleton(); services.AddSingleton(); services.AddDbContext(options => @@ -46,6 +47,7 @@ namespace MareSynchronosServer }); services.AddHostedService(); + services.AddHostedService(provider => provider.GetService()); services.AddDatabaseDeveloperPageExceptionFilter(); services.AddAuthentication(options => diff --git a/MareSynchronosServer/MareSynchronosServer/SystemInfoService.cs b/MareSynchronosServer/MareSynchronosServer/SystemInfoService.cs new file mode 100644 index 0000000..ff11402 --- /dev/null +++ b/MareSynchronosServer/MareSynchronosServer/SystemInfoService.cs @@ -0,0 +1,133 @@ +using System; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Net.NetworkInformation; +using System.Threading; +using System.Threading.Tasks; +using MareSynchronos.API; +using MareSynchronosServer.Data; +using MareSynchronosServer.Hubs; +using Microsoft.AspNetCore.SignalR; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace MareSynchronosServer; + +public class SystemInfoService : IHostedService, IDisposable +{ + private readonly ILogger _logger; + private readonly IServiceProvider _services; + private readonly IConfiguration _configuration; + private readonly IHubContext _hubContext; + private Timer _timer; + public SystemInfoDto SystemInfoDto { get; private set; } = new(); + + public SystemInfoService(ILogger logger, IServiceProvider services, + IConfiguration configuration, IHubContext hubContext) + { + _logger = logger; + _services = services; + _configuration = configuration; + _hubContext = hubContext; + } + + public Task StartAsync(CancellationToken cancellationToken) + { + _logger.LogInformation("System Info Service started"); + + _timer = new Timer(CalculateCpuUsage, null, TimeSpan.Zero, TimeSpan.FromSeconds(5)); + + return Task.CompletedTask; + } + + private void CalculateCpuUsage(object state) + { + var startTime = DateTime.UtcNow; + double startCpuUsage = 0; + foreach (var process in Process.GetProcesses()) + { + try + { + startCpuUsage += process.TotalProcessorTime.TotalMilliseconds; + } + catch { } + } + var networkOut = NetworkInterface.GetAllNetworkInterfaces().Sum(n => n.GetIPStatistics().BytesSent); + var networkIn = NetworkInterface.GetAllNetworkInterfaces().Sum(n => n.GetIPStatistics().BytesReceived); + var stopWatch = new Stopwatch(); + stopWatch.Start(); + + Thread.Sleep(TimeSpan.FromSeconds(5)); + + stopWatch.Stop(); + var endTime = DateTime.UtcNow; + double endCpuUsage = 0; + long ramUsage = 0; + foreach (var process in Process.GetProcesses()) + { + try + { + endCpuUsage += process.TotalProcessorTime.TotalMilliseconds; + ramUsage += process.WorkingSet64; + } + catch { } + } + var endNetworkOut = NetworkInterface.GetAllNetworkInterfaces().Sum(n => n.GetIPStatistics().BytesSent); + var endNetworkIn = NetworkInterface.GetAllNetworkInterfaces().Sum(n => n.GetIPStatistics().BytesReceived); + + var totalMsPassed = (endTime - startTime).TotalMilliseconds; + var totalSPassed = (endTime - startTime).TotalSeconds; + + var cpuUsedMs = (endCpuUsage - startCpuUsage); + var cpuUsageTotal = cpuUsedMs / (Environment.ProcessorCount * totalMsPassed); + var bytesSent = endNetworkOut - networkOut; + var bytesReceived = endNetworkIn - networkIn; + + using var scope = _services.CreateScope(); + var dbContext = scope.ServiceProvider.GetService()!; + + int uploadedFiles = 0; + var loggedInUsers = dbContext.Users.Count(u => !string.IsNullOrEmpty(u.CharacterIdentification)); + var localCacheSize = Directory.EnumerateFiles(_configuration["CacheDirectory"]) + .Sum(f => + { + uploadedFiles++; + return new FileInfo(f).Length; + }); + + var totalNetworkOut = bytesSent / totalSPassed; + var totalNetworkIn = bytesReceived / totalSPassed; + var cpuUsage = cpuUsageTotal * 100; + var usedRAM = Process.GetCurrentProcess().WorkingSet64 + Process.GetProcessesByName("sqlservr").FirstOrDefault()?.WorkingSet64 ?? 0; + + SystemInfoDto = new SystemInfoDto() + { + CacheUsage = localCacheSize, + CpuUsage = cpuUsage, + RAMUsage = usedRAM, + NetworkIn = totalNetworkIn, + NetworkOut = totalNetworkOut, + OnlineUsers = loggedInUsers, + UploadedFiles = uploadedFiles + }; + + _hubContext.Clients.All.SendAsync(ConnectionHubAPI.OnUpdateSystemInfo, SystemInfoDto); + + _logger.LogInformation($"CPU:{cpuUsage:0.00}%, RAM Used:{(double)usedRAM / 1024 / 1024 / 1024:0.00}GB, Cache:{(double)localCacheSize / 1024 / 1024 / 1024:0.00}GB, Users:{loggedInUsers}, NetworkIn:{totalNetworkIn / 1024 / 1024:0.00}MB/s, NetworkOut:{totalNetworkOut / 1024 / 1024:0.00}MB/s"); + } + + public Task StopAsync(CancellationToken cancellationToken) + { + _timer?.Change(Timeout.Infinite, 0); + + return Task.CompletedTask; + } + + public void Dispose() + { + _timer?.Dispose(); + } +} \ No newline at end of file