Добавил метод для получения нагрузки на процессор

This commit is contained in:
2026-06-28 21:34:58 +03:00
parent b53c690457
commit c4ead0d8de
12 changed files with 198 additions and 3 deletions
@@ -0,0 +1,7 @@
namespace MonitorAgent.API.Contracts;
public class CpuUsageResponse(decimal total, IEnumerable<decimal> cores)
{
public decimal Total { get; set; } = total;
public List<decimal> Cores { get; set; } = new([.. cores]);
}
@@ -1,11 +1,15 @@
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using MonitorAgent.API.Contracts; using MonitorAgent.API.Contracts;
using MonitorAgent.Application.Services;
using MonitorAgent.Core.Abstraction; using MonitorAgent.Core.Abstraction;
namespace MonitorAgent.API.Controllers; namespace MonitorAgent.API.Controllers;
[ApiController] [ApiController]
[Route("[controller]")] [Route("[controller]")]
public class MonitorController(IMonitorService monitorService) : ControllerBase public class MonitorController(
IMonitorService monitorService,
CpuMonitorBackgroundService cpuMonitorService)
: ControllerBase
{ {
[HttpGet("uptime")] [HttpGet("uptime")]
public async Task<ActionResult<decimal>> GetUptimeAsync(CancellationToken cancellationToken = default) public async Task<ActionResult<decimal>> GetUptimeAsync(CancellationToken cancellationToken = default)
@@ -30,9 +34,18 @@ public class MonitorController(IMonitorService monitorService) : ControllerBase
} }
[HttpGet("loadavg")] [HttpGet("loadavg")]
public async Task<ActionResult<LoadAverageResponse>> GetActionResultAsync(CancellationToken cancellationToken = default) public async Task<ActionResult<LoadAverageResponse>> GetLoadAverageAsync(CancellationToken cancellationToken = default)
{ {
var response = new LoadAverageResponse(await monitorService.GetLoadAverageAsync(cancellationToken)); var response = new LoadAverageResponse(await monitorService.GetLoadAverageAsync(cancellationToken));
return Ok(response); return Ok(response);
} }
[HttpGet("cpuUsage")]
public async Task<ActionResult<CpuUsageResponse>> GetCpuUsageAsync(CancellationToken cancellationToken = default)
{
var usage = await cpuMonitorService.GetCpuUsageAsync(cancellationToken);
var response = new CpuUsageResponse(usage.TotalUsagePercent, usage.CoresUsagePercent);
return Ok(response);
}
} }
@@ -1,5 +1,6 @@
using MonitorAgent.Core.Abstraction; using MonitorAgent.Core.Abstraction;
using MonitorAgent.Application.Services; using MonitorAgent.Application.Services;
using MonitorAgent.Application.Settings;
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
@@ -12,7 +13,12 @@ builder.Services.AddOpenApi();
builder.Services.AddEndpointsApiExplorer(); builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(); builder.Services.AddSwaggerGen();
builder.Services.Configure<CpuMonitorSettings>(builder.Configuration.GetSection("CpuMonitorSettings"));
builder.Services.AddScoped<IMonitorService, MonitorService>(); builder.Services.AddScoped<IMonitorService, MonitorService>();
builder.Services.AddSingleton<RawCpuMonitorService>();
builder.Services.AddSingleton<CpuMonitorBackgroundService>();
builder.Services.AddHostedService(provider => provider.GetRequiredService<CpuMonitorBackgroundService>());
var app = builder.Build(); var app = builder.Build();
@@ -5,5 +5,8 @@
"Microsoft.AspNetCore": "Warning" "Microsoft.AspNetCore": "Warning"
} }
}, },
"AllowedHosts": "*" "AllowedHosts": "*",
"CpuMonitorSettings": {
"DelayTime": 2000
}
} }
@@ -4,6 +4,10 @@
<ProjectReference Include="..\MonitorAgent.Core\MonitorAgent.Core.csproj" /> <ProjectReference Include="..\MonitorAgent.Core\MonitorAgent.Core.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.0" />
</ItemGroup>
<PropertyGroup> <PropertyGroup>
<TargetFramework>net10.0</TargetFramework> <TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
@@ -0,0 +1,64 @@
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using MonitorAgent.Application.Settings;
using MonitorAgent.Core.Models;
namespace MonitorAgent.Application.Services;
public class CpuMonitorBackgroundService(
RawCpuMonitorService cpuMonitorService,
IOptions<CpuMonitorSettings> options,
ILogger<CpuMonitorBackgroundService> logger) : BackgroundService
{
private RawCpuStatsCollection? _prevStats;
private RawCpuStatsCollection? _nextStats;
public async Task<CpuUsage> GetCpuUsageAsync(CancellationToken cancellationToken = default)
{
RawCpuStatsCollection prev, next;
if (_prevStats != null)
prev = _prevStats;
else
prev = await cpuMonitorService.ParseRawCpuStatsAsync(cancellationToken);
if (_nextStats != null)
next = _nextStats;
else
next = await cpuMonitorService.ParseRawCpuStatsAsync(cancellationToken);
var total = CalcUsage(prev.Total, next.Total);
var cores = new List<decimal>(prev.Cores.Count);
for (int i = 0; i < prev.Cores.Count; i++)
cores.Add(CalcUsage(prev.Cores[i], next.Cores[i]));
return new CpuUsage(total, cores);
}
private decimal CalcUsage(RawCpuStats prev, RawCpuStats next)
{
decimal deltaTotal = next.Total - prev.Total;
decimal deltaIdle = next.Idle - prev.Idle;
return (1 - deltaIdle / deltaTotal) * 100;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
try
{
_prevStats = _nextStats;
_nextStats = await cpuMonitorService.ParseRawCpuStatsAsync(stoppingToken);
}
catch (Exception e)
{
logger.LogError(e.Message);
}
finally
{
await Task.Delay(options.Value.DelayTime, stoppingToken);
}
}
}
}
@@ -0,0 +1,19 @@
using MonitorAgent.Core.Abstraction;
using MonitorAgent.Core.Models;
namespace MonitorAgent.Application.Services;
public class RawCpuMonitorService : ICpuMonitorService
{
public async Task<RawCpuStatsCollection> ParseRawCpuStatsAsync(CancellationToken cancellationToken = default)
{
var raw = await File.ReadAllLinesAsync("/proc/stat", cancellationToken);
var total = RawCpuStats.Parse(raw[0]);
var cores = new List<RawCpuStats>();
for (int i = 1; i < raw.Length && raw[i].StartsWith("cpu"); i++)
cores.Add(RawCpuStats.Parse(raw[i]));
return new RawCpuStatsCollection(total, cores);
}
}
@@ -0,0 +1,6 @@
namespace MonitorAgent.Application.Settings;
public class CpuMonitorSettings
{
public int DelayTime { get; set; } = 1000;
}
@@ -0,0 +1,8 @@
using MonitorAgent.Core.Models;
namespace MonitorAgent.Core.Abstraction;
public interface ICpuMonitorService
{
public Task<RawCpuStatsCollection> ParseRawCpuStatsAsync(CancellationToken cancellationToken = default);
}
@@ -0,0 +1,9 @@
using System.Collections.ObjectModel;
namespace MonitorAgent.Core.Models;
public class CpuUsage(decimal totalUsagePercent, IEnumerable<decimal> coresUsagePercent)
{
public readonly decimal TotalUsagePercent = totalUsagePercent;
public readonly ReadOnlyCollection<decimal> CoresUsagePercent = new([.. coresUsagePercent]);
}
@@ -0,0 +1,46 @@
namespace MonitorAgent.Core.Models;
public struct RawCpuStats
{
public long User { get; set; }
public long Nice { get; set; }
public long System { get; set; }
public long Idle { get; set; }
public long Iowait { get; set; }
public long Irq { get; set; }
public long Softirq { get; set; }
public long Steal { get; set; }
public long Guest { get; set; }
public long GuestNice { get; set; }
public readonly long Total => User + Nice + System + Idle + Irq + Softirq + Steal;
public readonly long TotalIdle => Idle + Iowait;
public static RawCpuStats Parse(string s)
{
var stats = s.Split();
if (!stats[0].StartsWith("cpu"))
throw new FormatException($"The input string '{s}' was not in a correct format.");
var longStats = new List<long>();
for (int i = 1; i < stats.Length; i++)
{
if (!string.IsNullOrEmpty(stats[i]))
longStats.Add(long.Parse(stats[i]));
}
return new RawCpuStats
{
User = longStats[0],
Nice = longStats[1],
System = longStats[2],
Idle = longStats[3],
Iowait = longStats[4],
Irq = longStats[5],
Softirq = longStats[6],
Steal = longStats[7],
Guest = longStats[8],
GuestNice = longStats[9]
};
}
}
@@ -0,0 +1,10 @@
using System.Collections;
using System.Collections.ObjectModel;
namespace MonitorAgent.Core.Models;
public class RawCpuStatsCollection(RawCpuStats total, IEnumerable<RawCpuStats> cores)
{
public readonly RawCpuStats Total = total;
public readonly ReadOnlyCollection<RawCpuStats> Cores = new([.. cores]);
}