U:调整结构性目录
This commit is contained in:
1 parent
693b390df0
commit
c255419412
172 files changed
+839
-18898
No files matched your search
@@ -0,0 +1,34 @@
|
||||
using Common.Configuration;
|
||||
|
||||
namespace Common;
|
||||
|
||||
public sealed class AppConfig
|
||||
{
|
||||
// 使用 Lazy<T> 实现线程安全的懒加载单例
|
||||
private static readonly Lazy<AppConfig> _instance = new Lazy<AppConfig>(() => new AppConfig());
|
||||
|
||||
// 提供全局访问点
|
||||
public static AppConfig Instance => _instance.Value;
|
||||
|
||||
// 私有构造函数,防止外部实例化
|
||||
private AppConfig()
|
||||
{
|
||||
var model = ConfigReader.LoadConfig();
|
||||
ConfigModel = model;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 全局配置
|
||||
/// </summary>
|
||||
public AppConfigModel ConfigModel { get; }
|
||||
|
||||
/// <summary>
|
||||
/// WebSocket 端口
|
||||
/// </summary>
|
||||
public int WebSocketPort { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// WebSocket 连接地址
|
||||
/// </summary>
|
||||
public string? WebSocketPath { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="10.0.0-preview.4.25258.110" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="10.0.0-preview.4.25258.110" />
|
||||
<PackageReference Include="Serilog" Version="4.3.1-dev-02373" />
|
||||
<PackageReference Include="Serilog.Enrichers.Environment" Version="3.0.1" />
|
||||
<PackageReference Include="Serilog.Enrichers.Process" Version="3.0.0" />
|
||||
<PackageReference Include="Serilog.Enrichers.Thread" Version="4.0.0" />
|
||||
<PackageReference Include="Serilog.Settings.Configuration" Version="9.0.1-dev-02317" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.1-dev-00953" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="7.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,34 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace Common.Configuration;
|
||||
|
||||
public static class ConfigLoader
|
||||
{
|
||||
private static readonly object _lock = new();
|
||||
private static AppConfig _config;
|
||||
|
||||
public static AppConfig Config
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_config == null)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (_config == null)
|
||||
{
|
||||
var builder = new ConfigurationBuilder()
|
||||
.SetBasePath(AppContext.BaseDirectory)
|
||||
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
|
||||
|
||||
IConfiguration configuration = builder.Build();
|
||||
|
||||
// _config = new AppConfig();
|
||||
configuration.GetSection("AppSettings").Bind(_config);
|
||||
}
|
||||
}
|
||||
}
|
||||
return _config;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using Common.Logger;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace Common.Configuration;
|
||||
|
||||
public static class ConfigReader
|
||||
{
|
||||
public static AppConfigModel LoadConfig()
|
||||
{
|
||||
var env = Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT") ?? "Production";
|
||||
var builder = new ConfigurationBuilder()
|
||||
.SetBasePath(AppContext.BaseDirectory)
|
||||
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
|
||||
.AddJsonFile($"appsettings.{env}.json", optional: true, reloadOnChange: true);
|
||||
|
||||
IConfiguration config = builder.Build();
|
||||
|
||||
var model = new AppConfigModel();
|
||||
config.GetSection("AppSettings").Bind(model);
|
||||
return model;
|
||||
}
|
||||
}
|
||||
|
||||
// 可单独定义模型类用于绑定
|
||||
public class AppConfigModel
|
||||
{
|
||||
public string AppName { get; set; }
|
||||
public string Version { get; set; }
|
||||
public int MaxConnection { get; set; }
|
||||
public LoggerConfig Logging { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
namespace Common.ExpandException;
|
||||
|
||||
public class ExceptionEnum
|
||||
{
|
||||
// region 系统常见错误本地化
|
||||
|
||||
/// <summary>
|
||||
/// 进程信息获取失败
|
||||
/// </summary>
|
||||
public static readonly ExceptionExpand ProcessGetException = new(100001L, "进程消息获取失败,指定线程可能不存在或已关闭!", "以管理员权限重试!");
|
||||
|
||||
|
||||
// endregion
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
namespace Common.ExpandException;
|
||||
|
||||
public class ExceptionExpand : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// 默认异常编码
|
||||
/// </summary>
|
||||
private const long DefaultCode = -1;
|
||||
|
||||
public string ErrorMessage { get; private set; } = "";
|
||||
public long ErrorCode { get; private set; } = DefaultCode;
|
||||
public string Suggestion { get; private set; } = "";
|
||||
|
||||
private ExceptionExpand()
|
||||
{
|
||||
}
|
||||
|
||||
public ExceptionExpand(long errorCode, string massage, string solution) : base(
|
||||
massage)
|
||||
{
|
||||
ErrorCode = errorCode;
|
||||
Suggestion = solution;
|
||||
}
|
||||
|
||||
public ExceptionExpand(long errorCode, string massage, string solution, Exception? innerException) : base(
|
||||
massage, innerException)
|
||||
{
|
||||
ErrorCode = errorCode;
|
||||
Suggestion = solution;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return $"Error Code: {ErrorCode}\nMessage: {Message}\nSuggestion: {Suggestion}\n{base.ToString()}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
using Serilog;
|
||||
|
||||
namespace Common.FunExpand;
|
||||
|
||||
public static class StringExpand
|
||||
{
|
||||
/// <summary>
|
||||
/// 检测是否是 url
|
||||
/// </summary>
|
||||
/// <param name="str"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsValidUrl(this string str)
|
||||
{
|
||||
return Uri.TryCreate(str, UriKind.Absolute, out _);
|
||||
}
|
||||
|
||||
public static int ToInt(this string? str, int defaultValue = default)
|
||||
{
|
||||
return TryToInt(str) ?? defaultValue;
|
||||
}
|
||||
|
||||
public static int? TryToInt(this string? str)
|
||||
{
|
||||
if (String.IsNullOrWhiteSpace(str))
|
||||
{
|
||||
Log.Error($"数据为空, 返回默认值 {null} .");
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return int.Parse(str);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error($"数据 {str} 转换失败, 返回默认值 {null} .");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static double ToDouble(this string? str, double defaultValue = default)
|
||||
{
|
||||
return TryToDouble(str) ?? defaultValue;
|
||||
}
|
||||
|
||||
public static double? TryToDouble(this string? str)
|
||||
{
|
||||
if (String.IsNullOrWhiteSpace(str))
|
||||
{
|
||||
Log.Error($"数据为空, 返回默认值 {null} .");
|
||||
return null;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return double.Parse(str);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Log.Error($"数据 {str} 转换失败, 返回默认值 {null} .");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
using Serilog.Events;
|
||||
|
||||
namespace Common.Logger;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 日志配置类
|
||||
/// </summary>
|
||||
public class LoggerConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// 是否启用控制台输出
|
||||
/// </summary>
|
||||
public bool EnableConsole { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// 是否启用文件输出
|
||||
/// </summary>
|
||||
public bool EnableFile { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// 日志文件路径
|
||||
/// </summary>
|
||||
public string LogPath { get; set; } = "logs";
|
||||
|
||||
/// <summary>
|
||||
/// 单个日志文件最大大小(MB)
|
||||
/// </summary>
|
||||
public int MaxFileSizeMB { get; set; } = 1;
|
||||
|
||||
/// <summary>
|
||||
/// 日志保留天数
|
||||
/// </summary>
|
||||
public int RetentionDays { get; set; } = 30;
|
||||
|
||||
/// <summary>
|
||||
/// 压缩天数阈值(几天前的日志开始压缩)
|
||||
/// </summary>
|
||||
public int CompressDaysThreshold { get; set; } = 3;
|
||||
|
||||
/// <summary>
|
||||
/// 最小日志级别
|
||||
/// </summary>
|
||||
public LogEventLevel MinimumLevel { get; set; } = LogEventLevel.Information;
|
||||
|
||||
/// <summary>
|
||||
/// 日志格式模板
|
||||
/// </summary>
|
||||
public string OutputTemplate { get; set; } = "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} [{Level:u3}] {Message:lj}{NewLine}{Exception}";
|
||||
|
||||
/// <summary>
|
||||
/// 是否启用JSON格式
|
||||
/// </summary>
|
||||
public bool UseJsonFormat { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// 压缩检查间隔(小时)
|
||||
/// </summary>
|
||||
public int CompressCheckIntervalHours { get; set; } = 24;
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
# UniversalLogger - 通用日志记录库
|
||||
|
||||
一个基于Serilog构建的功能完整的C#日志记录库,支持多种输出方式、自动维护、配置灵活等特性。
|
||||
|
||||
## 主要功能
|
||||
|
||||
✅ **多种输出方式**: 支持控制台、文件输出,可单独或同时使用
|
||||
✅ **按日期分割**: 自动按日期创建日志文件
|
||||
✅ **文件大小控制**: 超过指定大小自动创建新文件
|
||||
✅ **自动压缩**: 指定天数后自动压缩旧日志文件
|
||||
✅ **自动清理**: 超过保留期限自动删除旧日志
|
||||
✅ **配置灵活**: 支持代码配置、JSON文件配置
|
||||
✅ **功能完整**: 支持各级别日志、异常堆栈、上下文信息
|
||||
✅ **性能优化**: 基于Serilog高性能日志框架
|
||||
✅ **易于使用**: 提供静态类和实例两种使用方式
|
||||
|
||||
## 安装依赖
|
||||
|
||||
在你的项目中添加以下NuGet包:
|
||||
|
||||
```xml
|
||||
<PackageReference Include="Serilog" Version="3.1.1" />
|
||||
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.1" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="5.0.0" />
|
||||
<PackageReference Include="Serilog.Formatting.Json" Version="1.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
|
||||
```
|
||||
|
||||
## 快速开始
|
||||
|
||||
### 1. 使用默认配置
|
||||
|
||||
```csharp
|
||||
using UniversalLogger;
|
||||
|
||||
// 使用默认配置创建日志记录器
|
||||
using var logger = new UniversalLogger(new LoggerConfig());
|
||||
|
||||
logger.LogInformation("Hello, World!");
|
||||
logger.LogWarning("这是一个警告");
|
||||
logger.LogError("这是一个错误");
|
||||
```
|
||||
|
||||
### 2. 使用静态日志记录器
|
||||
|
||||
```csharp
|
||||
// 初始化静态日志记录器
|
||||
Log.Initialize();
|
||||
|
||||
// 直接使用
|
||||
Log.Information("应用程序启动");
|
||||
Log.Warning("警告信息");
|
||||
Log.Error(exception, "发生错误: {Message}", exception.Message);
|
||||
```
|
||||
|
||||
### 3. 从配置文件加载
|
||||
|
||||
创建 `appsettings.json` 文件:
|
||||
|
||||
```json
|
||||
{
|
||||
"Logging": {
|
||||
"EnableConsole": true,
|
||||
"EnableFile": true,
|
||||
"LogPath": "logs",
|
||||
"MaxFileSizeMB": 1,
|
||||
"RetentionDays": 30,
|
||||
"CompressDaysThreshold": 3,
|
||||
"MinimumLevel": "Information",
|
||||
"OutputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} [{Level:u3}] {Message:lj}{NewLine}{Exception}",
|
||||
"UseJsonFormat": false,
|
||||
"CompressCheckIntervalHours": 24
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
使用配置文件:
|
||||
|
||||
```csharp
|
||||
// 从配置文件初始化
|
||||
Log.InitializeFromJsonFile("appsettings.json");
|
||||
|
||||
Log.Information("使用配置文件的日志记录器");
|
||||
```
|
||||
|
||||
## 配置选项
|
||||
|
||||
| 属性 | 类型 | 默认值 | 说明 |
|
||||
|------|------|--------|------|
|
||||
| `EnableConsole` | bool | true | 是否启用控制台输出 |
|
||||
| `EnableFile` | bool | true | 是否启用文件输出 |
|
||||
| `LogPath` | string | "logs" | 日志文件存储路径 |
|
||||
| `MaxFileSizeMB` | int | 1 | 单个日志文件最大大小(MB) |
|
||||
| `RetentionDays` | int | 30 | 日志保留天
|
||||
@@ -0,0 +1,463 @@
|
||||
using System.IO.Compression;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Serilog;
|
||||
using Serilog.Events;
|
||||
using Serilog.Formatting.Json;
|
||||
|
||||
namespace Common.Logger;
|
||||
|
||||
/// <summary>
|
||||
/// 通用日志记录器
|
||||
/// </summary>
|
||||
public class UniversalLogger : IDisposable
|
||||
{
|
||||
private readonly Serilog.Core.Logger _logger;
|
||||
private readonly LoggerConfig _config;
|
||||
private readonly System.Timers.Timer _maintenanceTimer;
|
||||
private bool _disposed = false;
|
||||
|
||||
public UniversalLogger(LoggerConfig config = null)
|
||||
{
|
||||
_config = config ?? new LoggerConfig();
|
||||
_logger = CreateLogger();
|
||||
|
||||
// 启动维护定时器
|
||||
_maintenanceTimer =
|
||||
new System.Timers.Timer(TimeSpan.FromHours(_config.CompressCheckIntervalHours).TotalMilliseconds);
|
||||
_maintenanceTimer.Elapsed += async (s, e) => await PerformMaintenanceAsync();
|
||||
_maintenanceTimer.Start();
|
||||
|
||||
// 启动时执行一次维护
|
||||
Task.Run(async () => await PerformMaintenanceAsync());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从配置文件创建日志记录器
|
||||
/// </summary>
|
||||
public static UniversalLogger FromConfiguration(IConfiguration configuration, string sectionName = "Logging")
|
||||
{
|
||||
var config = new LoggerConfig();
|
||||
configuration.GetSection(sectionName).Bind(config);
|
||||
return new UniversalLogger(config);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从JSON配置文件创建日志记录器
|
||||
/// </summary>
|
||||
public static UniversalLogger FromJsonFile(string configPath)
|
||||
{
|
||||
var configuration = new ConfigurationBuilder()
|
||||
.AddJsonFile(configPath, optional: false)
|
||||
.Build();
|
||||
return FromConfiguration(configuration);
|
||||
}
|
||||
|
||||
private Serilog.Core.Logger CreateLogger()
|
||||
{
|
||||
var loggerConfig = new LoggerConfiguration()
|
||||
.MinimumLevel.Is(_config.MinimumLevel)
|
||||
.Enrich.WithThreadId()
|
||||
.Enrich.WithProcessId()
|
||||
// .Enrich.WithMachineName() // 推荐添加机器名 enrich 扩展
|
||||
;
|
||||
|
||||
// 配置控制台输出
|
||||
if (_config.EnableConsole)
|
||||
{
|
||||
loggerConfig = loggerConfig.WriteTo.Console(
|
||||
outputTemplate: _config.OutputTemplate,
|
||||
restrictedToMinimumLevel: _config.MinimumLevel);
|
||||
}
|
||||
|
||||
// 配置文件输出
|
||||
if (_config.EnableFile)
|
||||
{
|
||||
Directory.CreateDirectory(_config.LogPath);
|
||||
|
||||
string logFileName = Path.Combine(_config.LogPath, "log-.log");
|
||||
|
||||
if (_config.UseJsonFormat)
|
||||
{
|
||||
loggerConfig = loggerConfig.WriteTo.File(
|
||||
formatter: new JsonFormatter(),
|
||||
path: logFileName,
|
||||
rollingInterval: RollingInterval.Day, // 按天分类
|
||||
rollOnFileSizeLimit: true, // 当日志文件大小超过限制时滚动
|
||||
fileSizeLimitBytes: _config.MaxFileSizeMB * 1024 * 1024, // 日志文件大小限制
|
||||
retainedFileCountLimit: null, // 保留最近7天的日志文件
|
||||
restrictedToMinimumLevel: _config.MinimumLevel);
|
||||
}
|
||||
else
|
||||
{
|
||||
loggerConfig = loggerConfig.WriteTo.File(
|
||||
path: logFileName,
|
||||
outputTemplate: _config.OutputTemplate,
|
||||
rollingInterval: RollingInterval.Day,
|
||||
rollOnFileSizeLimit: true,
|
||||
fileSizeLimitBytes: _config.MaxFileSizeMB * 1024 * 1024,
|
||||
retainedFileCountLimit: null,
|
||||
restrictedToMinimumLevel: _config.MinimumLevel,
|
||||
shared: true // 允许多个应用程序实例共享同一日志文件
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return loggerConfig.CreateLogger();
|
||||
}
|
||||
|
||||
#region 日志记录方法
|
||||
|
||||
public void LogVerbose(string message, params object[] args)
|
||||
{
|
||||
_logger.Verbose(message, args);
|
||||
}
|
||||
|
||||
public void LogDebug(string message, params object[] args)
|
||||
{
|
||||
_logger.Debug(message, args);
|
||||
}
|
||||
|
||||
public void LogInformation(string message, params object[] args)
|
||||
{
|
||||
_logger.Information(message, args);
|
||||
}
|
||||
|
||||
public void LogWarning(string message, params object[] args)
|
||||
{
|
||||
_logger.Warning(message, args);
|
||||
}
|
||||
|
||||
public void LogError(string message, params object[] args)
|
||||
{
|
||||
_logger.Error(message, args);
|
||||
}
|
||||
|
||||
public void LogError(Exception? exception, string message, params object[] args)
|
||||
{
|
||||
_logger.Error(exception, message, args);
|
||||
}
|
||||
|
||||
public void LogFatal(string message, params object[] args)
|
||||
{
|
||||
_logger.Fatal(message, args);
|
||||
}
|
||||
|
||||
public void LogFatal(Exception? exception, string message, params object[] args)
|
||||
{
|
||||
_logger.Fatal(exception, message, args);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取上下文
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public ILogger GetLogger<T>() => _logger.ForContext<T>();
|
||||
|
||||
public ILogger ForContext(string propertyName, object? value)
|
||||
=> _logger.ForContext(propertyName, value);
|
||||
|
||||
/// <summary>
|
||||
/// 记录带上下文信息的日志
|
||||
/// </summary>
|
||||
public void LogWithContext(LogEventLevel level, string message, Dictionary<string, object> context,
|
||||
Exception? exception = null)
|
||||
{
|
||||
var logEvent = new LogEventLevel[] { level };
|
||||
ILogger logger = _logger;
|
||||
|
||||
foreach (var kvp in context ?? new Dictionary<string, object>())
|
||||
{
|
||||
var i = logger.ForContext(kvp.Key, kvp.Value);
|
||||
logger = logger.ForContext(kvp.Key, kvp.Value);
|
||||
}
|
||||
|
||||
if (exception != null)
|
||||
{
|
||||
logger.Write(level, exception, message);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Write(level, message);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 维护功能
|
||||
|
||||
/// <summary>
|
||||
/// 执行日志维护任务
|
||||
/// </summary>
|
||||
private async Task PerformMaintenanceAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_config.EnableFile)
|
||||
{
|
||||
await CompressOldLogsAsync();
|
||||
await DeleteOldLogsAsync();
|
||||
}
|
||||
}
|
||||
catch (Exception? ex)
|
||||
{
|
||||
_logger.Error(ex, "执行日志维护任务时发生错误");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 压缩旧日志文件
|
||||
/// </summary>
|
||||
private async Task CompressOldLogsAsync()
|
||||
{
|
||||
var logDirectory = new DirectoryInfo(_config.LogPath);
|
||||
if (!logDirectory.Exists) return;
|
||||
|
||||
var cutoffDate = DateTime.Now.Date.AddDays(-_config.CompressDaysThreshold);
|
||||
var logFiles = logDirectory.GetFiles("log*.log")
|
||||
.Where(f => f.CreationTime.Date < cutoffDate && !f.Name.EndsWith(".gz"))
|
||||
.ToList();
|
||||
|
||||
foreach (var file in logFiles)
|
||||
{
|
||||
try
|
||||
{
|
||||
var compressedFileName = file.FullName + ".gz";
|
||||
if (File.Exists(compressedFileName)) continue;
|
||||
|
||||
await CompressFileAsync(file.FullName, compressedFileName);
|
||||
File.Delete(file.FullName);
|
||||
|
||||
_logger.Information("已压缩日志文件: {FileName}", file.Name);
|
||||
}
|
||||
catch (Exception? ex)
|
||||
{
|
||||
_logger.Error(ex, "压缩日志文件失败: {FileName}", file.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除过期的日志文件
|
||||
/// </summary>
|
||||
private async Task DeleteOldLogsAsync()
|
||||
{
|
||||
var logDirectory = new DirectoryInfo(_config.LogPath);
|
||||
if (!logDirectory.Exists) return;
|
||||
|
||||
var cutoffDate = DateTime.Now.Date.AddDays(-_config.RetentionDays);
|
||||
var oldFiles = logDirectory.GetFiles("log*.*")
|
||||
.Where(f => f.CreationTime.Date < cutoffDate)
|
||||
.ToList();
|
||||
|
||||
foreach (var file in oldFiles)
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Delete(file.FullName);
|
||||
_logger.Information("已删除过期日志文件: {FileName}", file.Name);
|
||||
}
|
||||
catch (Exception? ex)
|
||||
{
|
||||
_logger.Error(ex, "删除过期日志文件失败: {FileName}", file.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 压缩文件
|
||||
/// </summary>
|
||||
private async Task CompressFileAsync(string sourceFile, string compressedFile)
|
||||
{
|
||||
using (var originalFileStream = File.OpenRead(sourceFile))
|
||||
using (var compressedFileStream = File.Create(compressedFile))
|
||||
using (var compressionStream = new GZipStream(compressedFileStream, CompressionMode.Compress))
|
||||
{
|
||||
await originalFileStream.CopyToAsync(compressionStream);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 手动执行维护任务
|
||||
/// </summary>
|
||||
public async Task RunMaintenanceAsync()
|
||||
{
|
||||
await PerformMaintenanceAsync();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IDisposable
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (!_disposed)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_maintenanceTimer?.Stop();
|
||||
_maintenanceTimer?.Dispose();
|
||||
_logger?.Dispose();
|
||||
}
|
||||
|
||||
_disposed = true;
|
||||
}
|
||||
}
|
||||
|
||||
~UniversalLogger()
|
||||
{
|
||||
Dispose(false);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 静态日志记录器(单例模式)
|
||||
/// </summary>
|
||||
public static class Log
|
||||
{
|
||||
private static UniversalLogger _instance;
|
||||
private static readonly object _lock = new object();
|
||||
|
||||
/// <summary>
|
||||
/// 初始化日志记录器
|
||||
/// </summary>
|
||||
public static void Initialize(LoggerConfig config = null)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_instance?.Dispose();
|
||||
_instance = new UniversalLogger(config);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从配置文件初始化
|
||||
/// </summary>
|
||||
public static void InitializeFromConfig(IConfiguration configuration, string sectionName = "Logging")
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_instance?.Dispose();
|
||||
_instance = UniversalLogger.FromConfiguration(configuration, sectionName);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从JSON文件初始化
|
||||
/// </summary>
|
||||
public static void InitializeFromJsonFile(string configPath)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_instance?.Dispose();
|
||||
_instance = UniversalLogger.FromJsonFile(configPath);
|
||||
}
|
||||
}
|
||||
|
||||
private static UniversalLogger Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
if (_instance == null)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (_instance == null)
|
||||
{
|
||||
_instance = new UniversalLogger();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _instance;
|
||||
}
|
||||
}
|
||||
|
||||
public static void Verbose(string message, params object[] args) => Instance.LogVerbose(message, args);
|
||||
|
||||
public static void Verbose4Ctx(string message, [CallerMemberName] string callerMemberName = "",
|
||||
[CallerFilePath] string filePath = "", [CallerLineNumber] int callerLineNumber = 0)
|
||||
{
|
||||
var str = GetLog(callerMemberName, Path.GetFileName(filePath), callerLineNumber, message);
|
||||
Verbose(str);
|
||||
}
|
||||
|
||||
public static void Debug(string message, params object[] args) => Instance.LogDebug(message, args);
|
||||
|
||||
public static void Debug4Ctx(string message, [CallerMemberName] string callerMemberName = "",
|
||||
[CallerFilePath] string filePath = "", [CallerLineNumber] int callerLineNumber = 0)
|
||||
{
|
||||
var str = GetLog(callerMemberName, Path.GetFileName(filePath), callerLineNumber, message);
|
||||
Debug(str);
|
||||
}
|
||||
|
||||
public static void Information(string message, params object[] args) => Instance.LogInformation(message, args);
|
||||
|
||||
public static void Information4Ctx(string message, [CallerMemberName] string callerMemberName = "",
|
||||
[CallerFilePath] string filePath = "", [CallerLineNumber] int callerLineNumber = 0)
|
||||
{
|
||||
var str = GetLog(callerMemberName, Path.GetFileName(filePath), callerLineNumber, message);
|
||||
Information(str);
|
||||
}
|
||||
|
||||
public static void Warning(string message, params object[] args) => Instance.LogWarning(message, args);
|
||||
|
||||
public static void Warning4Ctx(string message, [CallerMemberName] string callerMemberName = "",
|
||||
[CallerFilePath] string filePath = "", [CallerLineNumber] int callerLineNumber = 0)
|
||||
{
|
||||
var str = GetLog(callerMemberName, Path.GetFileName(filePath), callerLineNumber, message);
|
||||
Warning(str);
|
||||
}
|
||||
|
||||
public static void Error(string message, params object[] args) => Instance.LogError(message, args);
|
||||
|
||||
public static void Error4Ctx(string message, [CallerMemberName] string callerMemberName = "",
|
||||
[CallerFilePath] string filePath = "", [CallerLineNumber] int callerLineNumber = 0)
|
||||
{
|
||||
var str = GetLog(callerMemberName, Path.GetFileName(filePath), callerLineNumber, message);
|
||||
Error(str);
|
||||
}
|
||||
|
||||
|
||||
public static void Error(Exception? exception, string message, [CallerMemberName] string callerMemberName = "",
|
||||
[CallerFilePath] string filePath = "", [CallerLineNumber] int callerLineNumber = 0)
|
||||
{
|
||||
var str = GetLog(callerMemberName, Path.GetFileName(filePath), callerLineNumber, message);
|
||||
Instance.LogError(exception, str);
|
||||
}
|
||||
|
||||
public static void Fatal(string message, params object[] args) => Instance.LogFatal(message, args);
|
||||
|
||||
public static void Fatal4Ctx(string message, [CallerMemberName] string callerMemberName = "",
|
||||
[CallerFilePath] string filePath = "", [CallerLineNumber] int callerLineNumber = 0)
|
||||
{
|
||||
var str = GetLog(callerMemberName, Path.GetFileName(filePath), callerLineNumber, message);
|
||||
Fatal(str);
|
||||
}
|
||||
|
||||
public static void Fatal(Exception? exception, string message, [CallerMemberName] string callerMemberName = "",
|
||||
[CallerFilePath] string filePath = "", [CallerLineNumber] int callerLineNumber = 0)
|
||||
{
|
||||
var str = GetLog(callerMemberName, Path.GetFileName(filePath), callerLineNumber, message);
|
||||
Instance.LogFatal(exception, str);
|
||||
}
|
||||
|
||||
private static string GetLog(string callerMemberName, string getFileName, int callerLineNumber, string message)
|
||||
{
|
||||
return $"[{callerMemberName}] - {getFileName}:{callerLineNumber} :{message}";
|
||||
}
|
||||
|
||||
public static void WithContext(LogEventLevel level, string message, Dictionary<string, object> context,
|
||||
Exception? exception = null)
|
||||
=> Instance.LogWithContext(level, message, context, exception);
|
||||
|
||||
public static async Task RunMaintenanceAsync() => await Instance.RunMaintenanceAsync();
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace Common.Monitors;
|
||||
|
||||
public class ApplicationHealthMonitor
|
||||
{
|
||||
private readonly Timer _healthCheckTimer;
|
||||
|
||||
public ApplicationHealthMonitor()
|
||||
{
|
||||
_healthCheckTimer = new Timer(CheckApplicationHealth, null,
|
||||
TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(5));
|
||||
}
|
||||
|
||||
private void CheckApplicationHealth(object state)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 检查内存使用
|
||||
var memoryUsage = GC.GetTotalMemory(false);
|
||||
if (memoryUsage > 500 * 1024 * 1024) // 500MB
|
||||
{
|
||||
// Logger.Warn($"内存使用过高: {memoryUsage / 1024 / 1024}MB");
|
||||
GC.Collect(); // 强制垃圾回收
|
||||
}
|
||||
|
||||
// 检查线程数量
|
||||
var threadCount = Process.GetCurrentProcess().Threads.Count;
|
||||
if (threadCount > 100)
|
||||
{
|
||||
// Logger.Warn($"线程数量过多: {threadCount}");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// LogException(ex, "健康检查异常");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
| 类别 | 示例 |
|
||||
| -------- | --------------------------------------------- |
|
||||
| ✅ 配置封装 | `AppConfig`, `ConfigReader` |
|
||||
| ✅ 通用工具类 | 字符串处理、加密、哈希、路径处理 |
|
||||
| ✅ 基础模型类 | `Result<T>`, `PagedList<T>`, `ErrorInfo` |
|
||||
| ✅ 通用扩展方法 | `string.ToSafeInt()`, `DateTime.ToUnixTime()` |
|
||||
| ✅ 接口定义 | 如 `INetworkMonitor`, `ILogger`(便于依赖注入) |
|
||||
| ✅ 网络协议模型 | 公共 API 的数据结构定义(DTO/VO) |
|
||||
| ✅ 常量/枚举 | 错误码、状态码、常量定义 |
|
||||
@@ -0,0 +1,487 @@
|
||||
// namespace Common.TaskQueue;
|
||||
//
|
||||
// using System;
|
||||
// using System.Collections.Concurrent;
|
||||
// using System.Collections.Generic;
|
||||
// using System.Linq;
|
||||
// using System.Threading;
|
||||
// using System.Threading.Channels;
|
||||
// using System.Threading.Tasks;
|
||||
//
|
||||
// // 1. 任务接口定义
|
||||
// public interface ITaskItem
|
||||
// {
|
||||
// string TaskId { get; }
|
||||
// TaskPriority Priority { get; }
|
||||
// DateTime CreatedAt { get; }
|
||||
//
|
||||
// /// <summary>
|
||||
// /// 异步执行方法
|
||||
// /// </summary>
|
||||
// /// <param name="cancellationToken"></param>
|
||||
// /// <returns></returns>
|
||||
// Task ExecuteAsync(CancellationToken cancellationToken = default);
|
||||
// }
|
||||
//
|
||||
// public enum TaskPriority
|
||||
// {
|
||||
// Low = 0,
|
||||
// Normal = 1,
|
||||
// High = 2,
|
||||
// Critical = 3
|
||||
// }
|
||||
//
|
||||
// // 2. 基础任务实现
|
||||
// public abstract class BaseTask : ITaskItem
|
||||
// {
|
||||
// public string TaskId { get; } = Guid.NewGuid().ToString();
|
||||
// public TaskPriority Priority { get; protected set; } = TaskPriority.Normal;
|
||||
// public DateTime CreatedAt { get; } = DateTime.UtcNow;
|
||||
//
|
||||
// public abstract Task ExecuteAsync(CancellationToken cancellationToken = default);
|
||||
// }
|
||||
//
|
||||
// // 3. ETW网络连接任务示例
|
||||
// public class NetworkConnectionTask : BaseTask
|
||||
// {
|
||||
// private readonly NetworkConnectionData _data;
|
||||
// private readonly INetworkDataStore _dataStore;
|
||||
//
|
||||
// public NetworkConnectionTask(NetworkConnectionData data, INetworkDataStore dataStore)
|
||||
// {
|
||||
// _data = data;
|
||||
// _dataStore = dataStore;
|
||||
// Priority = TaskPriority.High; // 网络事件优先级较高
|
||||
// }
|
||||
//
|
||||
// public override async Task ExecuteAsync(CancellationToken cancellationToken = default)
|
||||
// {
|
||||
// // 处理网络连接数据
|
||||
// await _dataStore.UpdateNetworkConnectionAsync(_data);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // 4. 数据库写入任务示例
|
||||
// public class DatabaseWriteTask : BaseTask
|
||||
// {
|
||||
// private readonly object _data;
|
||||
// private readonly Func<object, CancellationToken, Task> _writeAction;
|
||||
//
|
||||
// public DatabaseWriteTask(object data, Func<object, CancellationToken, Task> writeAction)
|
||||
// {
|
||||
// _data = data;
|
||||
// _writeAction = writeAction;
|
||||
// Priority = TaskPriority.Normal;
|
||||
// }
|
||||
//
|
||||
// public override async Task ExecuteAsync(CancellationToken cancellationToken = default)
|
||||
// {
|
||||
// await _writeAction(_data, cancellationToken);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // 5. 任务队列配置
|
||||
// public class TaskQueueOptions
|
||||
// {
|
||||
// /// <summary>
|
||||
// /// 默认并发数 = CPU核心数
|
||||
// /// </summary>
|
||||
// public int MaxConcurrency { get; set; } = Environment.ProcessorCount;
|
||||
//
|
||||
// /// <summary>
|
||||
// /// 队列最大容量
|
||||
// /// </summary>
|
||||
// public int MaxQueueSize { get; set; } = 10000;
|
||||
//
|
||||
// /// <summary>
|
||||
// /// 单个任务超时时间
|
||||
// /// </summary>
|
||||
// public TimeSpan TaskTimeout { get; set; } = TimeSpan.FromMinutes(5);
|
||||
//
|
||||
// /// <summary>
|
||||
// /// 是否启用优先级处理
|
||||
// /// </summary>
|
||||
// public bool EnablePriorityProcessing { get; set; } = true;
|
||||
// }
|
||||
//
|
||||
// // 6. 核心任务队列实现
|
||||
// public class TaskQueue : ITaskQueue, IDisposable
|
||||
// {
|
||||
// /// <summary>
|
||||
// /// 高性能队列通道
|
||||
// /// </summary>
|
||||
// private readonly Channel<ITaskItem> _taskChannel;
|
||||
//
|
||||
// private readonly ChannelWriter<ITaskItem> _writer;
|
||||
// private readonly ChannelReader<ITaskItem> _reader;
|
||||
// private readonly TaskQueueOptions _options;
|
||||
//
|
||||
// /// <summary>
|
||||
// /// 并发控制信号量
|
||||
// /// </summary>
|
||||
// private readonly SemaphoreSlim _concurrencySemaphore;
|
||||
//
|
||||
// /// <summary>
|
||||
// /// 正在运行的任务字典
|
||||
// /// </summary>
|
||||
// private readonly ConcurrentDictionary<string, TaskInfo> _runningTasks;
|
||||
//
|
||||
// /// <summary>
|
||||
// /// 取消令牌源
|
||||
// /// </summary>
|
||||
// private readonly CancellationTokenSource _cancellationTokenSource;
|
||||
//
|
||||
// /// <summary>
|
||||
// /// 工作线程数组
|
||||
// /// </summary>
|
||||
// private readonly Task[] _workerTasks;
|
||||
//
|
||||
// private bool _disposed = false;
|
||||
//
|
||||
// public TaskQueue(TaskQueueOptions options = null)
|
||||
// {
|
||||
// _options = options ?? new TaskQueueOptions();
|
||||
//
|
||||
// var channelOptions = new BoundedChannelOptions(_options.MaxQueueSize)
|
||||
// {
|
||||
// FullMode = BoundedChannelFullMode.Wait, // 队列满时等待,而不是丢弃
|
||||
// SingleReader = false, // 多个读取者(工作线程)
|
||||
// SingleWriter = false // 多个写入者(生产者)
|
||||
// };
|
||||
//
|
||||
// _taskChannel = Channel.CreateBounded<ITaskItem>(channelOptions);
|
||||
// _writer = _taskChannel.Writer;
|
||||
// _reader = _taskChannel.Reader;
|
||||
//
|
||||
// _concurrencySemaphore = new SemaphoreSlim(_options.MaxConcurrency, _options.MaxConcurrency);
|
||||
// _runningTasks = new ConcurrentDictionary<string, TaskInfo>();
|
||||
// _cancellationTokenSource = new CancellationTokenSource();
|
||||
//
|
||||
// // 启动工作线程
|
||||
// _workerTasks = new Task[_options.MaxConcurrency];
|
||||
// for (int i = 0; i < _options.MaxConcurrency; i++)
|
||||
// {
|
||||
// _workerTasks[i] = Task.Run(() => ProcessTasksAsync(_cancellationTokenSource.Token));
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// /// <summary>
|
||||
// /// 任务入队方法
|
||||
// /// </summary>
|
||||
// /// <param name="task"></param>
|
||||
// /// <param name="cancellationToken"></param>
|
||||
// /// <returns></returns>
|
||||
// public async Task<bool> EnqueueAsync(ITaskItem task, CancellationToken cancellationToken = default)
|
||||
// {
|
||||
// if (_disposed)
|
||||
// return false;
|
||||
//
|
||||
// try
|
||||
// {
|
||||
// // 异步写入,支持取消
|
||||
// await _writer.WriteAsync(task, cancellationToken);
|
||||
// return true;
|
||||
// }
|
||||
// catch (Exception)
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public TaskStatistics GetStatistics()
|
||||
// {
|
||||
// return new TaskStatistics
|
||||
// {
|
||||
// QueuedTasks = _taskChannel.Reader.Count,
|
||||
// RunningTasks = _runningTasks.Count,
|
||||
// AvailableWorkers = _concurrencySemaphore.CurrentCount
|
||||
// };
|
||||
// }
|
||||
//
|
||||
// public async Task StopAsync()
|
||||
// {
|
||||
// _writer.Complete();
|
||||
// _cancellationTokenSource.Cancel();
|
||||
//
|
||||
// try
|
||||
// {
|
||||
// await Task.WhenAll(_workerTasks);
|
||||
// }
|
||||
// catch (OperationCanceledException)
|
||||
// {
|
||||
// // 预期的取消异常
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// protected async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
// {
|
||||
// var tasks = new List<Task>();
|
||||
//
|
||||
// // 创建多个工作任务
|
||||
// for (int i = 0; i < _options.MaxConcurrency; i++)
|
||||
// {
|
||||
// tasks.Add(ProcessTasksAsync(stoppingToken));
|
||||
// }
|
||||
//
|
||||
// await Task.WhenAll(tasks);
|
||||
// }
|
||||
//
|
||||
// /// <summary>
|
||||
// /// 异步任务处理
|
||||
// /// </summary>
|
||||
// /// <param name="cancellationToken"></param>
|
||||
// private async Task ProcessTasksAsync(CancellationToken cancellationToken)
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// // 异步枚举,从Channel读取任务
|
||||
// await foreach (var task in _reader.ReadAllAsync(cancellationToken))
|
||||
// {
|
||||
// // 1. 获取信号量,控制并发数
|
||||
// await _concurrencySemaphore.WaitAsync(cancellationToken);
|
||||
//
|
||||
// // 2. 记录任务信息
|
||||
// var taskInfo = new TaskInfo
|
||||
// {
|
||||
// Task = task,
|
||||
// StartTime = DateTime.UtcNow
|
||||
// };
|
||||
//
|
||||
// _runningTasks.TryAdd(task.TaskId, taskInfo);
|
||||
//
|
||||
// // 异步执行任务,不阻塞队列处理
|
||||
// _ = Task.Run(async () =>
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// // 创建带超时的取消令牌
|
||||
// using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
|
||||
// cts.CancelAfter(_options.TaskTimeout);
|
||||
//
|
||||
// await task.ExecuteAsync(cts.Token);
|
||||
// }
|
||||
// catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
|
||||
// {
|
||||
// // 任务被取消
|
||||
// }
|
||||
// catch (Exception)
|
||||
// {
|
||||
// // 任务执行失败,静默处理
|
||||
// }
|
||||
// finally
|
||||
// {
|
||||
// _runningTasks.TryRemove(task.TaskId, out _);
|
||||
// _concurrencySemaphore.Release();
|
||||
// }
|
||||
// }, cancellationToken);
|
||||
// }
|
||||
// }
|
||||
// catch (OperationCanceledException)
|
||||
// {
|
||||
// // 工作线程被取消
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// public void Dispose()
|
||||
// {
|
||||
// if (_disposed)
|
||||
// return;
|
||||
//
|
||||
// _disposed = true;
|
||||
// _writer.Complete();
|
||||
// _cancellationTokenSource.Cancel();
|
||||
//
|
||||
// try
|
||||
// {
|
||||
// Task.WaitAll(_workerTasks, TimeSpan.FromSeconds(5));
|
||||
// }
|
||||
// catch (Exception)
|
||||
// {
|
||||
// // 忽略关闭时的异常
|
||||
// }
|
||||
//
|
||||
// _concurrencySemaphore?.Dispose();
|
||||
// _cancellationTokenSource?.Dispose();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // 7. 接口定义
|
||||
// public interface ITaskQueue
|
||||
// {
|
||||
// Task<bool> EnqueueAsync(ITaskItem task, CancellationToken cancellationToken = default);
|
||||
// TaskStatistics GetStatistics();
|
||||
// Task StopAsync();
|
||||
// }
|
||||
//
|
||||
// // 8. 辅助类
|
||||
// public class TaskInfo
|
||||
// {
|
||||
// public ITaskItem Task { get; set; }
|
||||
// public DateTime StartTime { get; set; }
|
||||
// }
|
||||
//
|
||||
// public class TaskStatistics
|
||||
// {
|
||||
// public int QueuedTasks { get; set; }
|
||||
// public int RunningTasks { get; set; }
|
||||
// public int AvailableWorkers { get; set; }
|
||||
// }
|
||||
//
|
||||
// // 9. 网络数据存储接口(单例数据存储)
|
||||
// public interface INetworkDataStore
|
||||
// {
|
||||
// Task UpdateNetworkConnectionAsync(NetworkConnectionData data);
|
||||
// Task<IEnumerable<NetworkConnectionData>> GetRecentConnectionsAsync(TimeSpan timeSpan);
|
||||
// Task<NetworkStatistics> GetStatisticsAsync();
|
||||
// }
|
||||
//
|
||||
// // 10. 网络数据存储单例实现
|
||||
// public class NetworkDataStore : INetworkDataStore
|
||||
// {
|
||||
// private readonly ConcurrentQueue<NetworkConnectionData> _recentConnections; // 最近连接队列
|
||||
// private readonly ConcurrentDictionary<string, ConnectionStatistics> _connectionStats; // 连接统计
|
||||
// private readonly ReaderWriterLockSlim _lock; // 读写锁
|
||||
// private readonly Timer _cleanupTimer; // 清理定时器
|
||||
// public NetworkDataStore()
|
||||
// {
|
||||
// _recentConnections = new ConcurrentQueue<NetworkConnectionData>();
|
||||
// _connectionStats = new ConcurrentDictionary<string, ConnectionStatistics>();
|
||||
// _lock = new ReaderWriterLockSlim();
|
||||
//
|
||||
// // 定期清理过期数据
|
||||
// _cleanupTimer = new Timer(CleanupExpiredData, null,
|
||||
// TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1));
|
||||
// }
|
||||
//
|
||||
// public async Task UpdateNetworkConnectionAsync(NetworkConnectionData data)
|
||||
// {
|
||||
// await Task.Run(() =>
|
||||
// {
|
||||
// _recentConnections.Enqueue(data);
|
||||
//
|
||||
// // 更新统计信息
|
||||
// var key = $"{data.ProcessName}:{data.LocalPort}";
|
||||
// _connectionStats.AddOrUpdate(key,
|
||||
// new ConnectionStatistics { Count = 1, LastSeen = DateTime.UtcNow },
|
||||
// (k, v) => new ConnectionStatistics { Count = v.Count + 1, LastSeen = DateTime.UtcNow });
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// public async Task<IEnumerable<NetworkConnectionData>> GetRecentConnectionsAsync(TimeSpan timeSpan)
|
||||
// {
|
||||
// return await Task.FromResult(
|
||||
// _recentConnections
|
||||
// .Where(c => DateTime.UtcNow - c.Timestamp <= timeSpan)
|
||||
// .ToList());
|
||||
// }
|
||||
//
|
||||
// public async Task<NetworkStatistics> GetStatisticsAsync()
|
||||
// {
|
||||
// return await Task.FromResult(new NetworkStatistics
|
||||
// {
|
||||
// TotalConnections = _recentConnections.Count,
|
||||
// ActiveProcesses = _connectionStats.Count,
|
||||
// LastUpdate = DateTime.UtcNow
|
||||
// });
|
||||
// }
|
||||
//
|
||||
// private void CleanupExpiredData(object state)
|
||||
// {
|
||||
// var cutoff = DateTime.UtcNow.AddHours(-1);
|
||||
//
|
||||
// // 清理过期连接数据
|
||||
// while (_recentConnections.TryPeek(out var connection) &&
|
||||
// connection.Timestamp < cutoff)
|
||||
// {
|
||||
// _recentConnections.TryDequeue(out _);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // 11. 数据模型
|
||||
// public class NetworkConnectionData
|
||||
// {
|
||||
// public string ProcessName { get; set; }
|
||||
// public int ProcessId { get; set; }
|
||||
// public string LocalAddress { get; set; }
|
||||
// public int LocalPort { get; set; }
|
||||
// public string RemoteAddress { get; set; }
|
||||
// public int RemotePort { get; set; }
|
||||
// public DateTime Timestamp { get; set; } = DateTime.UtcNow;
|
||||
// public string Protocol { get; set; }
|
||||
// }
|
||||
//
|
||||
// public class ConnectionStatistics
|
||||
// {
|
||||
// public int Count { get; set; }
|
||||
// public DateTime LastSeen { get; set; }
|
||||
// }
|
||||
//
|
||||
// public class NetworkStatistics
|
||||
// {
|
||||
// public int TotalConnections { get; set; }
|
||||
// public int ActiveProcesses { get; set; }
|
||||
// public DateTime LastUpdate { get; set; }
|
||||
// }
|
||||
//
|
||||
// // 12. 简单使用示例
|
||||
// public class TaskQueueManager
|
||||
// {
|
||||
// private readonly TaskQueue _taskQueue;
|
||||
// private readonly NetworkDataStore _dataStore;
|
||||
//
|
||||
// public TaskQueueManager()
|
||||
// {
|
||||
// var options = new TaskQueueOptions
|
||||
// {
|
||||
// MaxConcurrency = Environment.ProcessorCount,
|
||||
// MaxQueueSize = 5000
|
||||
// };
|
||||
//
|
||||
// _taskQueue = new TaskQueue(options);
|
||||
// _dataStore = new NetworkDataStore();
|
||||
// }
|
||||
//
|
||||
// // ETW事件处理
|
||||
// public async Task OnNetworkConnectionEvent(NetworkConnectionData connectionData)
|
||||
// {
|
||||
// var task = new NetworkConnectionTask(connectionData, _dataStore);
|
||||
// await _taskQueue.EnqueueAsync(task);
|
||||
// }
|
||||
//
|
||||
// // 数据库写入
|
||||
// public async Task SaveToDatabase(object data)
|
||||
// {
|
||||
// var dbTask = new DatabaseWriteTask(data, async (d, ct) =>
|
||||
// {
|
||||
// // 实际的数据库写入逻辑
|
||||
// await WriteToDatabase(d);
|
||||
// });
|
||||
//
|
||||
// await _taskQueue.EnqueueAsync(dbTask);
|
||||
// }
|
||||
//
|
||||
// // 获取统计信息
|
||||
// public TaskStatistics GetQueueStatistics()
|
||||
// {
|
||||
// return _taskQueue.GetStatistics();
|
||||
// }
|
||||
//
|
||||
// // 获取网络数据
|
||||
// public async Task<IEnumerable<NetworkConnectionData>> GetRecentConnections(TimeSpan timeSpan)
|
||||
// {
|
||||
// return await _dataStore.GetRecentConnectionsAsync(timeSpan);
|
||||
// }
|
||||
//
|
||||
// private async Task WriteToDatabase(object data)
|
||||
// {
|
||||
// // 数据库写入实现
|
||||
// await Task.Delay(100); // 模拟数据库操作
|
||||
// }
|
||||
//
|
||||
// public async Task StopAsync()
|
||||
// {
|
||||
// await _taskQueue.StopAsync();
|
||||
// _taskQueue.Dispose();
|
||||
// }
|
||||
// }
|
||||
@@ -0,0 +1,724 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Channels;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
// 1. 任务接口定义
|
||||
public interface ITaskItem
|
||||
{
|
||||
string TaskId { get; }
|
||||
TaskPriority Priority { get; }
|
||||
DateTime CreatedAt { get; }
|
||||
int SequenceNumber { get; set; } // 用于严格排序
|
||||
string GroupId { get; } // 用于分组排序
|
||||
Task ExecuteAsync(CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
public enum TaskPriority
|
||||
{
|
||||
Low = 0,
|
||||
Normal = 1,
|
||||
High = 2,
|
||||
Critical = 3,
|
||||
Urgent = 4 // 最高优先级,用于插队
|
||||
}
|
||||
|
||||
// 2. 基础任务实现
|
||||
public abstract class BaseTask : ITaskItem
|
||||
{
|
||||
public string TaskId { get; } = Guid.NewGuid().ToString();
|
||||
public TaskPriority Priority { get; set; } = TaskPriority.Normal;
|
||||
public DateTime CreatedAt { get; } = DateTime.UtcNow;
|
||||
public int SequenceNumber { get; set; } = 0;
|
||||
public string GroupId { get; set; } = null;
|
||||
|
||||
public abstract Task ExecuteAsync(CancellationToken cancellationToken = default);
|
||||
}
|
||||
|
||||
// 3. 支持分组的任务基类
|
||||
public abstract class GroupedTask : BaseTask
|
||||
{
|
||||
protected GroupedTask(string? groupId)
|
||||
{
|
||||
GroupId = groupId ?? throw new ArgumentNullException(nameof(groupId));
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 网络连接任务(支持分组)
|
||||
public class NetworkConnectionTask : GroupedTask
|
||||
{
|
||||
private readonly NetworkConnectionData _data;
|
||||
private readonly INetworkDataStore _dataStore;
|
||||
|
||||
public NetworkConnectionTask(NetworkConnectionData data, INetworkDataStore dataStore, string? groupId = null)
|
||||
: base(groupId ?? $"network_{data.ProcessName}")
|
||||
{
|
||||
_data = data;
|
||||
_dataStore = dataStore;
|
||||
Priority = TaskPriority.High;
|
||||
}
|
||||
|
||||
public override async Task ExecuteAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
await _dataStore.UpdateNetworkConnectionAsync(_data);
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 数据库写入任务
|
||||
public class DatabaseWriteTask : BaseTask
|
||||
{
|
||||
private readonly object _data;
|
||||
private readonly Func<object, CancellationToken, Task> _writeAction;
|
||||
|
||||
public DatabaseWriteTask(object data, Func<object, CancellationToken, Task> writeAction,
|
||||
TaskPriority priority = TaskPriority.Normal)
|
||||
{
|
||||
_data = data;
|
||||
_writeAction = writeAction;
|
||||
Priority = priority;
|
||||
}
|
||||
|
||||
public override async Task ExecuteAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
await _writeAction(_data, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
// 6. 任务队列配置
|
||||
public class TaskQueueOptions
|
||||
{
|
||||
public int MaxConcurrency { get; set; } = Environment.ProcessorCount;
|
||||
public int MaxQueueSize { get; set; } = 10000;
|
||||
public TimeSpan TaskTimeout { get; set; } = TimeSpan.FromMinutes(5);
|
||||
public bool EnablePriorityProcessing { get; set; } = true;
|
||||
public bool EnableGroupedExecution { get; set; } = true; // 是否启用分组执行
|
||||
public TimeSpan StatisticsUpdateInterval { get; set; } = TimeSpan.FromSeconds(1);
|
||||
}
|
||||
|
||||
// 7. 任务统计信息
|
||||
public class TaskStatistics
|
||||
{
|
||||
public int QueuedTasks { get; set; }
|
||||
public int RunningTasks { get; set; }
|
||||
public int AvailableWorkers { get; set; }
|
||||
public long TotalEnqueued { get; set; } // 总入队任务数
|
||||
public long TotalCompleted { get; set; } // 总完成任务数
|
||||
public long TotalFailed { get; set; } // 总失败任务数
|
||||
public double AverageExecutionTime { get; set; } // 平均执行时间(毫秒)
|
||||
public Dictionary<TaskPriority, int> QueuedByPriority { get; set; } = new();
|
||||
public Dictionary<string, int> QueuedByGroup { get; set; } = new();
|
||||
public DateTime LastUpdated { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
|
||||
// 8. 任务信息
|
||||
public class TaskInfo
|
||||
{
|
||||
public ITaskItem Task { get; set; }
|
||||
public DateTime StartTime { get; set; }
|
||||
public DateTime? EndTime { get; set; }
|
||||
public bool IsCompleted { get; set; }
|
||||
public bool IsFailed { get; set; }
|
||||
public Exception Exception { get; set; }
|
||||
|
||||
public TimeSpan ExecutionTime => EndTime.HasValue ? EndTime.Value - StartTime : DateTime.UtcNow - StartTime;
|
||||
}
|
||||
|
||||
// 9. 优先级任务比较器
|
||||
public class TaskPriorityComparer : IComparer<ITaskItem>
|
||||
{
|
||||
public int Compare(ITaskItem x, ITaskItem y)
|
||||
{
|
||||
if (x == null && y == null) return 0;
|
||||
if (x == null) return 1;
|
||||
if (y == null) return -1;
|
||||
|
||||
// 1. 先按优先级排序(优先级高的在前)
|
||||
var priorityComparison = y.Priority.CompareTo(x.Priority);
|
||||
if (priorityComparison != 0) return priorityComparison;
|
||||
|
||||
// 2. 同优先级按序列号排序
|
||||
if (x.SequenceNumber != 0 || y.SequenceNumber != 0)
|
||||
{
|
||||
return x.SequenceNumber.CompareTo(y.SequenceNumber);
|
||||
}
|
||||
|
||||
// 3. 最后按创建时间排序(FIFO)
|
||||
return x.CreatedAt.CompareTo(y.CreatedAt);
|
||||
}
|
||||
}
|
||||
|
||||
// 10. 增强的任务队列实现
|
||||
public class EnhancedTaskQueue : ITaskQueue, IDisposable
|
||||
{
|
||||
private readonly PriorityQueue<ITaskItem, ITaskItem> _priorityQueue;
|
||||
private readonly TaskQueueOptions _options;
|
||||
private readonly SemaphoreSlim _concurrencySemaphore;
|
||||
private readonly ConcurrentDictionary<string, TaskInfo> _runningTasks;
|
||||
private readonly ConcurrentDictionary<string, SemaphoreSlim> _groupSemaphores;
|
||||
private readonly CancellationTokenSource _cancellationTokenSource;
|
||||
private readonly Task[] _workerTasks;
|
||||
private readonly ReaderWriterLockSlim _queueLock;
|
||||
private readonly Timer _statisticsTimer;
|
||||
|
||||
// 统计数据
|
||||
private long _totalEnqueued = 0;
|
||||
private long _totalCompleted = 0;
|
||||
private long _totalFailed = 0;
|
||||
private readonly ConcurrentQueue<double> _executionTimes = new();
|
||||
|
||||
/// <summary>
|
||||
/// 当前序列号
|
||||
/// </summary>
|
||||
private int _currentSequenceNumber = 0;
|
||||
|
||||
private bool _disposed = false;
|
||||
private readonly ManualResetEventSlim _newTaskEvent;
|
||||
|
||||
public EnhancedTaskQueue(TaskQueueOptions? options = null)
|
||||
{
|
||||
_options = options ?? new TaskQueueOptions();
|
||||
|
||||
_priorityQueue = new PriorityQueue<ITaskItem, ITaskItem>(new TaskPriorityComparer());
|
||||
_concurrencySemaphore = new SemaphoreSlim(_options.MaxConcurrency, _options.MaxConcurrency);
|
||||
_runningTasks = new ConcurrentDictionary<string, TaskInfo>();
|
||||
_groupSemaphores = new ConcurrentDictionary<string, SemaphoreSlim>();
|
||||
_cancellationTokenSource = new CancellationTokenSource();
|
||||
_queueLock = new ReaderWriterLockSlim();
|
||||
_newTaskEvent = new ManualResetEventSlim(false);
|
||||
|
||||
// 启动统计更新定时器
|
||||
_statisticsTimer = new Timer(UpdateStatistics, null,
|
||||
_options.StatisticsUpdateInterval, _options.StatisticsUpdateInterval);
|
||||
|
||||
// 启动工作线程
|
||||
_workerTasks = new Task[_options.MaxConcurrency];
|
||||
for (int i = 0; i < _options.MaxConcurrency; i++)
|
||||
{
|
||||
_workerTasks[i] = Task.Run(() => ProcessTasksAsync(_cancellationTokenSource.Token));
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> EnqueueAsync(ITaskItem task, CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (_disposed) return false;
|
||||
|
||||
try
|
||||
{
|
||||
_queueLock.EnterWriteLock();
|
||||
|
||||
// 检查队列大小
|
||||
if (_priorityQueue.Count >= _options.MaxQueueSize)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// 如果没有设置序列号,自动分配
|
||||
if (task.SequenceNumber == 0)
|
||||
{
|
||||
task.SequenceNumber = Interlocked.Increment(ref _currentSequenceNumber);
|
||||
}
|
||||
|
||||
_priorityQueue.Enqueue(task, task);
|
||||
Interlocked.Increment(ref _totalEnqueued);
|
||||
|
||||
_newTaskEvent.Set(); // 通知工作线程有新任务
|
||||
return true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_queueLock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<bool> EnqueueUrgentAsync(ITaskItem task, CancellationToken cancellationToken = default)
|
||||
{
|
||||
// 设置为最高优先级并立即处理
|
||||
if (task is BaseTask baseTask)
|
||||
{
|
||||
baseTask.Priority = TaskPriority.Urgent;
|
||||
}
|
||||
|
||||
return await EnqueueAsync(task, cancellationToken);
|
||||
}
|
||||
|
||||
public async Task<bool> EnqueueSequentialAsync(IEnumerable<ITaskItem> tasks, string? groupId = null)
|
||||
{
|
||||
if (_disposed) return false;
|
||||
|
||||
var taskList = tasks.ToList();
|
||||
if (!taskList.Any()) return true;
|
||||
|
||||
var actualGroupId = groupId ?? Guid.NewGuid().ToString();
|
||||
|
||||
try
|
||||
{
|
||||
_queueLock.EnterWriteLock();
|
||||
|
||||
// 检查队列容量
|
||||
if (_priorityQueue.Count + taskList.Count > _options.MaxQueueSize)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var sequenceStart = Interlocked.Add(ref _currentSequenceNumber, taskList.Count) - taskList.Count + 1;
|
||||
|
||||
for (int i = 0; i < taskList.Count; i++)
|
||||
{
|
||||
var task = taskList[i];
|
||||
task.SequenceNumber = sequenceStart + i;
|
||||
|
||||
// 设置分组ID
|
||||
if (task is BaseTask baseTask && string.IsNullOrEmpty(baseTask.GroupId))
|
||||
{
|
||||
baseTask.GroupId = actualGroupId;
|
||||
}
|
||||
|
||||
_priorityQueue.Enqueue(task, task);
|
||||
Interlocked.Increment(ref _totalEnqueued);
|
||||
}
|
||||
|
||||
_newTaskEvent.Set();
|
||||
return true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_queueLock.ExitWriteLock();
|
||||
}
|
||||
}
|
||||
|
||||
public TaskStatistics GetStatistics()
|
||||
{
|
||||
var stats = new TaskStatistics
|
||||
{
|
||||
RunningTasks = _runningTasks.Count,
|
||||
AvailableWorkers = _concurrencySemaphore.CurrentCount,
|
||||
TotalEnqueued = Interlocked.Read(ref _totalEnqueued),
|
||||
TotalCompleted = Interlocked.Read(ref _totalCompleted),
|
||||
TotalFailed = Interlocked.Read(ref _totalFailed),
|
||||
LastUpdated = DateTime.UtcNow
|
||||
};
|
||||
|
||||
// 计算平均执行时间
|
||||
if (_executionTimes.Count > 0)
|
||||
{
|
||||
stats.AverageExecutionTime = _executionTimes.Average();
|
||||
}
|
||||
|
||||
// 获取队列统计
|
||||
_queueLock.EnterReadLock();
|
||||
try
|
||||
{
|
||||
stats.QueuedTasks = _priorityQueue.Count;
|
||||
|
||||
// 按优先级和分组统计(这里简化处理,实际中可能需要遍历队列)
|
||||
var tempQueue = new List<ITaskItem>();
|
||||
while (_priorityQueue.Count > 0)
|
||||
{
|
||||
var task = _priorityQueue.Dequeue();
|
||||
tempQueue.Add(task);
|
||||
|
||||
// 统计优先级
|
||||
if (!stats.QueuedByPriority.ContainsKey(task.Priority))
|
||||
stats.QueuedByPriority[task.Priority] = 0;
|
||||
stats.QueuedByPriority[task.Priority]++;
|
||||
|
||||
// 统计分组
|
||||
if (!string.IsNullOrEmpty(task.GroupId))
|
||||
{
|
||||
stats.QueuedByGroup.TryAdd(task.GroupId, 0);
|
||||
stats.QueuedByGroup[task.GroupId]++;
|
||||
}
|
||||
}
|
||||
|
||||
// 重新入队
|
||||
foreach (var task in tempQueue)
|
||||
{
|
||||
_priorityQueue.Enqueue(task, task);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_queueLock.ExitReadLock();
|
||||
}
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
public List<ITaskItem> GetQueuedTasks()
|
||||
{
|
||||
_queueLock.EnterReadLock();
|
||||
try
|
||||
{
|
||||
var result = new List<ITaskItem>();
|
||||
var tempQueue = new List<ITaskItem>();
|
||||
|
||||
while (_priorityQueue.Count > 0)
|
||||
{
|
||||
var task = _priorityQueue.Dequeue();
|
||||
tempQueue.Add(task);
|
||||
result.Add(task);
|
||||
}
|
||||
|
||||
// 重新入队
|
||||
foreach (var task in tempQueue)
|
||||
{
|
||||
_priorityQueue.Enqueue(task, task);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_queueLock.ExitReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
public List<TaskInfo> GetRunningTasks()
|
||||
{
|
||||
return _runningTasks.Values.ToList();
|
||||
}
|
||||
|
||||
private async Task ProcessTasksAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
ITaskItem? task = null;
|
||||
|
||||
// 从优先级队列获取任务
|
||||
_queueLock.EnterWriteLock();
|
||||
try
|
||||
{
|
||||
if (_priorityQueue.Count > 0)
|
||||
{
|
||||
task = _priorityQueue.Dequeue();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_queueLock.ExitWriteLock();
|
||||
}
|
||||
|
||||
if (task == null)
|
||||
{
|
||||
// 没有任务,等待新任务信号
|
||||
try
|
||||
{
|
||||
_newTaskEvent.Wait(1000, cancellationToken); // 最多等待1秒
|
||||
_newTaskEvent.Reset();
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// 控制并发数
|
||||
await _concurrencySemaphore.WaitAsync(cancellationToken);
|
||||
|
||||
// 处理分组任务的串行执行
|
||||
SemaphoreSlim? groupSemaphore = null;
|
||||
if (_options.EnableGroupedExecution && !string.IsNullOrEmpty(task.GroupId))
|
||||
{
|
||||
groupSemaphore = _groupSemaphores.GetOrAdd(task.GroupId, _ => new SemaphoreSlim(1, 1));
|
||||
await groupSemaphore.WaitAsync(cancellationToken);
|
||||
}
|
||||
|
||||
var taskInfo = new TaskInfo
|
||||
{
|
||||
Task = task,
|
||||
StartTime = DateTime.UtcNow
|
||||
};
|
||||
|
||||
_runningTasks.TryAdd(task.TaskId, taskInfo);
|
||||
|
||||
// 异步执行任务
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
|
||||
cts.CancelAfter(_options.TaskTimeout);
|
||||
|
||||
await task.ExecuteAsync(cts.Token);
|
||||
|
||||
taskInfo.IsCompleted = true;
|
||||
taskInfo.EndTime = DateTime.UtcNow;
|
||||
Interlocked.Increment(ref _totalCompleted);
|
||||
|
||||
// 记录执行时间
|
||||
var executionTime = taskInfo.ExecutionTime.TotalMilliseconds;
|
||||
_executionTimes.Enqueue(executionTime);
|
||||
|
||||
// 限制执行时间队列大小
|
||||
while (_executionTimes.Count > 1000)
|
||||
{
|
||||
_executionTimes.TryDequeue(out _);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
taskInfo.IsFailed = true;
|
||||
taskInfo.Exception = ex;
|
||||
taskInfo.EndTime = DateTime.UtcNow;
|
||||
Interlocked.Increment(ref _totalFailed);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_runningTasks.TryRemove(task.TaskId, out _);
|
||||
_concurrencySemaphore.Release();
|
||||
groupSemaphore?.Release();
|
||||
}
|
||||
}, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateStatistics(object state)
|
||||
{
|
||||
// 定期更新统计信息的逻辑
|
||||
// 这里可以添加更复杂的统计计算
|
||||
}
|
||||
|
||||
public async Task StopAsync()
|
||||
{
|
||||
if (_disposed) return;
|
||||
|
||||
_cancellationTokenSource.Cancel();
|
||||
_newTaskEvent.Set(); // 唤醒等待的工作线程
|
||||
|
||||
try
|
||||
{
|
||||
await Task.WhenAll(_workerTasks);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
// 预期的取消异常
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_disposed) return;
|
||||
|
||||
_disposed = true;
|
||||
_cancellationTokenSource.Cancel();
|
||||
_newTaskEvent.Set();
|
||||
|
||||
try
|
||||
{
|
||||
Task.WaitAll(_workerTasks, TimeSpan.FromSeconds(5));
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
// 忽略关闭时的异常
|
||||
}
|
||||
|
||||
_concurrencySemaphore?.Dispose();
|
||||
_cancellationTokenSource?.Dispose();
|
||||
_queueLock?.Dispose();
|
||||
_newTaskEvent?.Dispose();
|
||||
_statisticsTimer?.Dispose();
|
||||
|
||||
// 清理分组信号量
|
||||
foreach (var semaphore in _groupSemaphores.Values)
|
||||
{
|
||||
semaphore.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 11. 增强的接口定义
|
||||
public interface ITaskQueue
|
||||
{
|
||||
Task<bool> EnqueueAsync(ITaskItem task, CancellationToken cancellationToken = default);
|
||||
Task<bool> EnqueueUrgentAsync(ITaskItem task, CancellationToken cancellationToken = default);
|
||||
Task<bool> EnqueueSequentialAsync(IEnumerable<ITaskItem> tasks, string groupId = null);
|
||||
TaskStatistics GetStatistics();
|
||||
List<ITaskItem> GetQueuedTasks();
|
||||
List<TaskInfo> GetRunningTasks();
|
||||
Task StopAsync();
|
||||
}
|
||||
|
||||
// 12. 网络数据存储接口和实现(保持原有实现)
|
||||
public interface INetworkDataStore
|
||||
{
|
||||
Task UpdateNetworkConnectionAsync(NetworkConnectionData data);
|
||||
Task<IEnumerable<NetworkConnectionData>> GetRecentConnectionsAsync(TimeSpan timeSpan);
|
||||
Task<NetworkStatistics> GetStatisticsAsync();
|
||||
}
|
||||
|
||||
public class NetworkDataStore : INetworkDataStore
|
||||
{
|
||||
private readonly ConcurrentQueue<NetworkConnectionData> _recentConnections;
|
||||
private readonly ConcurrentDictionary<string, ConnectionStatistics> _connectionStats;
|
||||
private readonly Timer _cleanupTimer;
|
||||
|
||||
public NetworkDataStore()
|
||||
{
|
||||
_recentConnections = new ConcurrentQueue<NetworkConnectionData>();
|
||||
_connectionStats = new ConcurrentDictionary<string, ConnectionStatistics>();
|
||||
|
||||
_cleanupTimer = new Timer(CleanupExpiredData, null,
|
||||
TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1));
|
||||
}
|
||||
|
||||
public async Task UpdateNetworkConnectionAsync(NetworkConnectionData data)
|
||||
{
|
||||
await Task.Run(() =>
|
||||
{
|
||||
_recentConnections.Enqueue(data);
|
||||
|
||||
var key = $"{data.ProcessName}:{data.LocalPort}";
|
||||
_connectionStats.AddOrUpdate(key,
|
||||
new ConnectionStatistics { Count = 1, LastSeen = DateTime.UtcNow },
|
||||
(k, v) => new ConnectionStatistics { Count = v.Count + 1, LastSeen = DateTime.UtcNow });
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<NetworkConnectionData>> GetRecentConnectionsAsync(TimeSpan timeSpan)
|
||||
{
|
||||
return await Task.FromResult(
|
||||
_recentConnections
|
||||
.Where(c => DateTime.UtcNow - c.Timestamp <= timeSpan)
|
||||
.ToList());
|
||||
}
|
||||
|
||||
public async Task<NetworkStatistics> GetStatisticsAsync()
|
||||
{
|
||||
return await Task.FromResult(new NetworkStatistics
|
||||
{
|
||||
TotalConnections = _recentConnections.Count,
|
||||
ActiveProcesses = _connectionStats.Count,
|
||||
LastUpdate = DateTime.UtcNow
|
||||
});
|
||||
}
|
||||
|
||||
private void CleanupExpiredData(object state)
|
||||
{
|
||||
var cutoff = DateTime.UtcNow.AddHours(-1);
|
||||
|
||||
while (_recentConnections.TryPeek(out var connection) &&
|
||||
connection.Timestamp < cutoff)
|
||||
{
|
||||
_recentConnections.TryDequeue(out _);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 13. 数据模型
|
||||
public class NetworkConnectionData
|
||||
{
|
||||
public string ProcessName { get; set; }
|
||||
public int ProcessId { get; set; }
|
||||
public string LocalAddress { get; set; }
|
||||
public int LocalPort { get; set; }
|
||||
public string RemoteAddress { get; set; }
|
||||
public int RemotePort { get; set; }
|
||||
public DateTime Timestamp { get; set; } = DateTime.UtcNow;
|
||||
public string Protocol { get; set; }
|
||||
}
|
||||
|
||||
public class ConnectionStatistics
|
||||
{
|
||||
public int Count { get; set; }
|
||||
public DateTime LastSeen { get; set; }
|
||||
}
|
||||
|
||||
public class NetworkStatistics
|
||||
{
|
||||
public int TotalConnections { get; set; }
|
||||
public int ActiveProcesses { get; set; }
|
||||
public DateTime LastUpdate { get; set; }
|
||||
}
|
||||
|
||||
// 14. 增强的任务队列管理器
|
||||
public class EnhancedTaskQueueManager
|
||||
{
|
||||
private readonly EnhancedTaskQueue _taskQueue;
|
||||
private readonly NetworkDataStore _dataStore;
|
||||
|
||||
public EnhancedTaskQueueManager(TaskQueueOptions options = null)
|
||||
{
|
||||
var defaultOptions = new TaskQueueOptions
|
||||
{
|
||||
MaxConcurrency = Environment.ProcessorCount,
|
||||
MaxQueueSize = 5000,
|
||||
EnableGroupedExecution = true,
|
||||
EnablePriorityProcessing = true
|
||||
};
|
||||
|
||||
_taskQueue = new EnhancedTaskQueue(options ?? defaultOptions);
|
||||
_dataStore = new NetworkDataStore();
|
||||
}
|
||||
|
||||
// 普通任务入队
|
||||
public async Task<bool> EnqueueNetworkTask(NetworkConnectionData data)
|
||||
{
|
||||
var task = new NetworkConnectionTask(data, _dataStore);
|
||||
return await _taskQueue.EnqueueAsync(task);
|
||||
}
|
||||
|
||||
// 紧急任务插队
|
||||
public async Task<bool> EnqueueUrgentNetworkTask(NetworkConnectionData data)
|
||||
{
|
||||
var task = new NetworkConnectionTask(data, _dataStore);
|
||||
return await _taskQueue.EnqueueUrgentAsync(task);
|
||||
}
|
||||
|
||||
// 批量有序任务
|
||||
public async Task<bool> EnqueueSequentialDatabaseWrites(IEnumerable<object> dataList)
|
||||
{
|
||||
var tasks = dataList.Select(data => new DatabaseWriteTask(data,
|
||||
async (d, ct) => await WriteToDatabase(d),
|
||||
TaskPriority.Normal)).Cast<ITaskItem>();
|
||||
|
||||
return await _taskQueue.EnqueueSequentialAsync(tasks, "database_batch");
|
||||
}
|
||||
|
||||
// 获取详细统计信息
|
||||
public TaskStatistics GetDetailedStatistics()
|
||||
{
|
||||
return _taskQueue.GetStatistics();
|
||||
}
|
||||
|
||||
// 获取队列中的任务列表
|
||||
public List<ITaskItem> GetPendingTasks()
|
||||
{
|
||||
return _taskQueue.GetQueuedTasks();
|
||||
}
|
||||
|
||||
// 获取正在执行的任务
|
||||
public List<TaskInfo> GetActiveTasks()
|
||||
{
|
||||
return _taskQueue.GetRunningTasks();
|
||||
}
|
||||
|
||||
// 获取网络数据
|
||||
public async Task<IEnumerable<NetworkConnectionData>> GetRecentConnections(TimeSpan timeSpan)
|
||||
{
|
||||
return await _dataStore.GetRecentConnectionsAsync(timeSpan);
|
||||
}
|
||||
|
||||
private async Task WriteToDatabase(object data)
|
||||
{
|
||||
// 模拟数据库写入
|
||||
await Task.Delay(100);
|
||||
}
|
||||
|
||||
public async Task StopAsync()
|
||||
{
|
||||
await _taskQueue.StopAsync();
|
||||
_taskQueue.Dispose();
|
||||
}
|
||||
}
|
||||
// 保留一个任务队列工具函数;
|
||||
// 提供公共阿德网络处理模板;
|
||||
// 尝试获取所有的网络连接信息;
|
||||
// 如何模拟低性能设设备的软件运行情况?
|
||||
// 提供公共的网络处理任务类,并将其投入使用;
|
||||
// 提供全局的公共的,唯一的信息类(提供软件开启状态;网络连接;等其他信息) 将网络信息及其他信息进程整合
|
||||
// 页面展示提供多种状态:(仅联网软件;已打开软件)
|
||||
@@ -0,0 +1,23 @@
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
|
||||
namespace Application.Utils;
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public static class SysHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取可用端口
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static int GetAvailablePort()
|
||||
{
|
||||
TcpListener listener = new TcpListener(IPAddress.Loopback, 0); // 端口号为0表示由系统自动分配
|
||||
listener.Start();
|
||||
int port = ((IPEndPoint)listener.LocalEndpoint).Port;
|
||||
listener.Stop();
|
||||
return port;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user