支持自定义连接池大小 日志分等级存储
This commit is contained in:
parent
5bf3549522
commit
d34b491c11
8 changed files with 120 additions and 48 deletions
|
@ -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; }
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
{
|
{
|
||||||
|
|
|
@ -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>
|
||||||
/// 用户注册
|
/// 用户注册
|
||||||
|
|
|
@ -130,7 +130,7 @@ namespace iFileProxy.Models
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 连接池最大连接数
|
/// 连接池最大连接数
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int MaxPoolSize { get; set; }
|
public uint MaxPoolSize { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 连接详情列表
|
/// 连接详情列表
|
||||||
|
|
|
@ -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,
|
// 错误和致命错误写入单独的文件
|
||||||
outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] [{SourceContext}] {ClientIp} {Message:lj} {contentType} {queryString}{NewLine}{Exception}",
|
.WriteTo.Logger(lc => lc
|
||||||
rollingInterval: RollingInterval.Day,
|
.Filter.ByIncludingOnly(evt => evt.Level >= LogEventLevel.Error)
|
||||||
fileSizeLimitBytes: 1073741824) //1GB
|
.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())
|
.Enrich.WithProperty("node_ip", GetIpAddress())
|
||||||
.CreateLogger();
|
.CreateLogger();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,46 +32,82 @@ 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()
|
||||||
{
|
{
|
||||||
// 初始化并打开一个MySQL连接 防止后续数据过多导致程序crush
|
try
|
||||||
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<TaskInfo>? taskInfos = JsonConvert.DeserializeObject<List<TaskInfo>>(result);
|
|
||||||
if (taskInfos != null)
|
|
||||||
{
|
{
|
||||||
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<TaskInfo>? taskInfos;
|
||||||
|
using (var conn = _dbGateService.GetAndOpenDBConn(DbConfigName.iFileProxy))
|
||||||
{
|
{
|
||||||
string cacheFileName = Path.Combine(_appConfig.DownloadOptions.SavePath, taskInfo.FileName);
|
using var cmd = new MySqlCommand(sql, conn);
|
||||||
if (File.Exists(cacheFileName))
|
using var adapter = new MySqlDataAdapter(cmd);
|
||||||
|
var dataTable = new DataTable();
|
||||||
|
adapter.Fill(dataTable);
|
||||||
|
taskInfos = JsonConvert.DeserializeObject<List<TaskInfo>>(
|
||||||
|
JsonConvert.SerializeObject(dataTable)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (taskInfos != null)
|
||||||
|
{
|
||||||
|
foreach (TaskInfo taskInfo in taskInfos)
|
||||||
{
|
{
|
||||||
_logger.Information($"正在清理缓存文件: {cacheFileName}");
|
string cacheFileName = Path.Combine(_appConfig.DownloadOptions.SavePath, taskInfo.FileName);
|
||||||
try
|
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}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
{
|
{
|
||||||
"Database": {
|
"Database": {
|
||||||
|
"MaxConnectionPoolSize": 100,
|
||||||
"Common": {
|
"Common": {
|
||||||
"Host": "47.243.56.137",
|
"Host": "47.243.56.137",
|
||||||
"Port": 3306,
|
"Port": 3306,
|
||||||
|
|
Loading…
Reference in a new issue