Initial_commit_SecMPS_v2
This commit is contained in:
451
api_sqlsugar/VolPro.Core/PerformanceMonitor/ComputerHelper.cs
Normal file
451
api_sqlsugar/VolPro.Core/PerformanceMonitor/ComputerHelper.cs
Normal file
@@ -0,0 +1,451 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using Newtonsoft.Json;
|
||||
using VolPro.Core.PerformanceMonitor;
|
||||
using VolPro.Core.Services;
|
||||
|
||||
namespace VolPro.Core.PerformanceMonitor
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 获取服务器参数
|
||||
/// gaobin
|
||||
/// </summary>
|
||||
public static class ComputerHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 内存使用情况
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static MemoryMetrics GetComputerInfo()
|
||||
{
|
||||
try
|
||||
{
|
||||
MemoryMetricsClient client = new();
|
||||
MemoryMetrics memoryMetrics = IsUnix() ? client.GetUnixMetrics() : client.GetWindowsMetrics();
|
||||
|
||||
//剩余内存
|
||||
memoryMetrics.FreeRam = Math.Round(memoryMetrics.Free / 1024, 2) + "GB";
|
||||
//已用内存
|
||||
memoryMetrics.UsedRam = Math.Round(memoryMetrics.Used / 1024, 2) + "GB";
|
||||
//总内存
|
||||
memoryMetrics.TotalRAM = Math.Round(memoryMetrics.Total / 1024, 2) + "GB";
|
||||
//内存使用率
|
||||
memoryMetrics.RAMRate = Math.Ceiling(100 * memoryMetrics.Used / memoryMetrics.Total).ToString() + "%";
|
||||
//cpu使用率
|
||||
// double cpurate = Math.Ceiling(GetCPURate());
|
||||
memoryMetrics.CPURate = GetCPURate();
|
||||
|
||||
//cpu数量
|
||||
//memoryMetrics.Total = client.total
|
||||
|
||||
|
||||
return memoryMetrics;
|
||||
}
|
||||
catch { }
|
||||
//catch (Exception ex)
|
||||
//{
|
||||
// Console.WriteLine("获取内存使用出错,msg=" + ex.Message + "," + ex.StackTrace);
|
||||
//}
|
||||
return new MemoryMetrics();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取内存大小
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static List<DiskInfo> GetDiskInfos()
|
||||
{
|
||||
List<DiskInfo> diskInfos = new();
|
||||
|
||||
if (IsUnix())
|
||||
{
|
||||
try
|
||||
{
|
||||
string output = ShellHelper.Bash("df -m / | awk '{print $2,$3,$4,$5,$6}'");
|
||||
var arr = output.Split('\n', StringSplitOptions.RemoveEmptyEntries);
|
||||
if (arr.Length == 0) return diskInfos;
|
||||
|
||||
var rootDisk = arr[1].Split(' ', (char)StringSplitOptions.RemoveEmptyEntries);
|
||||
if (rootDisk == null || rootDisk.Length == 0)
|
||||
{
|
||||
return diskInfos;
|
||||
}
|
||||
DiskInfo diskInfo = new()
|
||||
{
|
||||
DiskName = "/",
|
||||
TotalSize = long.Parse(rootDisk[0]) / 1024,
|
||||
Used = long.Parse(rootDisk[1]) / 1024,
|
||||
AvailableFreeSpace = long.Parse(rootDisk[2]) / 1024,
|
||||
AvailablePercent = decimal.Parse(rootDisk[3].Replace("%", ""))
|
||||
};
|
||||
diskInfos.Add(diskInfo);
|
||||
}
|
||||
catch { }
|
||||
//catch (Exception ex)
|
||||
//{
|
||||
// //Console.WriteLine("获取磁盘信息出错了" + ex.Message);
|
||||
//}
|
||||
}
|
||||
else
|
||||
{
|
||||
var driv = DriveInfo.GetDrives();
|
||||
foreach (var item in driv)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!item.IsReady || (item.DriveType != DriveType.Fixed && item.DriveType != DriveType.Removable))
|
||||
{
|
||||
continue; // 跳过未就绪/非存储类磁盘
|
||||
}
|
||||
var obj = new DiskInfo()
|
||||
{
|
||||
DiskName = item.Name,
|
||||
TypeName = item.DriveType.ToString(),
|
||||
TotalSize = item.TotalSize / 1024 / 1024 / 1024,
|
||||
AvailableFreeSpace = item.AvailableFreeSpace / 1024 / 1024 / 1024,
|
||||
};
|
||||
obj.Used = obj.TotalSize - obj.AvailableFreeSpace;
|
||||
obj.AvailablePercent = decimal.Ceiling(obj.Used / (decimal)obj.TotalSize * 100);
|
||||
diskInfos.Add(obj);
|
||||
}
|
||||
catch { }
|
||||
//catch (Exception ex)
|
||||
//{
|
||||
// // Console.WriteLine("获取磁盘信息出错了" + ex.Message);
|
||||
// continue;
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
return diskInfos;
|
||||
}
|
||||
|
||||
public static bool IsUnix()
|
||||
{
|
||||
var isUnix = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) || RuntimeInformation.IsOSPlatform(OSPlatform.Linux);
|
||||
return isUnix;
|
||||
}
|
||||
/// <summary>
|
||||
/// iis部署存在权限问题获取不到CPU,
|
||||
/// 打开 Internet Information Services (IIS) 管理器
|
||||
///在左侧导航栏中展开服务器名称,点击 应用程序池
|
||||
///找到你的应用程序使用的应用池,右键点击并选择 高级设置
|
||||
///在 [进程模型] -> [标识] 属性,默认为 ApplicationPoolIdentity(改为LocalSystem)
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static string GetCPURate()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Console.WriteLine("开始获取CPU使用率...");
|
||||
if (IsUnix())
|
||||
{
|
||||
string output = ShellHelper.Bash("top -b -n1 | grep \"Cpu(s)\" | awk '{print $2 + $4}'");
|
||||
string result = output.Trim();
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 只在Windows平台上使用Windows特定的方法
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
// Console.WriteLine("尝试方法1:性能计数器(增加等待时间)");
|
||||
double rate = GetCpuUsageUsingPerformanceCounterWithLongerWait();
|
||||
// Console.WriteLine("性能计数器返回值:" + rate);
|
||||
if (rate > 0 && rate <= 100)
|
||||
{
|
||||
string cpuRate = rate.ToString("F1"); // 保留一位小数
|
||||
return cpuRate;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.AddAsync("获取CPU使用率出错:" + ex.Message);
|
||||
Console.WriteLine("获取CPU使用率出错:" + ex.Message);
|
||||
Console.WriteLine("堆栈跟踪:" + ex.StackTrace);
|
||||
}
|
||||
//Console.WriteLine("返回默认CPU使用率:0.0");
|
||||
return "0.0";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 使用性能计数器获取CPU使用率(增加等待时间)
|
||||
/// </summary>
|
||||
/// <returns>CPU使用率百分比</returns>
|
||||
private static double GetCpuUsageUsingPerformanceCounterWithLongerWait()
|
||||
{
|
||||
// 确保只在Windows平台上执行
|
||||
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
using (var cpuCounter = new System.Diagnostics.PerformanceCounter(
|
||||
"Processor", "% Processor Time", "_Total", true))
|
||||
{
|
||||
try
|
||||
{
|
||||
// 第一次获取是初始值
|
||||
double initialValue = cpuCounter.NextValue();
|
||||
// Console.WriteLine("性能计数器初始值:" + initialValue);
|
||||
|
||||
// 增加等待时间以获得更准确的值
|
||||
System.Threading.Thread.Sleep(200); // 增加到500毫秒
|
||||
|
||||
// 第二次获取实际值
|
||||
double value = cpuCounter.NextValue();
|
||||
// Console.WriteLine("性能计数器实际值:" + value);
|
||||
|
||||
// 确保返回值在合理范围内
|
||||
if (value < 0) value = 0;
|
||||
if (value > 100) value = 100;
|
||||
return value;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("性能计数器操作失败:" + ex.Message);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取系统运行时间
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static string GetRunTime()
|
||||
{
|
||||
string runTime = string.Empty;
|
||||
try
|
||||
{
|
||||
if (IsUnix())
|
||||
{
|
||||
string output = ShellHelper.Bash("uptime -s").Trim();
|
||||
runTime = FormatTime(ParseToLong((DateTime.Now - ParseToDateTime(output)).TotalMilliseconds.ToString().Split('.')[0]));
|
||||
}
|
||||
else
|
||||
{
|
||||
string output = ShellHelper.Cmd("wmic", "OS get LastBootUpTime/Value");
|
||||
string[] outputArr = output.Split('=', (char)StringSplitOptions.RemoveEmptyEntries);
|
||||
if (outputArr.Length == 2)
|
||||
{
|
||||
runTime = FormatTime(ParseToLong((DateTime.Now - ParseToDateTime(outputArr[1].Split('.')[0])).TotalMilliseconds.ToString().Split('.')[0]));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine("获取runTime出错" + ex.Message);
|
||||
}
|
||||
return runTime;
|
||||
}
|
||||
|
||||
private static string FormatTime(long value)
|
||||
{
|
||||
return "2020-01-01";
|
||||
}
|
||||
public static long ParseToLong(string str, long defaultValue)
|
||||
{
|
||||
try
|
||||
{
|
||||
return long.Parse(str);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return defaultValue;
|
||||
}
|
||||
}
|
||||
public static long ParseToLong(this object obj)
|
||||
{
|
||||
try
|
||||
{
|
||||
return long.Parse(obj.ToString());
|
||||
}
|
||||
catch
|
||||
{
|
||||
return 0L;
|
||||
}
|
||||
}
|
||||
|
||||
public static DateTime ParseToDateTime(string str)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(str))
|
||||
{
|
||||
return DateTime.MinValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
int length = str.Length;
|
||||
switch (length)
|
||||
{
|
||||
case 4:
|
||||
return DateTime.ParseExact(str, "yyyy", System.Globalization.CultureInfo.CurrentCulture);
|
||||
case 6:
|
||||
return DateTime.ParseExact(str, "yyyyMM", System.Globalization.CultureInfo.CurrentCulture);
|
||||
case 8:
|
||||
return DateTime.ParseExact(str, "yyyyMMdd", System.Globalization.CultureInfo.CurrentCulture);
|
||||
case 10:
|
||||
return DateTime.ParseExact(str, "yyyyMMddHH", System.Globalization.CultureInfo.CurrentCulture);
|
||||
case 12:
|
||||
return DateTime.ParseExact(str, "yyyyMMddHHmm", System.Globalization.CultureInfo.CurrentCulture);
|
||||
case 14:
|
||||
return DateTime.ParseExact(str, "yyyyMMddHHmmss", System.Globalization.CultureInfo.CurrentCulture);
|
||||
default:
|
||||
return DateTime.ParseExact(str, "yyyyMMddHHmmss", System.Globalization.CultureInfo.CurrentCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return DateTime.MinValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 内存信息
|
||||
/// </summary>
|
||||
public class MemoryMetrics
|
||||
{
|
||||
/// <summary>
|
||||
/// CPU逻辑处理器数量
|
||||
/// </summary>
|
||||
public double CPUTotal { get; set; }
|
||||
/// <summary>
|
||||
/// CPU核数
|
||||
/// </summary>
|
||||
public double CPUNum { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 内存大小
|
||||
/// </summary>
|
||||
[JsonIgnore]
|
||||
public double Total { get; set; }
|
||||
//[JsonIgnore]
|
||||
public double Used { get; set; }
|
||||
//[JsonIgnore]
|
||||
public double Free { get; set; }
|
||||
|
||||
public string UsedRam { get; set; }
|
||||
/// <summary>
|
||||
/// CPU使用率%
|
||||
/// </summary>
|
||||
public string CPURate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 总内存 GB
|
||||
/// </summary>
|
||||
public string TotalRAM { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 内存使用率 %
|
||||
/// </summary>
|
||||
public string RAMRate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 空闲内存
|
||||
/// </summary>
|
||||
public string FreeRam { get; set; }
|
||||
}
|
||||
|
||||
public class DiskInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// 磁盘名
|
||||
/// </summary>
|
||||
public string DiskName { get; set; }
|
||||
public string TypeName { get; set; }
|
||||
public long TotalFree { get; set; }
|
||||
public long TotalSize { get; set; }
|
||||
/// <summary>
|
||||
/// 已使用
|
||||
/// </summary>
|
||||
public long Used { get; set; }
|
||||
/// <summary>
|
||||
/// 可使用
|
||||
/// </summary>
|
||||
public long AvailableFreeSpace { get; set; }
|
||||
public decimal AvailablePercent { get; set; }
|
||||
}
|
||||
|
||||
public class MemoryMetricsClient
|
||||
{
|
||||
#region 获取内存信息
|
||||
|
||||
public MemoryMetrics GetWindowsMetrics()
|
||||
{
|
||||
var metrics = new MemoryMetrics();
|
||||
metrics.CPUTotal = Environment.ProcessorCount;
|
||||
|
||||
MEMORYSTATUSEX memStatus = new MEMORYSTATUSEX();
|
||||
if (GlobalMemoryStatusEx(memStatus))
|
||||
{
|
||||
// ullTotalPhys: 总物理内存(字节)
|
||||
// ullAvailPhys: 可用物理内存(字节)
|
||||
metrics.Total = Math.Round((double)memStatus.ullTotalPhys / (1024 * 1024), 0);
|
||||
metrics.Free = Math.Round((double)memStatus.ullAvailPhys / (1024 * 1024), 0);
|
||||
metrics.Used = metrics.Total - metrics.Free; // 系统已用内存 = 总内存 - 可用内存
|
||||
}
|
||||
|
||||
return metrics;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public class MEMORYSTATUSEX
|
||||
{
|
||||
public uint dwLength; // 结构体大小
|
||||
public uint dwMemoryLoad; // 内存使用率(0-100)
|
||||
public ulong ullTotalPhys; // 总物理内存(字节)
|
||||
public ulong ullAvailPhys; // 可用物理内存(字节)
|
||||
public ulong ullTotalPageFile; // 总页面文件大小(字节)
|
||||
public ulong ullAvailPageFile; // 可用页面文件大小(字节)
|
||||
public ulong ullTotalVirtual; // 总虚拟内存(字节)
|
||||
public ulong ullAvailVirtual; // 可用虚拟内存(字节)
|
||||
public ulong ullAvailExtendedVirtual; // 可用扩展虚拟内存(字节)
|
||||
|
||||
public MEMORYSTATUSEX()
|
||||
{
|
||||
this.dwLength = (uint)Marshal.SizeOf(typeof(MEMORYSTATUSEX));
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport("kernel32.dll")]
|
||||
public static extern bool GlobalMemoryStatusEx([In, Out] MEMORYSTATUSEX lpBuffer);
|
||||
|
||||
/// <summary>
|
||||
/// Unix系统获取
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public MemoryMetrics GetUnixMetrics()
|
||||
{
|
||||
string output = ShellHelper.Bash("free -m | awk '{print $2,$3,$4,$5,$6}'");
|
||||
var metrics = new MemoryMetrics();
|
||||
var lines = output.Split('\n', (char)StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
if (lines.Length <= 0) return metrics;
|
||||
|
||||
if (lines != null && lines.Length > 0)
|
||||
{
|
||||
var memory = lines[1].Split(' ', (char)StringSplitOptions.RemoveEmptyEntries);
|
||||
if (memory.Length >= 3)
|
||||
{
|
||||
metrics.Total = double.Parse(memory[0]);
|
||||
metrics.Used = double.Parse(memory[1]);
|
||||
metrics.Free = double.Parse(memory[2]);//m
|
||||
}
|
||||
}
|
||||
//cpu逻辑处理器数量
|
||||
metrics.CPUTotal = System.Environment.ProcessorCount;
|
||||
return metrics;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace VolPro.Core.PerformanceMonitor
|
||||
{
|
||||
/// <summary>
|
||||
/// 性能监控广播器接口
|
||||
/// </summary>
|
||||
public interface IPerformanceMonitorBroadcaster
|
||||
{
|
||||
/// <summary>
|
||||
/// 广播性能监控数据给所有客户端
|
||||
/// </summary>
|
||||
Task BroadcastPerformanceMetricsAsync();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using VolPro.Entity.DomainModels;
|
||||
|
||||
namespace VolPro.Core.PerformanceMonitor
|
||||
{
|
||||
/// <summary>
|
||||
/// 性能监控服务接口
|
||||
/// </summary>
|
||||
public interface IPerformanceMonitorService
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取当前性能指标
|
||||
/// </summary>
|
||||
/// <param name="cancellationToken">取消令牌</param>
|
||||
/// <returns>性能指标数据</returns>
|
||||
Task<object> GetCurrentMetricsAsync(CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 获取历史性能数据
|
||||
/// </summary>
|
||||
/// <param name="from">开始时间</param>
|
||||
/// <param name="to">结束时间</param>
|
||||
/// <returns>历史性能数据</returns>
|
||||
Task<object> GetHistoricalMetricsAsync(DateTime from, DateTime to);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using VolPro.Core.EFDbContext;
|
||||
using VolPro.Core.Log;
|
||||
using VolPro.Core.Services;
|
||||
using VolPro.Core.Utilities;
|
||||
using VolPro.Entity.DomainModels;
|
||||
|
||||
namespace VolPro.Core.PerformanceMonitor
|
||||
{
|
||||
/// <summary>
|
||||
/// 服务器监控后台服务,定时采集服务器信息并写入 Sys_Monitor 表
|
||||
/// </summary>
|
||||
public class MonitorBackgroundService : BackgroundService
|
||||
{
|
||||
private readonly IServiceScopeFactory _scopeFactory;
|
||||
private readonly ILogger<MonitorBackgroundService> _logger;
|
||||
private readonly IPerformanceMonitorBroadcaster _broadcaster;
|
||||
/// <summary>采集间隔(毫秒),默认 20 秒</summary>
|
||||
private const int IntervalMs = 20_000;
|
||||
|
||||
// 缓存的服务器信息
|
||||
private static string _cachedHostName;
|
||||
private static string _cachedIp;
|
||||
private static string _cachedMac;
|
||||
|
||||
public MonitorBackgroundService(
|
||||
IServiceScopeFactory scopeFactory,
|
||||
ILogger<MonitorBackgroundService> logger,
|
||||
IPerformanceMonitorBroadcaster broadcaster = null)
|
||||
{
|
||||
_scopeFactory = scopeFactory;
|
||||
_logger = logger;
|
||||
_broadcaster = broadcaster; // 可以为null,如果没有注册广播器
|
||||
|
||||
// 初始化时缓存服务器信息
|
||||
CacheServerInfo();
|
||||
}
|
||||
/// <summary>
|
||||
/// 缓存服务器信息(主机名、IP、MAC地址)
|
||||
/// </summary>
|
||||
private void CacheServerInfo()
|
||||
{
|
||||
try
|
||||
{
|
||||
var (hostName, ip, mac) = GetHostAndNetwork();
|
||||
_cachedHostName = hostName ?? "Unknown";
|
||||
_cachedIp = ip ?? "127.0.0.1";
|
||||
_cachedMac = mac ?? "-";
|
||||
|
||||
//_logger.LogInformation($"服务器信息已缓存 - 主机名: {_cachedHostName}, IP: {_cachedIp}, MAC: {_cachedMac}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "缓存服务器信息失败");
|
||||
_cachedHostName = "Unknown";
|
||||
_cachedIp = "127.0.0.1";
|
||||
_cachedMac = "-";
|
||||
}
|
||||
}
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
string msg = $"服务性能监控启动,采集间隔:{IntervalMs / 1000} 秒";
|
||||
Console.Write(msg);
|
||||
Logger.AddAsync(msg);
|
||||
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
await CollectAndSaveAsync(stoppingToken);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
break;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "采集或保存服务器监控数据失败");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await Task.Delay(IntervalMs, stoppingToken);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
Logger.AddAsync("MonitorBackgroundService 已停止");
|
||||
}
|
||||
|
||||
private async Task CollectAndSaveAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
using var scope = _scopeFactory.CreateScope();
|
||||
var db = scope.ServiceProvider.GetRequiredService<SysDbContext>();
|
||||
|
||||
// 使用缓存的服务器信息
|
||||
var hostName = _cachedHostName;
|
||||
var ip = _cachedIp;
|
||||
var mac = _cachedMac;
|
||||
|
||||
var metrics = ComputerHelper.GetComputerInfo();
|
||||
var disks = ComputerHelper.GetDiskInfos();
|
||||
|
||||
decimal? cpuUsage = null;
|
||||
if (!string.IsNullOrWhiteSpace(metrics?.CPURate) && double.TryParse(metrics.CPURate.TrimEnd('%'), out var cpuVal))
|
||||
cpuUsage = (decimal)Math.Min(100, Math.Max(0, cpuVal));
|
||||
|
||||
decimal? memoryUsage = null;
|
||||
if (!string.IsNullOrWhiteSpace(metrics?.RAMRate) && double.TryParse(metrics.RAMRate.TrimEnd('%'), out var memVal))
|
||||
memoryUsage = (decimal)Math.Min(100, Math.Max(0, memVal));
|
||||
|
||||
string diskInfoJson = null;
|
||||
if (disks != null && disks.Count > 0)
|
||||
{
|
||||
var diskList = disks.Select(d => new
|
||||
{
|
||||
d.DiskName,
|
||||
d.TypeName,
|
||||
d.TotalSize,
|
||||
d.Used,
|
||||
d.AvailableFreeSpace,
|
||||
d.AvailablePercent
|
||||
}).ToList();
|
||||
diskInfoJson = JsonConvert.SerializeObject(diskList);
|
||||
}
|
||||
var record = new Sys_Monitor
|
||||
{
|
||||
Id = new IdWorker().NextId(),
|
||||
HostName = hostName ?? "Unknown",
|
||||
Ip = ip ?? "127.0.0.1",
|
||||
MacAddress = mac ?? "-",
|
||||
CpuUsage = cpuUsage,
|
||||
MemoryUsage = memoryUsage,
|
||||
BandwidthUsage = null, // 需基于流量统计,此处暂不实现
|
||||
DiskInfo = diskInfoJson,
|
||||
RecordTime = DateTime.Now
|
||||
};
|
||||
await db.SqlSugarClient.Insertable(record).ExecuteCommandAsync();
|
||||
//db.Set<Sys_Monitor>().add(record);
|
||||
//await db.SaveChangesAsync(cancellationToken);
|
||||
|
||||
//// 数据保存成功后,通过广播器推送给所有连接的客户端
|
||||
//if (_broadcaster != null)
|
||||
//{
|
||||
// try
|
||||
// {
|
||||
// await _broadcaster.BroadcastPerformanceMetricsAsync();
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// _logger.LogError(ex, "广播性能监控数据失败");
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
||||
private static (string hostName, string ip, string mac) GetHostAndNetwork()
|
||||
{
|
||||
var hostName = Environment.MachineName;
|
||||
if (string.IsNullOrWhiteSpace(hostName))
|
||||
{
|
||||
try { hostName = Dns.GetHostName(); } catch { hostName = "Unknown"; }
|
||||
}
|
||||
|
||||
string ip = "127.0.0.1";
|
||||
string mac = "-";
|
||||
|
||||
try
|
||||
{
|
||||
var ni = NetworkInterface.GetAllNetworkInterfaces()
|
||||
.FirstOrDefault(n =>
|
||||
n.OperationalStatus == OperationalStatus.Up
|
||||
&& n.NetworkInterfaceType != NetworkInterfaceType.Loopback
|
||||
&& n.NetworkInterfaceType != NetworkInterfaceType.Tunnel);
|
||||
|
||||
if (ni != null)
|
||||
{
|
||||
var addr = ni.GetIPProperties().UnicastAddresses
|
||||
.FirstOrDefault(a => a.Address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork);
|
||||
if (addr != null)
|
||||
ip = addr.Address.ToString();
|
||||
|
||||
var raw = ni.GetPhysicalAddress()?.GetAddressBytes();
|
||||
if (raw != null && raw.Length > 0)
|
||||
mac = string.Join("-", raw.Select(b => b.ToString("X2")));
|
||||
}
|
||||
}
|
||||
catch { /* 忽略网络信息获取异常 */ }
|
||||
|
||||
return (hostName, ip, mac);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using VolPro.Core.Log;
|
||||
using VolPro.Core.PerformanceMonitor;
|
||||
|
||||
namespace VolPro.Core.PerformanceMonitor
|
||||
{
|
||||
public static class MonitorExtensions
|
||||
{
|
||||
public static IServiceCollection AddMonitor(this IServiceCollection services, WebApplicationBuilder builder)
|
||||
{
|
||||
|
||||
// 注册性能监控广播器
|
||||
services.AddSingleton<IPerformanceMonitorBroadcaster, PerformanceMonitorBroadcaster>();
|
||||
// 注册性能监控服务
|
||||
services.AddScoped<IPerformanceMonitorService, PerformanceMonitorService>();
|
||||
// builder.Configuration
|
||||
|
||||
// Console.WriteLine("mm" + builder.Configuration["montior"]);
|
||||
//发布后才会启动服务监听 !builder.Environment.IsDevelopment() ||
|
||||
if (builder.Configuration["montior"] == "1")
|
||||
{
|
||||
services.AddHostedService<MonitorBackgroundService>();
|
||||
}
|
||||
// services.AddHostedService<MonitorBackgroundService>();
|
||||
return services;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Threading.Tasks;
|
||||
using VolPro.Core.Services;
|
||||
|
||||
namespace VolPro.Core.PerformanceMonitor
|
||||
{
|
||||
/// <summary>
|
||||
/// 性能监控广播器实现
|
||||
/// </summary>
|
||||
public class PerformanceMonitorBroadcaster : IPerformanceMonitorBroadcaster
|
||||
{
|
||||
private readonly IServiceScopeFactory _scopeFactory;
|
||||
private readonly ILogger<PerformanceMonitorBroadcaster> _logger;
|
||||
private readonly IHubContext<PerformanceMonitorSignalR> _hubContext;
|
||||
|
||||
public PerformanceMonitorBroadcaster(
|
||||
IServiceScopeFactory scopeFactory,
|
||||
ILogger<PerformanceMonitorBroadcaster> logger,
|
||||
IHubContext<PerformanceMonitorSignalR> hubContext)
|
||||
{
|
||||
_scopeFactory = scopeFactory;
|
||||
_logger = logger;
|
||||
_hubContext = hubContext;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 广播性能监控数据给所有客户端
|
||||
/// </summary>
|
||||
public async Task BroadcastPerformanceMetricsAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
using (var scope = _scopeFactory.CreateScope())
|
||||
{
|
||||
var service = scope.ServiceProvider.GetRequiredService<IPerformanceMonitorService>();
|
||||
var metrics = await service.GetCurrentMetricsAsync();
|
||||
|
||||
// 将object转换为dynamic以便访问属性
|
||||
dynamic data = metrics;
|
||||
|
||||
// SignalR推送时过滤掉diskInfo,只推送其他实时更新的数据
|
||||
var signalRData = new
|
||||
{
|
||||
hostName = data.hostName,
|
||||
ip = data.ip,
|
||||
macAddress = data.macAddress,
|
||||
cpuUsage = data.cpuUsage,
|
||||
memoryUsagePercentage = data.memoryUsagePercentage,
|
||||
cacheHitRate = data.cacheHitRate,
|
||||
errorRate = data.errorRate,
|
||||
apiResponseTimes = data.apiResponseTimes,
|
||||
requestCount = data.requestCount,
|
||||
errorCount = data.errorCount,
|
||||
lastUpdateTime = data.lastUpdateTime
|
||||
// 注意:不包含diskInfo,磁盘信息只在首次API调用时获取
|
||||
};
|
||||
|
||||
// 广播数据给所有连接的客户端
|
||||
await _hubContext.Clients.All.SendAsync("PerformanceMonitorCurrent", signalRData);
|
||||
}
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Logger.AddAsync($"广播性能监控数据失败:{ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using VolPro.Core.EFDbContext;
|
||||
using VolPro.Entity.DomainModels;
|
||||
|
||||
namespace VolPro.Core.PerformanceMonitor
|
||||
{
|
||||
/// <summary>
|
||||
/// 性能监控服务实现
|
||||
/// </summary>
|
||||
public class PerformanceMonitorService : IPerformanceMonitorService
|
||||
{
|
||||
private readonly SysDbContext _dbContext;
|
||||
private readonly ILogger<PerformanceMonitorService> _logger;
|
||||
|
||||
public PerformanceMonitorService(
|
||||
SysDbContext dbContext,
|
||||
ILogger<PerformanceMonitorService> logger)
|
||||
{
|
||||
_dbContext = dbContext;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前服务器的MAC地址
|
||||
/// </summary>
|
||||
private static string GetCurrentServerMacAddress()
|
||||
{
|
||||
try
|
||||
{
|
||||
var ni = NetworkInterface.GetAllNetworkInterfaces()
|
||||
.FirstOrDefault(n =>
|
||||
n.OperationalStatus == OperationalStatus.Up
|
||||
&& n.NetworkInterfaceType != NetworkInterfaceType.Loopback
|
||||
&& n.NetworkInterfaceType != NetworkInterfaceType.Tunnel);
|
||||
|
||||
if (ni != null)
|
||||
{
|
||||
var raw = ni.GetPhysicalAddress()?.GetAddressBytes();
|
||||
if (raw != null && raw.Length > 0)
|
||||
{
|
||||
return string.Join("-", raw.Select(b => b.ToString("X2")));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { /* 忽略获取MAC地址的异常 */ }
|
||||
|
||||
return "-";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前性能指标
|
||||
/// </summary>
|
||||
public async Task<object> GetCurrentMetricsAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
// 获取当前服务器的MAC地址
|
||||
var currentMacAddress = GetCurrentServerMacAddress();
|
||||
|
||||
// 获取最新的监控数据,按MacAddress过滤
|
||||
var latestRecord = await _dbContext.Set<Sys_Monitor>()
|
||||
.Where(x => x.MacAddress == currentMacAddress)
|
||||
.OrderByDescending(x => x.Id)
|
||||
.FirstAsync();
|
||||
|
||||
// 解析磁盘信息
|
||||
var diskInfo = new List<object>();
|
||||
if (latestRecord != null && !string.IsNullOrWhiteSpace(latestRecord.DiskInfo))
|
||||
{
|
||||
try
|
||||
{
|
||||
diskInfo = JsonConvert.DeserializeObject<List<object>>(latestRecord.DiskInfo) ?? new List<object>();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "解析磁盘信息失败");
|
||||
}
|
||||
}
|
||||
|
||||
// 获取系统信息
|
||||
var systemInfo = GetSystemInformation();
|
||||
|
||||
if (latestRecord == null)
|
||||
{
|
||||
return new
|
||||
{
|
||||
// 服务器信息
|
||||
hostName = "Unknown",
|
||||
ip = "127.0.0.1",
|
||||
macAddress = "-",
|
||||
|
||||
// 新增系统信息
|
||||
systemArchitecture = systemInfo.Architecture,
|
||||
systemVersion = systemInfo.Version,
|
||||
cpuCoreCount = systemInfo.CpuCoreCount,
|
||||
totalMemory = systemInfo.TotalMemory,
|
||||
usedMemory = systemInfo.UsedMemory,
|
||||
|
||||
// CPU使用率
|
||||
cpuUsage = 0.0,
|
||||
|
||||
// 内存使用率
|
||||
memoryUsagePercentage = 0.0,
|
||||
|
||||
// 缓存命中率
|
||||
cacheHitRate = 0.0,
|
||||
|
||||
// 错误率
|
||||
errorRate = 0.0,
|
||||
|
||||
// API响应时间(暂时模拟数据)
|
||||
apiResponseTimes = new List<object>(),
|
||||
|
||||
// 请求计数
|
||||
requestCount = 0,
|
||||
|
||||
// 错误计数
|
||||
errorCount = 0,
|
||||
|
||||
// 磁盘信息
|
||||
diskInfo,
|
||||
|
||||
// 最后更新时间
|
||||
lastUpdateTime = DateTime.Now
|
||||
};
|
||||
}
|
||||
|
||||
// 构建返回数据
|
||||
var metrics = new
|
||||
{
|
||||
// 服务器信息
|
||||
hostName = latestRecord.HostName ?? "Unknown",
|
||||
ip = latestRecord.Ip ?? "127.0.0.1",
|
||||
macAddress = latestRecord.MacAddress ?? "-",
|
||||
|
||||
// 新增系统信息
|
||||
systemArchitecture = systemInfo.Architecture,
|
||||
systemVersion = systemInfo.Version,
|
||||
cpuCoreCount = systemInfo.CpuCoreCount,
|
||||
totalMemory = systemInfo.TotalMemory,
|
||||
usedMemory = systemInfo.UsedMemory,
|
||||
|
||||
// CPU使用率
|
||||
cpuUsage = latestRecord.CpuUsage ?? 0,
|
||||
|
||||
// 内存使用率
|
||||
memoryUsagePercentage = latestRecord.MemoryUsage ?? 0,
|
||||
|
||||
// 缓存命中率(暂时固定值,实际需要从缓存系统获取)
|
||||
cacheHitRate = 85.0,
|
||||
|
||||
// 错误率(暂时固定值,实际需要从日志系统获取)
|
||||
errorRate = 0.5,
|
||||
|
||||
// 请求计数
|
||||
requestCount = 1250,
|
||||
|
||||
// 错误计数
|
||||
errorCount = 6,
|
||||
|
||||
// 磁盘信息
|
||||
diskInfo,
|
||||
|
||||
// 最后更新时间
|
||||
lastUpdateTime = latestRecord.RecordTime
|
||||
};
|
||||
|
||||
return metrics;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取系统信息
|
||||
/// </summary>
|
||||
private static dynamic GetSystemInformation()
|
||||
{
|
||||
try
|
||||
{
|
||||
// 获取内存信息
|
||||
var memoryMetrics = ComputerHelper.GetComputerInfo();
|
||||
|
||||
// 构建系统信息对象
|
||||
return new
|
||||
{
|
||||
// 系统架构
|
||||
Architecture = RuntimeInformation.OSArchitecture.ToString(),
|
||||
// 系统版本
|
||||
Version = RuntimeInformation.OSDescription,
|
||||
// CPU核心数量
|
||||
CpuCoreCount = Environment.ProcessorCount,
|
||||
// 内存总大小
|
||||
TotalMemory = memoryMetrics.TotalRAM,
|
||||
// 内存使用大小
|
||||
UsedMemory = memoryMetrics.UsedRam
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 记录错误并返回默认值
|
||||
Console.WriteLine("获取系统信息失败:" + ex.Message);
|
||||
return new
|
||||
{
|
||||
Architecture = "Unknown",
|
||||
Version = "Unknown",
|
||||
CpuCoreCount = 0,
|
||||
TotalMemory = "0GB",
|
||||
UsedMemory = "0GB"
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取历史性能数据
|
||||
/// </summary>
|
||||
public async Task<object> GetHistoricalMetricsAsync(DateTime from, DateTime to)
|
||||
{
|
||||
|
||||
var currentMacAddress = GetCurrentServerMacAddress();
|
||||
var historicalData = await _dbContext.Set<Sys_Monitor>()
|
||||
// .Where(x => x.RecordTime >= from && x.RecordTime <= to)
|
||||
.Where(x => x.MacAddress == currentMacAddress)
|
||||
.OrderByDescending(x => x.Id)
|
||||
.Select(x => new
|
||||
{
|
||||
x.Id,
|
||||
recordTime = x.RecordTime,
|
||||
cpuUsage = x.CpuUsage,
|
||||
memoryUsage = x.MemoryUsage,
|
||||
//bandwidthUsage = x.BandwidthUsage,
|
||||
//diskInfo = x.DiskInfo
|
||||
})
|
||||
.Take(2000).ToListAsync();
|
||||
return historicalData.OrderBy(x => x.Id);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
using Microsoft.AspNetCore.SignalR;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace VolPro.Core.PerformanceMonitor
|
||||
{
|
||||
/// <summary>
|
||||
/// 性能监控SignalR Hub
|
||||
/// </summary>
|
||||
public class PerformanceMonitorSignalR : Hub
|
||||
{
|
||||
private readonly ILogger<PerformanceMonitorSignalR> _logger;
|
||||
|
||||
public PerformanceMonitorSignalR(
|
||||
ILogger<PerformanceMonitorSignalR> logger)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 客户端连接时调用
|
||||
/// </summary>
|
||||
public override Task OnConnectedAsync()
|
||||
{
|
||||
//var connectionId = Context.ConnectionId;
|
||||
//_logger.LogInformation($"客户端连接: {connectionId}");
|
||||
return base.OnConnectedAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 客户端断开连接时调用
|
||||
/// </summary>
|
||||
public override Task OnDisconnectedAsync(Exception exception)
|
||||
{
|
||||
//var connectionId = Context.ConnectionId;
|
||||
//_logger.LogInformation($"客户端断开连接: {connectionId}");
|
||||
return base.OnDisconnectedAsync(exception);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
63
api_sqlsugar/VolPro.Core/PerformanceMonitor/ShellHelper.cs
Normal file
63
api_sqlsugar/VolPro.Core/PerformanceMonitor/ShellHelper.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace VolPro.Core.PerformanceMonitor
|
||||
{
|
||||
public class ShellHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// linux 系统命令
|
||||
/// </summary>
|
||||
/// <param name="command"></param>
|
||||
/// <returns></returns>
|
||||
public static string Bash(string command)
|
||||
{
|
||||
var escapedArgs = command.Replace("\"", "\\\"");
|
||||
var process = new Process()
|
||||
{
|
||||
StartInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = "/bin/bash",
|
||||
Arguments = $"-c \"{escapedArgs}\"",
|
||||
RedirectStandardOutput = true,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
}
|
||||
};
|
||||
process.Start();
|
||||
string result = process.StandardOutput.ReadToEnd();
|
||||
process.WaitForExit();
|
||||
process.Dispose();
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// windows系统命令
|
||||
/// </summary>
|
||||
/// <param name="fileName"></param>
|
||||
/// <param name="args"></param>
|
||||
/// <returns></returns>
|
||||
public static string Cmd(string fileName, string args)
|
||||
{
|
||||
string output = string.Empty;
|
||||
|
||||
var info = new ProcessStartInfo();
|
||||
info.FileName = fileName;
|
||||
info.Arguments = args;
|
||||
info.RedirectStandardOutput = true;
|
||||
info.UseShellExecute = false;
|
||||
info.CreateNoWindow = true;
|
||||
|
||||
using (var process = Process.Start(info))
|
||||
{
|
||||
output = process.StandardOutput.ReadToEnd();
|
||||
process.WaitForExit();
|
||||
}
|
||||
return output;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user