支持自定义连接池大小 日志分等级存储

This commit is contained in:
root 2024-12-01 23:29:05 +08:00
parent 5bf3549522
commit d34b491c11
8 changed files with 120 additions and 48 deletions

View file

@ -2,7 +2,6 @@
using Serilog; using Serilog;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using MySql.Data.MySqlClient;
namespace iFileProxy.Config namespace iFileProxy.Config
{ {
@ -93,6 +92,9 @@ namespace iFileProxy.Config
public partial class Database public partial class Database
{ {
[JsonPropertyName("MaxConnectionPoolSize")]
public uint MaxConnectionPoolSize { get; set; } = 50;
[JsonPropertyName("Common")] [JsonPropertyName("Common")]
public Common Common { get; set; } public Common Common { get; set; }

View file

@ -15,7 +15,7 @@ namespace iFileProxy.Controllers
{ {
public TaskManager _taskManager = taskManager; public TaskManager _taskManager = taskManager;
public DatabaseGateService _dbGateService = dbGateService; public DatabaseGateService _dbGateService = dbGateService;
private readonly Serilog.ILogger _logger = Log.Logger.ForContext<DatabaseGateService>(); private readonly Serilog.ILogger _logger = Log.Logger.ForContext<ManagementController>();
// 查看任务详情 // 查看任务详情
@ -362,6 +362,7 @@ namespace iFileProxy.Controllers
/// <summary> /// <summary>
/// 获取数据库连接池状态 /// 获取数据库连接池状态
/// </summary> /// </summary>
[Authorize(UserMask.SuperAdmin)]
[HttpGet("GetConnectionPoolInfo")] [HttpGet("GetConnectionPoolInfo")]
public async Task<ActionResult<CommonRsp>> GetConnectionPoolInfo() public async Task<ActionResult<CommonRsp>> GetConnectionPoolInfo()
{ {

View file

@ -8,16 +8,10 @@ namespace iFileProxy.Controllers
{ {
[Route("[controller]")] [Route("[controller]")]
[ApiController] [ApiController]
public class UserController : ControllerBase public class UserController(AuthService authService, ILogger<UserController> logger) : ControllerBase
{ {
private readonly AuthService _authService; private readonly AuthService _authService = authService;
private readonly ILogger<UserController> _logger; private readonly ILogger<UserController> _logger = logger;
public UserController(AuthService authService, ILogger<UserController> logger)
{
_authService = authService;
_logger = logger;
}
/// <summary> /// <summary>
/// 用户注册 /// 用户注册

View file

@ -130,7 +130,7 @@ namespace iFileProxy.Models
/// <summary> /// <summary>
/// 连接池最大连接数 /// 连接池最大连接数
/// </summary> /// </summary>
public int MaxPoolSize { get; set; } public uint MaxPoolSize { get; set; }
/// <summary> /// <summary>
/// 连接详情列表 /// 连接详情列表

View file

@ -8,7 +8,14 @@
{ {
public static void CreateLogger() 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() Log.Logger = new LoggerConfiguration()
#if RELEASE #if RELEASE
@ -19,12 +26,41 @@
.MinimumLevel.Override("Microsoft", LogEventLevel.Information) .MinimumLevel.Override("Microsoft", LogEventLevel.Information)
.MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning) .MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning)
.Enrich.FromLogContext() .Enrich.FromLogContext()
// 控制台输出所有日志
.WriteTo.Console( .WriteTo.Console(
outputTemplate: "{Timestamp:HH:mm:ss.fff} [{Level:u3}] [{SourceContext}] {ClientIp} {Message:lj} {contentType}{NewLine} {Exception}") outputTemplate: "{Timestamp:HH:mm:ss.fff} [{Level:u3}] [{SourceContext}] {ClientIp} {Message:lj} {contentType}{NewLine} {Exception}")
.WriteTo.File(filePath, // 错误和致命错误写入单独的文件
.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}", outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] [{SourceContext}] {ClientIp} {Message:lj} {contentType} {queryString}{NewLine}{Exception}",
rollingInterval: RollingInterval.Day, rollingInterval: RollingInterval.Day,
fileSizeLimitBytes: 1073741824) //1GB 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()) .Enrich.WithProperty("node_ip", GetIpAddress())
.CreateLogger(); .CreateLogger();
} }

View file

@ -6,7 +6,6 @@ using MySql.Data.MySqlClient;
using iFileProxy.Models; using iFileProxy.Models;
using Newtonsoft.Json; using Newtonsoft.Json;
using System.Text; using System.Text;
using System.Threading.Tasks;
namespace iFileProxy.Services namespace iFileProxy.Services
{ {
@ -124,7 +123,7 @@ namespace iFileProxy.Services
Password = db_password, Password = db_password,
Pooling = true, Pooling = true,
MinimumPoolSize = 1, MinimumPoolSize = 1,
MaximumPoolSize = 50, MaximumPoolSize = AppConfig.GetCurrConfig().Database.MaxConnectionPoolSize,
ConnectionLifeTime = 300 // 连接最大生命周期(秒) ConnectionLifeTime = 300 // 连接最大生命周期(秒)
}; };
@ -929,27 +928,27 @@ namespace iFileProxy.Services
// 获取总连接数 // 获取总连接数
var totalSql = @"SELECT COUNT(*) var totalSql = @"SELECT COUNT(*)
FROM information_schema.processlist FROM information_schema.processlist
WHERE info LIKE @appName OR host LIKE @appName"; WHERE `USER` LIKE @appName OR `DB` LIKE @appName";
var totalConnections = await ExecuteScalarAsync<long>(totalSql, parameters); var totalConnections = await ExecuteScalarAsync<long>(totalSql, parameters);
// 获取活跃连接数 // 获取活跃连接数
var activeSql = @"SELECT COUNT(*) var activeSql = @"SELECT COUNT(*)
FROM information_schema.processlist FROM information_schema.processlist
WHERE Command != 'Sleep' WHERE Command != 'Sleep'
AND (info LIKE @appName OR host LIKE @appName)"; AND ( `USER` LIKE @appName OR `DB` LIKE @appName)";
var activeConnections = await ExecuteScalarAsync<long>(activeSql, parameters); var activeConnections = await ExecuteScalarAsync<long>(activeSql, parameters);
// 获取睡眠连接数 // 获取睡眠连接数
var sleepingSql = @"SELECT COUNT(*) var sleepingSql = @"SELECT COUNT(*)
FROM information_schema.processlist FROM information_schema.processlist
WHERE Command = 'Sleep' WHERE Command = 'Sleep'
AND (info LIKE @appName OR host LIKE @appName)"; AND ( `USER` LIKE @appName OR `DB` LIKE @appName)";
var sleepingConnections = await ExecuteScalarAsync<long>(sleepingSql, parameters); var sleepingConnections = await ExecuteScalarAsync<long>(sleepingSql, parameters);
// 获取连接详情 // 获取连接详情
var detailSql = @"SELECT id, user, host, db, command, time, state, info var detailSql = @"SELECT id, user, host, db, command, time, state, info
FROM information_schema.processlist FROM information_schema.processlist
WHERE info LIKE @appName OR host LIKE @appName"; WHERE `USER` LIKE @appName OR `DB` LIKE @appName";
var connections = await ExecuteQueryAsync<ProcessListInfo>(detailSql, parameters); var connections = await ExecuteQueryAsync<ProcessListInfo>(detailSql, parameters);
return new ConnectionPoolInfo return new ConnectionPoolInfo
@ -957,7 +956,7 @@ namespace iFileProxy.Services
TotalConnections = totalConnections, TotalConnections = totalConnections,
ActiveConnections = activeConnections, ActiveConnections = activeConnections,
SleepingConnections = sleepingConnections, SleepingConnections = sleepingConnections,
MaxPoolSize = 50, // 从配置中获取的最大连接数 MaxPoolSize = AppConfig.GetCurrConfig().Database.MaxConnectionPoolSize, // 从配置中获取的最大连接数
Connections = connections Connections = connections
}; };
} }

View file

@ -3,6 +3,8 @@ using iFileProxy.Helpers;
using iFileProxy.Models; using iFileProxy.Models;
using Newtonsoft.Json; using Newtonsoft.Json;
using Serilog; using Serilog;
using MySql.Data.MySqlClient;
using System.Data;
namespace iFileProxy.Services namespace iFileProxy.Services
{ {
@ -16,6 +18,7 @@ namespace iFileProxy.Services
private readonly AppConfig _appConfig = AppConfig.GetCurrConfig(); private readonly AppConfig _appConfig = AppConfig.GetCurrConfig();
private readonly static Serilog.ILogger _logger = Log.Logger.ForContext<LocalCacheManager>(); private readonly static Serilog.ILogger _logger = Log.Logger.ForContext<LocalCacheManager>();
private readonly object _lock = new object();
private readonly Timer _timer; private readonly Timer _timer;
private readonly DatabaseGateService _dbGateService; private readonly DatabaseGateService _dbGateService;
private readonly int CACHE_LIFETIME; private readonly int CACHE_LIFETIME;
@ -29,21 +32,39 @@ namespace iFileProxy.Services
CACHE_LIFETIME = _appConfig.DownloadOptions.CacheLifetime; CACHE_LIFETIME = _appConfig.DownloadOptions.CacheLifetime;
_dbGateService = serviceProvider.GetRequiredService<DatabaseGateService>(); _dbGateService = serviceProvider.GetRequiredService<DatabaseGateService>();
// 开始定时清理任务 // 开始定时清理任务
_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."); _logger.Information("succ.");
} }
/// <summary> /// <summary>
/// 检查并且清理缓存数据 /// 检查并且清理缓存数据
/// </summary> /// </summary>
public void CheckAndCleanCache(object state) public void CheckAndCleanCache()
{
try
{ {
// 初始化并打开一个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); string sql = $@"SELECT * FROM t_tasks_info
List<TaskInfo>? taskInfos = JsonConvert.DeserializeObject<List<TaskInfo>>(result); WHERE UNIX_TIMESTAMP(NOW()) - UNIX_TIMESTAMP(update_time) > {CACHE_LIFETIME}
AND (tag <> 'CLEANED' OR tag IS NULL)";
List<TaskInfo>? taskInfos;
using (var conn = _dbGateService.GetAndOpenDBConn(DbConfigName.iFileProxy))
{
using var cmd = new MySqlCommand(sql, conn);
using var adapter = new MySqlDataAdapter(cmd);
var dataTable = new DataTable();
adapter.Fill(dataTable);
taskInfos = JsonConvert.DeserializeObject<List<TaskInfo>>(
JsonConvert.SerializeObject(dataTable)
);
}
if (taskInfos != null) if (taskInfos != null)
{ {
foreach (TaskInfo taskInfo in taskInfos) foreach (TaskInfo taskInfo in taskInfos)
@ -58,17 +79,35 @@ namespace iFileProxy.Services
} }
catch (Exception e) catch (Exception e)
{ {
_logger.Error("缓存文件删除失败: {e}", e); _logger.Error($"缓存文件删除失败: {e.Message}");
throw; continue;
} }
} }
_dbGateService.Query($"UPDATE t_tasks_info SET `tag` = \"CLEANED\" WHERE `tid` = '{taskInfo.TaskId}'", dbConn);
// 更新数据库状态
using (var conn = _dbGateService.GetAndOpenDBConn(DbConfigName.iFileProxy))
{
// 更新标签
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; taskInfo.Status = TaskState.Cleaned;
_dbGateService.UpdateTaskStatus(taskInfo,dbConn); _dbGateService.UpdateTaskStatus(taskInfo);
} }
} }
}
}
catch (Exception ex)
{
_logger.Error($"清理缓存时发生错误: {ex.Message}");
}
} }
/// <summary> /// <summary>

View file

@ -1,5 +1,6 @@
{ {
"Database": { "Database": {
"MaxConnectionPoolSize": 100,
"Common": { "Common": {
"Host": "47.243.56.137", "Host": "47.243.56.137",
"Port": 3306, "Port": 3306,