diff --git a/src/Config/AppConfig.cs b/src/Config/AppConfig.cs index 94c6fc9..14f6d92 100644 --- a/src/Config/AppConfig.cs +++ b/src/Config/AppConfig.cs @@ -2,7 +2,6 @@ using Serilog; using System.Text.Json; using System.Text.Json.Serialization; -using MySql.Data.MySqlClient; namespace iFileProxy.Config { @@ -93,6 +92,9 @@ namespace iFileProxy.Config public partial class Database { + [JsonPropertyName("MaxConnectionPoolSize")] + public uint MaxConnectionPoolSize { get; set; } = 50; + [JsonPropertyName("Common")] public Common Common { get; set; } diff --git a/src/Controllers/ManagementController.cs b/src/Controllers/ManagementController.cs index 47e3268..81cd778 100644 --- a/src/Controllers/ManagementController.cs +++ b/src/Controllers/ManagementController.cs @@ -15,7 +15,7 @@ namespace iFileProxy.Controllers { public TaskManager _taskManager = taskManager; public DatabaseGateService _dbGateService = dbGateService; - private readonly Serilog.ILogger _logger = Log.Logger.ForContext(); + private readonly Serilog.ILogger _logger = Log.Logger.ForContext(); // 查看任务详情 @@ -362,6 +362,7 @@ namespace iFileProxy.Controllers /// /// 获取数据库连接池状态 /// + [Authorize(UserMask.SuperAdmin)] [HttpGet("GetConnectionPoolInfo")] public async Task> GetConnectionPoolInfo() { diff --git a/src/Controllers/UserController.cs b/src/Controllers/UserController.cs index 75a1ea9..b8c4839 100644 --- a/src/Controllers/UserController.cs +++ b/src/Controllers/UserController.cs @@ -8,16 +8,10 @@ namespace iFileProxy.Controllers { [Route("[controller]")] [ApiController] - public class UserController : ControllerBase + public class UserController(AuthService authService, ILogger logger) : ControllerBase { - private readonly AuthService _authService; - private readonly ILogger _logger; - - public UserController(AuthService authService, ILogger logger) - { - _authService = authService; - _logger = logger; - } + private readonly AuthService _authService = authService; + private readonly ILogger _logger = logger; /// /// 用户注册 diff --git a/src/Models/Db.cs b/src/Models/Db.cs index 3b8a46e..575ba04 100644 --- a/src/Models/Db.cs +++ b/src/Models/Db.cs @@ -130,7 +130,7 @@ namespace iFileProxy.Models /// /// 连接池最大连接数 /// - public int MaxPoolSize { get; set; } + public uint MaxPoolSize { get; set; } /// /// 连接详情列表 diff --git a/src/SerilogConfig.cs b/src/SerilogConfig.cs index f1a2f85..3d0b0af 100644 --- a/src/SerilogConfig.cs +++ b/src/SerilogConfig.cs @@ -8,7 +8,14 @@ { public static void CreateLogger() { - var filePath = Path.Combine(AppContext.BaseDirectory, $"logs/dispatch.api.log"); + var baseLogPath = Path.Combine(AppContext.BaseDirectory, "logs"); + var appName = AppDomain.CurrentDomain.FriendlyName; + + // 确保日志目录存在 + if (!Directory.Exists(baseLogPath)) + { + Directory.CreateDirectory(baseLogPath); + } Log.Logger = new LoggerConfiguration() #if RELEASE @@ -19,12 +26,41 @@ .MinimumLevel.Override("Microsoft", LogEventLevel.Information) .MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning) .Enrich.FromLogContext() + // 控制台输出所有日志 .WriteTo.Console( - outputTemplate: "{Timestamp:HH:mm:ss.fff} [{Level:u3}] [{SourceContext}] {ClientIp} {Message:lj} {contentType}{NewLine} {Exception}") - .WriteTo.File(filePath, - outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] [{SourceContext}] {ClientIp} {Message:lj} {contentType} {queryString}{NewLine}{Exception}", - rollingInterval: RollingInterval.Day, - fileSizeLimitBytes: 1073741824) //1GB + outputTemplate: "{Timestamp:HH:mm:ss.fff} [{Level:u3}] [{SourceContext}] {ClientIp} {Message:lj} {contentType}{NewLine} {Exception}") + // 错误和致命错误写入单独的文件 + .WriteTo.Logger(lc => lc + .Filter.ByIncludingOnly(evt => evt.Level >= LogEventLevel.Error) + .WriteTo.File( + Path.Combine(baseLogPath, $"{appName}.error.log"), + outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] [{SourceContext}] {ClientIp} {Message:lj} {contentType} {queryString}{NewLine}{Exception}", + rollingInterval: RollingInterval.Day, + fileSizeLimitBytes: 1073741824)) // 1GB + // 警告日志写入单独的文件 + .WriteTo.Logger(lc => lc + .Filter.ByIncludingOnly(evt => evt.Level == LogEventLevel.Warning) + .WriteTo.File( + Path.Combine(baseLogPath, $"{appName}.warning.log"), + outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] [{SourceContext}] {ClientIp} {Message:lj} {contentType} {queryString}{NewLine}{Exception}", + rollingInterval: RollingInterval.Day, + fileSizeLimitBytes: 1073741824)) + // 信息日志写入单独的文件 + .WriteTo.Logger(lc => lc + .Filter.ByIncludingOnly(evt => evt.Level == LogEventLevel.Information) + .WriteTo.File( + Path.Combine(baseLogPath, $"{appName}.info.log"), + outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] [{SourceContext}] {ClientIp} {Message:lj} {contentType} {queryString}{NewLine}{Exception}", + rollingInterval: RollingInterval.Day, + fileSizeLimitBytes: 1073741824)) + // 调试日志写入单独的文件 + .WriteTo.Logger(lc => lc + .Filter.ByIncludingOnly(evt => evt.Level == LogEventLevel.Debug) + .WriteTo.File( + Path.Combine(baseLogPath, $"{appName}.debug.log"), + outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] [{SourceContext}] {ClientIp} {Message:lj} {contentType} {queryString}{NewLine}{Exception}", + rollingInterval: RollingInterval.Day, + fileSizeLimitBytes: 1073741824)) .Enrich.WithProperty("node_ip", GetIpAddress()) .CreateLogger(); } diff --git a/src/Services/DatabaseGateService.cs b/src/Services/DatabaseGateService.cs index 793a94c..c803adc 100644 --- a/src/Services/DatabaseGateService.cs +++ b/src/Services/DatabaseGateService.cs @@ -6,7 +6,6 @@ using MySql.Data.MySqlClient; using iFileProxy.Models; using Newtonsoft.Json; using System.Text; -using System.Threading.Tasks; namespace iFileProxy.Services { @@ -124,7 +123,7 @@ namespace iFileProxy.Services Password = db_password, Pooling = true, MinimumPoolSize = 1, - MaximumPoolSize = 50, + MaximumPoolSize = AppConfig.GetCurrConfig().Database.MaxConnectionPoolSize, ConnectionLifeTime = 300 // 连接最大生命周期(秒) }; @@ -929,27 +928,27 @@ namespace iFileProxy.Services // 获取总连接数 var totalSql = @"SELECT COUNT(*) FROM information_schema.processlist - WHERE info LIKE @appName OR host LIKE @appName"; + WHERE `USER` LIKE @appName OR `DB` LIKE @appName"; var totalConnections = await ExecuteScalarAsync(totalSql, parameters); // 获取活跃连接数 var activeSql = @"SELECT COUNT(*) FROM information_schema.processlist WHERE Command != 'Sleep' - AND (info LIKE @appName OR host LIKE @appName)"; + AND ( `USER` LIKE @appName OR `DB` LIKE @appName)"; var activeConnections = await ExecuteScalarAsync(activeSql, parameters); // 获取睡眠连接数 var sleepingSql = @"SELECT COUNT(*) FROM information_schema.processlist WHERE Command = 'Sleep' - AND (info LIKE @appName OR host LIKE @appName)"; + AND ( `USER` LIKE @appName OR `DB` LIKE @appName)"; var sleepingConnections = await ExecuteScalarAsync(sleepingSql, parameters); // 获取连接详情 var detailSql = @"SELECT id, user, host, db, command, time, state, info FROM information_schema.processlist - WHERE info LIKE @appName OR host LIKE @appName"; + WHERE `USER` LIKE @appName OR `DB` LIKE @appName"; var connections = await ExecuteQueryAsync(detailSql, parameters); return new ConnectionPoolInfo @@ -957,7 +956,7 @@ namespace iFileProxy.Services TotalConnections = totalConnections, ActiveConnections = activeConnections, SleepingConnections = sleepingConnections, - MaxPoolSize = 50, // 从配置中获取的最大连接数 + MaxPoolSize = AppConfig.GetCurrConfig().Database.MaxConnectionPoolSize, // 从配置中获取的最大连接数 Connections = connections }; } diff --git a/src/Services/LocalCacheManager.cs b/src/Services/LocalCacheManager.cs index 8cb46af..4df1130 100644 --- a/src/Services/LocalCacheManager.cs +++ b/src/Services/LocalCacheManager.cs @@ -3,6 +3,8 @@ using iFileProxy.Helpers; using iFileProxy.Models; using Newtonsoft.Json; using Serilog; +using MySql.Data.MySqlClient; +using System.Data; namespace iFileProxy.Services { @@ -16,6 +18,7 @@ namespace iFileProxy.Services private readonly AppConfig _appConfig = AppConfig.GetCurrConfig(); private readonly static Serilog.ILogger _logger = Log.Logger.ForContext(); + private readonly object _lock = new object(); private readonly Timer _timer; private readonly DatabaseGateService _dbGateService; private readonly int CACHE_LIFETIME; @@ -29,46 +32,82 @@ namespace iFileProxy.Services CACHE_LIFETIME = _appConfig.DownloadOptions.CacheLifetime; _dbGateService = serviceProvider.GetRequiredService(); // 开始定时清理任务 - _timer = new Timer(CheckAndCleanCache, null, TimeSpan.FromSeconds(6), TimeSpan.FromSeconds(60)); + _timer = new Timer((obj) => { + lock (_lock) + { + CheckAndCleanCache(); + } + }, null, TimeSpan.FromSeconds(6), TimeSpan.FromSeconds(60)); _logger.Information("succ."); } /// /// 检查并且清理缓存数据 /// - public void CheckAndCleanCache(object state) + public void CheckAndCleanCache() { - // 初始化并打开一个MySQL连接 防止后续数据过多导致程序crush - using var dbConn = _dbGateService.GetAndOpenDBConn(DbConfigName.iFileProxy); - - // 获取数据库中超出生命周期的缓存数据 - string result = _dbGateService.QueryTableData($"SELECT * FROM t_tasks_info WHERE UNIX_TIMESTAMP(NOW()) - UNIX_TIMESTAMP(update_time) > {CACHE_LIFETIME} AND (tag <> 'CLEANED' OR tag IS NULL)", DbConfigName.iFileProxy); - List? taskInfos = JsonConvert.DeserializeObject>(result); - if (taskInfos != null) + try { - foreach (TaskInfo taskInfo in taskInfos) + // 获取数据库中超出生命周期的缓存数据 + string sql = $@"SELECT * FROM t_tasks_info + WHERE UNIX_TIMESTAMP(NOW()) - UNIX_TIMESTAMP(update_time) > {CACHE_LIFETIME} + AND (tag <> 'CLEANED' OR tag IS NULL)"; + + List? taskInfos; + using (var conn = _dbGateService.GetAndOpenDBConn(DbConfigName.iFileProxy)) { - string cacheFileName = Path.Combine(_appConfig.DownloadOptions.SavePath, taskInfo.FileName); - if (File.Exists(cacheFileName)) + using var cmd = new MySqlCommand(sql, conn); + using var adapter = new MySqlDataAdapter(cmd); + var dataTable = new DataTable(); + adapter.Fill(dataTable); + taskInfos = JsonConvert.DeserializeObject>( + JsonConvert.SerializeObject(dataTable) + ); + } + + if (taskInfos != null) + { + foreach (TaskInfo taskInfo in taskInfos) { - _logger.Information($"正在清理缓存文件: {cacheFileName}"); - try + string cacheFileName = Path.Combine(_appConfig.DownloadOptions.SavePath, taskInfo.FileName); + if (File.Exists(cacheFileName)) { - File.Delete(cacheFileName); + _logger.Information($"正在清理缓存文件: {cacheFileName}"); + try + { + File.Delete(cacheFileName); + } + catch (Exception e) + { + _logger.Error($"缓存文件删除失败: {e.Message}"); + continue; + } } - catch (Exception e) + + // 更新数据库状态 + using (var conn = _dbGateService.GetAndOpenDBConn(DbConfigName.iFileProxy)) { - _logger.Error("缓存文件删除失败: {e}", e); - throw; + // 更新标签 + using (var cmd = new MySqlCommand( + "UPDATE t_tasks_info SET tag = @tag WHERE tid = @tid", + conn)) + { + cmd.Parameters.AddWithValue("@tag", "CLEANED"); + cmd.Parameters.AddWithValue("@tid", taskInfo.TaskId); + cmd.ExecuteNonQuery(); + } + + // 更新状态 + taskInfo.Status = TaskState.Cleaned; + _dbGateService.UpdateTaskStatus(taskInfo); } } - _dbGateService.Query($"UPDATE t_tasks_info SET `tag` = \"CLEANED\" WHERE `tid` = '{taskInfo.TaskId}'", dbConn); - taskInfo.Status = TaskState.Cleaned; - _dbGateService.UpdateTaskStatus(taskInfo,dbConn); } } - - + catch (Exception ex) + { + _logger.Error($"清理缓存时发生错误: {ex.Message}"); + } } /// diff --git a/src/iFileProxy.json b/src/iFileProxy.json index bf3f1b0..36ee834 100644 --- a/src/iFileProxy.json +++ b/src/iFileProxy.json @@ -1,5 +1,6 @@ { "Database": { + "MaxConnectionPoolSize": 100, "Common": { "Host": "47.243.56.137", "Port": 3306,