Browse Source

feat(metrics): Add system info metrics module

pull/1406/head
colin 3 months ago
parent
commit
4e518097d4
  1. 3
      aspnet-core/modules/system-info/LINGYUN.Abp.SystemInfo.HttpApi/FodyWeavers.xml
  2. 30
      aspnet-core/modules/system-info/LINGYUN.Abp.SystemInfo.HttpApi/FodyWeavers.xsd
  3. 22
      aspnet-core/modules/system-info/LINGYUN.Abp.SystemInfo.HttpApi/LINGYUN.Abp.SystemInfo.HttpApi.csproj
  4. 17
      aspnet-core/modules/system-info/LINGYUN.Abp.SystemInfo.HttpApi/LINGYUN/Abp/SystemInfo/AbpSystemInfoHttpApiModule.cs
  5. 27
      aspnet-core/modules/system-info/LINGYUN.Abp.SystemInfo.HttpApi/LINGYUN/Abp/SystemInfo/Models/ComponentInfoModel.cs
  6. 20
      aspnet-core/modules/system-info/LINGYUN.Abp.SystemInfo.HttpApi/LINGYUN/Abp/SystemInfo/Models/ComponentKeyModel.cs
  7. 9
      aspnet-core/modules/system-info/LINGYUN.Abp.SystemInfo.HttpApi/LINGYUN/Abp/SystemInfo/Models/SystemInfoModel.cs
  8. 20
      aspnet-core/modules/system-info/LINGYUN.Abp.SystemInfo.HttpApi/LINGYUN/Abp/SystemInfo/Permissions/SystemInfoPermissionDefinitionProvider.cs
  9. 8
      aspnet-core/modules/system-info/LINGYUN.Abp.SystemInfo.HttpApi/LINGYUN/Abp/SystemInfo/Permissions/SystemInfoPermissions.cs
  10. 361
      aspnet-core/modules/system-info/LINGYUN.Abp.SystemInfo.HttpApi/LINGYUN/Abp/SystemInfo/SystemInfoController.cs
  11. 63
      aspnet-core/modules/system-info/LINGYUN.Abp.SystemInfo.HttpApi/LINGYUN/Abp/SystemInfo/Utils/GCMonitor.cs
  12. 54
      aspnet-core/modules/system-info/LINGYUN.Abp.SystemInfo.HttpApi/LINGYUN/Abp/SystemInfo/Utils/MemoryMonitor.cs
  13. 59
      aspnet-core/modules/system-info/LINGYUN.Abp.SystemInfo.HttpApi/LINGYUN/Abp/SystemInfo/Utils/ThreadMonitor.cs
  14. 3
      aspnet-core/services/LY.MicroService.Applications.Single/LY.MicroService.Applications.Single.csproj
  15. 27
      aspnet-core/services/LY.MicroService.Applications.Single/MicroServiceApplicationsSingleModule.Configure.cs
  16. 8
      aspnet-core/services/LY.MicroService.Applications.Single/MicroServiceApplicationsSingleModule.cs
  17. 2
      aspnet-core/services/LY.MicroService.Applications.Single/appsettings.Development.json

3
aspnet-core/modules/system-info/LINGYUN.Abp.SystemInfo.HttpApi/FodyWeavers.xml

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait ContinueOnCapturedContext="false" />
</Weavers>

30
aspnet-core/modules/system-info/LINGYUN.Abp.SystemInfo.HttpApi/FodyWeavers.xsd

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. -->
<xs:element name="Weavers">
<xs:complexType>
<xs:all>
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1">
<xs:complexType>
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" />
</xs:complexType>
</xs:element>
</xs:all>
<xs:attribute name="VerifyAssembly" type="xs:boolean">
<xs:annotation>
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="VerifyIgnoreCodes" type="xs:string">
<xs:annotation>
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="GenerateXsd" type="xs:boolean">
<xs:annotation>
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:element>
</xs:schema>

22
aspnet-core/modules/system-info/LINGYUN.Abp.SystemInfo.HttpApi/LINGYUN.Abp.SystemInfo.HttpApi.csproj

@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\..\..\configureawait.props" />
<Import Project="..\..\..\..\common.props" />
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<AssemblyName>LINGYUN.Abp.SystemInfo.HttpApi</AssemblyName>
<PackageId>LINGYUN.Abp.SystemInfo.HttpApi</PackageId>
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Volo.Abp.AspNetCore.Mvc" />
<PackageReference Include="DotNetCore.CAP" />
<PackageReference Include="StackExchange.Redis" />
</ItemGroup>
</Project>

17
aspnet-core/modules/system-info/LINGYUN.Abp.SystemInfo.HttpApi/LINGYUN/Abp/SystemInfo/AbpSystemInfoHttpApiModule.cs

@ -0,0 +1,17 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.AspNetCore.Mvc;
using Volo.Abp.Modularity;
namespace LINGYUN.Abp.SystemInfo;
[DependsOn(typeof(AbpAspNetCoreMvcModule))]
public class AbpSystemInfoHttpApiModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
PreConfigure<IMvcBuilder>(mvcBuilder =>
{
mvcBuilder.AddApplicationPartIfNotExists(typeof(AbpSystemInfoHttpApiModule).Assembly);
});
}
}

27
aspnet-core/modules/system-info/LINGYUN.Abp.SystemInfo.HttpApi/LINGYUN/Abp/SystemInfo/Models/ComponentInfoModel.cs

@ -0,0 +1,27 @@
using System.Collections.Generic;
namespace LINGYUN.Abp.SystemInfo.Models;
/// <summary>
/// 组件
/// </summary>
public class ComponentInfoModel
{
/// <summary>
/// 组件名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 组件名称集合
/// </summary>
public ComponentKeyModel[] Keys { get; set; }
/// <summary>
/// 组件状态集合
/// </summary>
public Dictionary<string, object> Details { get; set; }
public ComponentInfoModel(string name, ComponentKeyModel[] keys, Dictionary<string, object> details)
{
Name = name;
Keys = keys;
Details = details;
}
}

20
aspnet-core/modules/system-info/LINGYUN.Abp.SystemInfo.HttpApi/LINGYUN/Abp/SystemInfo/Models/ComponentKeyModel.cs

@ -0,0 +1,20 @@
namespace LINGYUN.Abp.SystemInfo.Models;
/// <summary>
/// 组件名称
/// </summary>
public class ComponentKeyModel
{
/// <summary>
/// 组件名称
/// </summary>
public string Name { get; set; }
/// <summary>
/// 组件显示名称
/// </summary>
public string DisplayName { get; set; }
public ComponentKeyModel(string name, string displayName)
{
Name = name;
DisplayName = displayName;
}
}

9
aspnet-core/modules/system-info/LINGYUN.Abp.SystemInfo.HttpApi/LINGYUN/Abp/SystemInfo/Models/SystemInfoModel.cs

@ -0,0 +1,9 @@
namespace LINGYUN.Abp.SystemInfo.Models;
public class SystemInfoModel
{
/// <summary>
/// 组件状态集合
/// </summary>
public ComponentInfoModel[] Components { get; set; }
}

20
aspnet-core/modules/system-info/LINGYUN.Abp.SystemInfo.HttpApi/LINGYUN/Abp/SystemInfo/Permissions/SystemInfoPermissionDefinitionProvider.cs

@ -0,0 +1,20 @@
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Localization;
namespace LINGYUN.Abp.SystemInfo.Permissions;
public class SystemInfoPermissionDefinitionProvider : PermissionDefinitionProvider
{
public override void Define(IPermissionDefinitionContext context)
{
var systemInfoGroup = context.AddGroup(
SystemInfoPermissions.GroupName,
new FixedLocalizableString("系统详情"));
systemInfoGroup.AddPermission(
SystemInfoPermissions.Default,
new FixedLocalizableString("获取系统详情"),
Volo.Abp.MultiTenancy.MultiTenancySides.Host)
.WithProviders(ClientPermissionValueProvider.ProviderName); // 使用客户端授权
}
}

8
aspnet-core/modules/system-info/LINGYUN.Abp.SystemInfo.HttpApi/LINGYUN/Abp/SystemInfo/Permissions/SystemInfoPermissions.cs

@ -0,0 +1,8 @@
namespace LINGYUN.Abp.SystemInfo.Permissions;
public static class SystemInfoPermissions
{
public const string GroupName = "SystemInfo";
public const string Default = GroupName + ".Get";
}

361
aspnet-core/modules/system-info/LINGYUN.Abp.SystemInfo.HttpApi/LINGYUN/Abp/SystemInfo/SystemInfoController.cs

@ -0,0 +1,361 @@
using DotNetCore.CAP;
using LINGYUN.Abp.SystemInfo.Models;
using LINGYUN.Abp.SystemInfo.Permissions;
using LINGYUN.Abp.SystemInfo.Utils;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using StackExchange.Redis;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Volo.Abp.AspNetCore.Mvc;
namespace LINGYUN.Abp.SystemInfo;
[Controller]
[Route("api/system-info")]
[Authorize(SystemInfoPermissions.Default)]
public class SystemInfoController : AbpControllerBase
{
[HttpGet]
public async virtual Task<SystemInfoModel> GetSystemInfoAsync()
{
return new SystemInfoModel
{
Components = new ComponentInfoModel[]
{
GetSystemInfo(),
GetPerformanceInfo(),
GetRedisInfo(),
GetCapInfo(),
},
};
}
private ComponentInfoModel GetSystemInfo()
{
var env = HttpContext.RequestServices.GetService<IHostEnvironment>();
using var process = Process.GetCurrentProcess();
var systemKeys = new List<ComponentKeyModel>
{
new ComponentKeyModel("sys_machine_name", "机器名称"),
new ComponentKeyModel("sys_environment", "运行环境"),
new ComponentKeyModel("sys_framework_version", ".NET版本"),
new ComponentKeyModel("sys_os_version", "操作系统版本"),
new ComponentKeyModel("sys_app_db_provider", "数据库"),
new ComponentKeyModel("sys_app_version", "应用版本"),
new ComponentKeyModel("sys_app_build_time", "编译时间"),
new ComponentKeyModel("sys_app_start_time", "启动时间"),
};
var systemDetails = new Dictionary<string, object>
{
{ "sys_machine_name", Environment.MachineName },
{ "sys_environment", env?.EnvironmentName ?? Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") },
{ "sys_framework_version", RuntimeInformation.FrameworkDescription },
{ "sys_os_version", RuntimeInformation.OSDescription },
{ "sys_app_db_provider", Environment.GetEnvironmentVariable("APPLICATION_DATABASE_PROVIDER") },
{ "sys_app_start_time", process.StartTime.ToString("yyyy-MM-dd HH:mm:ss") },
};
try
{
var assembly = Assembly.GetEntryAssembly() ?? Assembly.GetExecutingAssembly();
var fileVersionInfo = FileVersionInfo.GetVersionInfo(assembly.Location);
var assemblyName = assembly.GetName();
var buildTime = GetBuildTime(assembly);
systemDetails.Add("sys_app_version", fileVersionInfo.FileVersion ?? assemblyName.Version?.ToString() ?? "N/A");
systemDetails.Add("sys_app_build_time", buildTime.HasValue ? buildTime.Value.ToString("yyyy-MM-dd HH:mm:ss") : "N/A");
}
catch
{
systemDetails.Add("sys_app_version", "N/A");
systemDetails.Add("sys_app_build_time", "N/A");
}
return new ComponentInfoModel("系统", systemKeys.ToArray(), systemDetails);
}
private ComponentInfoModel GetPerformanceInfo()
{
using var process = Process.GetCurrentProcess();
var memoryMetrics = new MemoryMonitor().GetMemoryMetrics();
var threadMetrics = new ThreadMonitor().GetThreadMetrics();
var gcMetrics = new GCMonitor().GetGCMetrics();
return new ComponentInfoModel(
"性能",
new ComponentKeyModel[]
{
new ComponentKeyModel("perf_total_memory", "总内存(MB)"),
new ComponentKeyModel("perf_working_set", "工作集内存(MB)"),
new ComponentKeyModel("perf_private_memory", "私有内存(MB)"),
new ComponentKeyModel("perf_gc_heap_size", "GC堆大小(MB)"),
new ComponentKeyModel("perf_gc0", "Gen0回收次数"),
new ComponentKeyModel("perf_gc1", "Gen1回收次数"),
new ComponentKeyModel("perf_gc2", "Gen2回收次数"),
new ComponentKeyModel("perf_active_total_memory", "总可用内存"),
new ComponentKeyModel("perf_memory_load", "内存负载"),
new ComponentKeyModel("perf_fragmented_bytes", "内存碎片大小"),
new ComponentKeyModel("perf_available_worker_threads", "可用工作线程数"),
new ComponentKeyModel("perf_available_completion_port_threads", "可用I/O线程数"),
new ComponentKeyModel("perf_max_worker_threads", "最大工作线程数"),
new ComponentKeyModel("perf_max_completion_port_threads", "最大I/O线程数"),
new ComponentKeyModel("perf_active_threads", "进程总线程数"),
new ComponentKeyModel("perf_thread_pool_thread_count", "线程池活动线程数"),
},
new Dictionary<string, object>
{
{ "perf_total_memory", memoryMetrics.TotalMemory },
{ "perf_working_set", memoryMetrics.WorkingSet },
{ "perf_private_memory", memoryMetrics.PrivateMemory },
{ "perf_gc_heap_size", memoryMetrics.GCHeapSize },
{ "perf_gc0", gcMetrics.Gen0Collections },
{ "perf_gc1", gcMetrics.Gen1Collections },
{ "perf_gc2", gcMetrics.Gen2Collections },
{ "perf_active_total_memory", gcMetrics.TotalMemory },
{ "perf_memory_load", gcMetrics.MemoryLoad },
{ "perf_fragmented_bytes", gcMetrics.FragmentedBytes },
{ "perf_available_worker_threads", threadMetrics.AvailableWorkerThreads },
{ "perf_available_completion_port_threads", threadMetrics.AvailableCompletionPortThreads },
{ "perf_max_worker_threads", threadMetrics.MaxWorkerThreads },
{ "perf_max_completion_port_threads", threadMetrics.MaxCompletionPortThreads },
{ "perf_active_threads", threadMetrics.ActiveThreads },
{ "perf_thread_pool_thread_count", threadMetrics.ThreadPoolThreadCount },
});
}
private ComponentInfoModel GetCapInfo()
{
var cap = HttpContext.RequestServices.GetService<CapMarkerService>();
var storage = HttpContext.RequestServices.GetService<CapStorageMarkerService>();
var broker = HttpContext.RequestServices.GetService<CapMessageQueueMakerService>();
var capKeys = new List<ComponentKeyModel>
{
new ComponentKeyModel("cap_status", "状态"),
new ComponentKeyModel("cap_version", "版本"),
new ComponentKeyModel("cap_storage", "持久化"),
new ComponentKeyModel("cap_transport", "传输"),
};
var capDetails = new Dictionary<string, object>
{
{ "cap_status", cap != null ? "正常" : "未启用" },
{ "cap_version", cap != null ? cap.Version.Substring(0, 5) : "N/A" },
{ "cap_storage", storage != null ? storage.Name : "N/A" },
{ "cap_transport", broker != null ? broker.Name : "N/A" }
};
return new ComponentInfoModel("CAP组件", capKeys.ToArray(), capDetails);
}
private ComponentInfoModel GetRedisInfo()
{
var redis = HttpContext.RequestServices.GetService<IConnectionMultiplexer>();
if (redis == null)
{
return new ComponentInfoModel(
"Redis组件",
new ComponentKeyModel[]
{
new ComponentKeyModel("redis_status", "状态")
},
new Dictionary<string, object>
{
{ "redis_status", "未注册" },
});
}
var redisInfo = new Dictionary<string, string>();
try
{
var server = redis.GetServer(redis.GetEndPoints().First());
var serverInfo = server.Info();
foreach (var section in serverInfo)
{
foreach (var item in section)
{
redisInfo[item.Key] = item.Value;
}
}
}
catch(Exception ex)
{
Logger.LogWarning("There is an exception in connecting to the redis server: {message}", ex.Message);
return new ComponentInfoModel(
"Redis组件",
new ComponentKeyModel[]
{
new ComponentKeyModel("redis_status", "状态")
},
new Dictionary<string, object>
{
{ "redis_status", "连接异常" },
});
}
var uptimeSeconds = redisInfo.ContainsKey("uptime_in_seconds") ? long.Parse(redisInfo["uptime_in_seconds"]) : 0;
var days = Math.Floor((double)uptimeSeconds / 86400);
var hours = Math.Floor((double)uptimeSeconds % 86400 / 3600);
var minutes = Math.Floor((double)uptimeSeconds % 3600 / 60);
var usedMemory = redisInfo.ContainsKey("used_memory") ? long.Parse(redisInfo["used_memory"]) : 0;
var maxMemory = redisInfo.ContainsKey("maxmemory") ? long.Parse(redisInfo["maxmemory"]) : 0;
var totalKeys = CalculateTotalKeys(redisInfo);
var expiresKeys = CalculateExpires(redisInfo);
var avgTtl = CalculateAvgTtl(redisInfo);
var redisKeys = new List<ComponentKeyModel>()
{
new ComponentKeyModel("redis_version", "版本"),
new ComponentKeyModel("redis_mode", "运行模式"),
new ComponentKeyModel("redis_os", "操作系统"),
new ComponentKeyModel("redis_uptime", "运行时间"),
new ComponentKeyModel("redis_used_cpu_sys", "系统CPU使用时间"),
new ComponentKeyModel("redis_used_cpu_user", "用户CPU使用时间"),
new ComponentKeyModel("redis_used_memory_mb", "使用内存(MB)"),
new ComponentKeyModel("redis_used_memory_rss_mb", "物理内存(MB)"),
new ComponentKeyModel("redis_used_memory_peak_mb", "内存使用峰值(MB)"),
new ComponentKeyModel("redis_maxmemory_mb", "最大内存限制(MB)"),
new ComponentKeyModel("redis_memory_usage_percent", "内存使用率(%)"),
new ComponentKeyModel("redis_mem_fragmentation_ratio", "内存碎片率"),
new ComponentKeyModel("redis_connected_clients", "已连接客户端数"),
new ComponentKeyModel("redis_blocked_clients", "阻塞客户端数"),
new ComponentKeyModel("redis_client_longest_output_list", "客户端最长输出列表"),
new ComponentKeyModel("redis_client_biggest_input_buf", "客户端最大输入缓冲区"),
new ComponentKeyModel("redis_maxclients", "最大客户端数"),
new ComponentKeyModel("redis_total_keys", "总键数量"),
new ComponentKeyModel("redis_expires_keys", "设置过期键数量"),
new ComponentKeyModel("redis_expired_keys", "已过期键数量"),
new ComponentKeyModel("redis_evicted_keys", "被驱逐键数量"),
new ComponentKeyModel("redis_avg_ttl_seconds", "平均TTL(秒)"),
};
var redisDetails = new Dictionary<string, object>()
{
{ "redis_version", redisInfo.GetValueOrDefault("redis_version", "unknown") },
{ "redis_mode", redisInfo.GetValueOrDefault("redis_mode", "unknown") },
{ "redis_os", redisInfo.GetValueOrDefault("os", "unknown") },
{ "redis_uptime", $"{days}天{hours}时{minutes}分" },
{ "redis_used_cpu_sys", redisInfo.GetValueOrDefault("used_cpu_sys", "0") },
{ "redis_used_cpu_user", redisInfo.GetValueOrDefault("used_cpu_user", "0") },
{ "redis_used_memory_mb", Math.Round((double)usedMemory / 1024 / 1024, 2) },
{ "redis_used_memory_rss_mb", redisInfo.ContainsKey("used_memory_rss") ?
Math.Round(double.Parse(redisInfo["used_memory_rss"]) / 1024 / 1024, 2) : 0 },
{ "redis_used_memory_peak_mb", redisInfo.ContainsKey("used_memory_peak") ?
Math.Round(double.Parse(redisInfo["used_memory_peak"]) / 1024 / 1024, 2) : 0 },
{ "redis_maxmemory_mb", maxMemory > 0 ? Math.Round((double)maxMemory / 1024 / 1024, 2) : 0 },
{ "redis_memory_usage_percent", maxMemory > 0 ? Math.Round((double)usedMemory / maxMemory * 100, 2) : 0 },
{ "redis_mem_fragmentation_ratio", redisInfo.GetValueOrDefault("mem_fragmentation_ratio", "0") },
{ "redis_connected_clients", redisInfo.GetValueOrDefault("connected_clients", "0") },
{ "redis_blocked_clients", redisInfo.GetValueOrDefault("blocked_clients", "0") },
{ "redis_client_longest_output_list", redisInfo.GetValueOrDefault("client_longest_output_list", "0") },
{ "redis_client_biggest_input_buf", redisInfo.GetValueOrDefault("client_biggest_input_buf", "0") },
{ "redis_maxclients", redisInfo.GetValueOrDefault("maxclients", "unlimited") },
{ "redis_total_keys", totalKeys },
{ "redis_expires_keys", expiresKeys },
{ "redis_expired_keys", redisInfo.GetValueOrDefault("expired_keys", "0") },
{ "redis_evicted_keys", redisInfo.GetValueOrDefault("evicted_keys", "0") },
{ "redis_avg_ttl_seconds", avgTtl },
};
return new ComponentInfoModel("Redis组件", redisKeys.ToArray(), redisDetails);
}
private long CalculateTotalKeys(Dictionary<string, string> redisInfo)
{
long total = 0;
foreach (var item in redisInfo)
{
if (item.Key.StartsWith("db"))
{
var keysPart = item.Value.Split(',').FirstOrDefault(x => x.StartsWith("keys="));
if (keysPart != null && long.TryParse(keysPart.Split('=')[1], out var count))
{
total += count;
}
}
}
return total;
}
private long CalculateExpires(Dictionary<string, string> redisInfo)
{
long expires = 0;
foreach (var item in redisInfo)
{
if (item.Key.StartsWith("db"))
{
var expiresPart = item.Value.Split(',').FirstOrDefault(x => x.StartsWith("expires="));
if (expiresPart != null && long.TryParse(expiresPart.Split('=')[1], out var count))
{
expires += count;
}
}
}
return expires;
}
private long CalculateAvgTtl(Dictionary<string, string> redisInfo)
{
long totalTtl = 0;
long dbCount = 0;
foreach (var item in redisInfo)
{
if (item.Key.StartsWith("db"))
{
var ttlPart = item.Value.Split(',').FirstOrDefault(x => x.StartsWith("avg_ttl="));
if (ttlPart != null && long.TryParse(ttlPart.Split('=')[1], out var ttl) && ttl > 0)
{
totalTtl += ttl;
dbCount++;
}
}
}
return dbCount > 0 ? totalTtl / dbCount : -1;
}
private DateTime? GetBuildTime(Assembly assembly)
{
try
{
var attribute = assembly.GetCustomAttribute<AssemblyMetadataAttribute>();
if (attribute != null && attribute.Key == "BuildTimestamp")
{
if (DateTime.TryParse(attribute.Value, out var buildTime))
{
return buildTime;
}
}
var fileInfo = new FileInfo(assembly.Location);
if (fileInfo.Exists)
{
return fileInfo.LastWriteTime;
}
return null;
}
catch
{
return null;
}
}
}

63
aspnet-core/modules/system-info/LINGYUN.Abp.SystemInfo.HttpApi/LINGYUN/Abp/SystemInfo/Utils/GCMonitor.cs

@ -0,0 +1,63 @@
using System;
namespace LINGYUN.Abp.SystemInfo.Utils;
public class GCMetrics
{
/// <summary>
/// Gen0回收次数
/// </summary>
public int Gen0Collections { get; set; }
/// <summary>
/// Gen1回收次数
/// </summary>
public int Gen1Collections { get; set; }
/// <summary>
/// Gen2回收次数
/// </summary>
public int Gen2Collections { get; set; }
/// <summary>
/// 总可用内存
/// </summary>
public long TotalMemory { get; set; }
/// <summary>
/// 内存负载
/// </summary>
public long MemoryLoad { get; set; }
/// <summary>
/// 内存碎片大小
/// </summary>
public long FragmentedBytes { get; set; }
}
public class GCMonitor
{
private int _lastGen0;
private int _lastGen1;
private int _lastGen2;
public GCMetrics GetGCMetrics()
{
var currentGen0 = GC.CollectionCount(0);
var currentGen1 = GC.CollectionCount(1);
var currentGen2 = GC.CollectionCount(2);
var metrics = new GCMetrics
{
Gen0Collections = currentGen0 - _lastGen0,
Gen1Collections = currentGen1 - _lastGen1,
Gen2Collections = currentGen2 - _lastGen2
};
var gcInfo = GC.GetGCMemoryInfo();
metrics.TotalMemory = gcInfo.TotalAvailableMemoryBytes;
metrics.MemoryLoad = gcInfo.MemoryLoadBytes;
metrics.FragmentedBytes = gcInfo.FragmentedBytes;
_lastGen0 = currentGen0;
_lastGen1 = currentGen1;
_lastGen2 = currentGen2;
return metrics;
}
}

54
aspnet-core/modules/system-info/LINGYUN.Abp.SystemInfo.HttpApi/LINGYUN/Abp/SystemInfo/Utils/MemoryMonitor.cs

@ -0,0 +1,54 @@
using System;
using System.Diagnostics;
namespace LINGYUN.Abp.SystemInfo.Utils;
public class MemoryMetrics
{
/// <summary>
/// 总物理内存
/// </summary>
public long TotalMemory { get; set; }
/// <summary>
/// 工作集内存
/// </summary>
public long WorkingSet { get; set; }
/// <summary>
/// 私有内存
/// </summary>
public long PrivateMemory { get; set; }
/// <summary>
/// 虚拟内存
/// </summary>
public long VirtualMemory { get; set; }
/// <summary>
/// GC堆大小
/// </summary>
public long GCHeapSize { get; set; }
}
public class MemoryMonitor
{
// 转换为MB
const long MB = 1024 * 1024;
public MemoryMetrics GetMemoryMetrics()
{
var process = Process.GetCurrentProcess();
return new MemoryMetrics
{
TotalMemory = GC.GetTotalMemory(false) / MB,
WorkingSet = process.WorkingSet64 / MB,
PrivateMemory = process.PrivateMemorySize64 / MB,
VirtualMemory = process.VirtualMemorySize64 / MB,
GCHeapSize = GetGCHeapSize() / MB
};
}
private long GetGCHeapSize()
{
var gcInfo = GC.GetGCMemoryInfo();
return (long)gcInfo.HeapSizeBytes;
}
}

59
aspnet-core/modules/system-info/LINGYUN.Abp.SystemInfo.HttpApi/LINGYUN/Abp/SystemInfo/Utils/ThreadMonitor.cs

@ -0,0 +1,59 @@
using System.Diagnostics;
using System.Threading;
namespace LINGYUN.Abp.SystemInfo.Utils;
public class ThreadMetrics
{
/// <summary>
/// 可用工作线程数
/// </summary>
public int AvailableWorkerThreads { get; set; }
/// <summary>
/// 可用I/O线程数
/// </summary>
public int AvailableCompletionPortThreads { get; set; }
/// <summary>
/// 最大工作线程数
/// </summary>
public int MaxWorkerThreads { get; set; }
/// <summary>
/// 最大I/O线程数
/// </summary>
public int MaxCompletionPortThreads { get; set; }
/// <summary>
/// 进程总线程数
/// </summary>
public int ActiveThreads { get; set; }
/// <summary>
/// 线程池活动线程数
/// </summary>
public int ThreadPoolThreadCount { get; set; }
}
public class ThreadMonitor
{
public ThreadMetrics GetThreadMetrics()
{
ThreadPool.GetAvailableThreads(
out var availableWorkerThreads,
out var availableCompletionPortThreads);
ThreadPool.GetMaxThreads(
out var maxWorkerThreads,
out var maxCompletionPortThreads);
// 获取进程线程数(近似活动线程数)
var process = Process.GetCurrentProcess();
var threads = process.Threads;
return new ThreadMetrics
{
AvailableWorkerThreads = availableWorkerThreads,
AvailableCompletionPortThreads = availableCompletionPortThreads,
MaxWorkerThreads = maxWorkerThreads,
MaxCompletionPortThreads = maxCompletionPortThreads,
ActiveThreads = threads.Count,
ThreadPoolThreadCount = ThreadPool.ThreadCount
};
}
}

3
aspnet-core/services/LY.MicroService.Applications.Single/LY.MicroService.Applications.Single.csproj

@ -7,6 +7,7 @@
<ImplicitUsings>enable</ImplicitUsings>
<RootNamespace>LY.MicroService.Applications.Single</RootNamespace>
<ImplicitUsings>enable</ImplicitUsings>
<Version>9.3.6.2</Version>
</PropertyGroup>
<ItemGroup>
@ -22,6 +23,7 @@
<PackageReference Include="Elsa.Activities.Temporal.Quartz" />
<PackageReference Include="Elsa.Webhooks.Api" />
<PackageReference Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.StackExchangeRedis" />
<PackageReference Include="OpenIddict.Validation.DataProtection" />
<PackageReference Include="OpenIddict.Server.DataProtection" />
<PackageReference Include="Serilog.AspNetCore" />
@ -247,6 +249,7 @@
<ProjectReference Include="..\..\modules\saas\LINGYUN.Abp.Saas.HttpApi\LINGYUN.Abp.Saas.HttpApi.csproj" />
<ProjectReference Include="..\..\modules\settings\LINGYUN.Abp.SettingManagement.Application\LINGYUN.Abp.SettingManagement.Application.csproj" />
<ProjectReference Include="..\..\modules\settings\LINGYUN.Abp.SettingManagement.HttpApi\LINGYUN.Abp.SettingManagement.HttpApi.csproj" />
<ProjectReference Include="..\..\modules\system-info\LINGYUN.Abp.SystemInfo.HttpApi\LINGYUN.Abp.SystemInfo.HttpApi.csproj" />
<ProjectReference Include="..\..\modules\task-management\LINGYUN.Abp.BackgroundTasks.Abstractions\LINGYUN.Abp.BackgroundTasks.Abstractions.csproj" />
<ProjectReference Include="..\..\modules\task-management\LINGYUN.Abp.BackgroundTasks.Activities\LINGYUN.Abp.BackgroundTasks.Activities.csproj" />
<ProjectReference Include="..\..\modules\task-management\LINGYUN.Abp.BackgroundTasks.DistributedLocking\LINGYUN.Abp.BackgroundTasks.DistributedLocking.csproj" />

27
aspnet-core/services/LY.MicroService.Applications.Single/MicroServiceApplicationsSingleModule.Configure.cs

@ -1,3 +1,4 @@
using Microsoft.AspNetCore.SignalR;
using VoloAbpExceptionHandlingOptions = Volo.Abp.AspNetCore.ExceptionHandling.AbpExceptionHandlingOptions;
namespace LY.MicroService.Applications.Single;
@ -135,6 +136,28 @@ public partial class MicroServiceApplicationsSingleModule
}
}
private void PreConfigureSignalR(IConfiguration configuration)
{
PreConfigure<ISignalRServerBuilder>(builder =>
{
var redisEnabled = configuration["SignalR:Redis:IsEnabled"];
if (redisEnabled.IsNullOrEmpty() || bool.Parse(redisEnabled))
{
builder.AddStackExchangeRedis(redis =>
{
var redisConfiguration = configuration["SignalR:Redis:Configuration"];
if (!redisConfiguration.IsNullOrEmpty())
{
redis.ConnectionFactory = async (writer) =>
{
return await ConnectionMultiplexer.ConnectAsync(redisConfiguration);
};
}
});
}
});
}
private void PreConfigureQuartz(IConfiguration configuration)
{
PreConfigure<AbpQuartzOptions>(options =>
@ -522,7 +545,7 @@ public partial class MicroServiceApplicationsSingleModule
});
}
private void ConfigureCaching(IConfiguration configuration)
private void ConfigureCaching(IServiceCollection services, IConfiguration configuration)
{
Configure<AbpDistributedCacheOptions>(options =>
{
@ -535,6 +558,8 @@ public partial class MicroServiceApplicationsSingleModule
options.ConfigurationOptions = redisConfig;
options.InstanceName = configuration["Redis:InstanceName"];
});
services.AddSingleton<IConnectionMultiplexer>(sp => ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]));
}
private void ConfigureMultiTenancy(IConfiguration configuration)

8
aspnet-core/services/LY.MicroService.Applications.Single/MicroServiceApplicationsSingleModule.cs

@ -1,3 +1,5 @@
using LINGYUN.Abp.SystemInfo;
namespace LY.MicroService.Applications.Single;
[DependsOn(
@ -382,6 +384,9 @@ namespace LY.MicroService.Applications.Single;
typeof(AbpMailKitModule),
typeof(AbpAutofacModule),
// 单体应用系统状态接口
typeof(AbpSystemInfoHttpApiModule),
// MySql
typeof(SingleMigrationsEntityFrameworkCoreMySqlModule),
// SqlServer
@ -402,6 +407,7 @@ public partial class MicroServiceApplicationsSingleModule : AbpModule
PreConfigureApp(configuration);
PreConfigureCAP(configuration);
PreConfigureQuartz(configuration);
PreConfigureSignalR(configuration);
PreConfigureAuthServer(configuration);
PreConfigureElsa(context.Services, configuration);
PreConfigureCertificate(configuration, hostingEnvironment);
@ -424,7 +430,6 @@ public partial class MicroServiceApplicationsSingleModule : AbpModule
ConfigureVirtualFileSystem();
ConfigureEntityDataProtected();
ConfigureUrls(configuration);
ConfigureCaching(configuration);
ConfigureAuditing(configuration);
ConfigureIdentity(configuration);
ConfigureDbContext(configuration);
@ -440,6 +445,7 @@ public partial class MicroServiceApplicationsSingleModule : AbpModule
ConfigureNotificationManagement(configuration);
ConfigureCors(context.Services, configuration);
ConfigureSwagger(context.Services, configuration);
ConfigureCaching(context.Services, configuration);
ConfigureDistributedLock(context.Services, configuration);
ConfigureKestrelServer(configuration, hostingEnvironment);
ConfigureSecurity(context.Services, configuration, hostingEnvironment.IsDevelopment());

2
aspnet-core/services/LY.MicroService.Applications.Single/appsettings.Development.json

@ -93,7 +93,7 @@
},
"Redis": {
"IsEnabled": true,
"Configuration": "127.0.0.1,defaultDatabase=15",
"Configuration": "127.0.0.1,defaultDatabase=15,allowAdmin=true",
"InstanceName": "LINGYUN.Abp.Application"
},
"Features": {

Loading…
Cancel
Save