修复数据库连接数过多导致程序崩溃问题

This commit is contained in:
root 2024-12-01 20:12:18 +08:00
parent 871e7d47a3
commit 5bf3549522
5 changed files with 193 additions and 18 deletions

View file

@ -358,5 +358,32 @@ namespace iFileProxy.Controllers
});
}
}
/// <summary>
/// 获取数据库连接池状态
/// </summary>
[HttpGet("GetConnectionPoolInfo")]
public async Task<ActionResult<CommonRsp>> GetConnectionPoolInfo()
{
try
{
var poolInfo = await _dbGateService.GetConnectionPoolInfoAsync();
return Ok(new CommonRsp
{
Retcode = 0,
Message = "success",
Data = poolInfo
});
}
catch (Exception ex)
{
_logger.Error($"获取连接池信息失败: {ex.Message}");
return Ok(new CommonRsp
{
Retcode = 1,
Message = "获取连接池信息失败"
});
}
}
}
}

View file

@ -106,4 +106,81 @@ namespace iFileProxy.Models
public static string iFileProxy = "iFileProxy_Db";
}
/// <summary>
/// 数据库连接池信息
/// </summary>
public class ConnectionPoolInfo
{
/// <summary>
/// 当前总连接数
/// </summary>
public long TotalConnections { get; set; }
/// <summary>
/// 活跃连接数
/// </summary>
public long ActiveConnections { get; set; }
/// <summary>
/// 空闲连接数
/// </summary>
public long SleepingConnections { get; set; }
/// <summary>
/// 连接池最大连接数
/// </summary>
public int MaxPoolSize { get; set; }
/// <summary>
/// 连接详情列表
/// </summary>
public List<ProcessListInfo> Connections { get; set; } = [];
}
/// <summary>
/// MySQL 进程列表信息
/// </summary>
public class ProcessListInfo
{
/// <summary>
/// 连接ID
/// </summary>
public long Id { get; set; }
/// <summary>
/// 用户名
/// </summary>
public string User { get; set; } = string.Empty;
/// <summary>
/// 主机信息
/// </summary>
public string Host { get; set; } = string.Empty;
/// <summary>
/// 数据库名
/// </summary>
public string Db { get; set; } = string.Empty;
/// <summary>
/// 命令类型
/// </summary>
public string Command { get; set; } = string.Empty;
/// <summary>
/// 执行时间(秒)
/// </summary>
public long Time { get; set; }
/// <summary>
/// 连接状态
/// </summary>
public string State { get; set; } = string.Empty;
/// <summary>
/// 执行的SQL
/// </summary>
public string Info { get; set; } = string.Empty;
}
}

View file

@ -16,18 +16,18 @@ namespace iFileProxy
SerilogConfig.CreateLogger();
Serilog.ILogger logger = Log.Logger.ForContext<Program>();
Console.Write(" "); // ǿ<EFBFBD><EFBFBD>֢<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ſ<EFBFBD>ʼ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>־<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>þ<EFBFBD>
Console.Write(" "); // 补全日志第一行开头的空白
var builder = WebApplication.CreateBuilder(args);
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD> CORS <20><><EFBFBD><EFBFBD>
// CORS配置
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowFrontend",
builder =>
{
builder
.WithOrigins("http://localhost:3000", "http://admin.gitdl.cn", "https://admin.gitdl.cn","http://47.243.56.137:50050") // ǰ<>˵<EFBFBD>ַ
.WithOrigins("http://localhost:3000", "http://admin.gitdl.cn", "https://admin.gitdl.cn","http://47.243.56.137:50050")
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials();
@ -55,10 +55,9 @@ namespace iFileProxy
builder.Services.AddSingleton<TaskManager>();
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>֤<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
// 添加验证服务
builder.Services.AddScoped<AuthService>();
// <20><><EFBFBD><EFBFBD>JWT<57><54>֤
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
@ -77,9 +76,8 @@ namespace iFileProxy
var app = builder.Build();
AppConfig.CheckAppConfig(app.Services);
LocalCacheManager localCacheManager = new(app.Services);
// 全局错误处理中间件
app.UseMiddleware<ErrorHandlerMiddleware>();
app.UseSerilogRequestLogging(options =>
{
@ -98,17 +96,21 @@ namespace iFileProxy
app.UseSwaggerUI();
}
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>м<EFBFBD><D0BC>
app.UseMiddleware<ErrorHandlerMiddleware>();
// 检查自定义配置
AppConfig.CheckAppConfig(app.Services);
// 初始化缓存管理器
LocalCacheManager localCacheManager = new(app.Services);
app.UseCors("AllowFrontend");
app.UseHttpsRedirection();
// <20><><EFBFBD><EFBFBD>Ĭ<EFBFBD><C4AC><EFBFBD>ļ<EFBFBD>ѡ<EFBFBD><D1A1>
var defaultFilesOptions = new DefaultFilesOptions();
defaultFilesOptions.DefaultFileNames.Clear(); // <20><><EFBFBD>Ĭ<EFBFBD><C4AC><EFBFBD>б<EFBFBD>
defaultFilesOptions.DefaultFileNames.Add("index.html"); // <20><><EFBFBD><EFBFBD><EFBFBD>Զ<EFBFBD><D4B6><EFBFBD>Ĭ<EFBFBD><C4AC><EFBFBD>ļ<EFBFBD>
defaultFilesOptions.DefaultFileNames.Clear();
defaultFilesOptions.DefaultFileNames.Add("index.html");
app.UseDefaultFiles(defaultFilesOptions);
@ -120,7 +122,7 @@ namespace iFileProxy
app.Services.GetRequiredService<Dictionary<string, Dictionary<string, uint>>>(),
AppConfig.GetCurrConfig().SecurityOptions.DailyRequestLimitPerIP);
// <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>м<EFBFBD><EFBFBD>
// JWT中间件
app.UseMiddleware<JwtMiddleware>();
app.MapControllers();

View file

@ -115,11 +115,22 @@ namespace iFileProxy.Services
if (db_user == null || db_password == null || db_host == null || db_port == null)
throw new NoNullAllowedException("数据库配置获取失败,不允许为空的字段出现空值");
string db_connstr = $"server={db_host};user={db_user};database={Db.DatabaseName};port={db_port};password={db_password};Pooling=true;MaximumPoolSize=500;";
MySqlConnection conn;
var builder = new MySqlConnectionStringBuilder
{
Server = db_host,
UserID = db_user,
Database = Db.DatabaseName,
Port = (uint)db_port,
Password = db_password,
Pooling = true,
MinimumPoolSize = 1,
MaximumPoolSize = 50,
ConnectionLifeTime = 300 // 连接最大生命周期(秒)
};
try
{
conn = new MySqlConnection(db_connstr);
var conn = new MySqlConnection(builder.ConnectionString);
conn.Open();
return conn;
}
@ -900,5 +911,61 @@ namespace iFileProxy.Services
var users = await ExecuteQueryAsync<User>(sql, parameters);
return users.FirstOrDefault();
}
/// <summary>
/// 获取数据库连接池状态
/// </summary>
/// <returns>连接池信息</returns>
public async Task<ConnectionPoolInfo> GetConnectionPoolInfoAsync()
{
try
{
var appName = "iFileProxy"; // 程序名称
var parameters = new Dictionary<string, object>
{
{ "@appName", $"%{appName}%" }
};
// 获取总连接数
var totalSql = @"SELECT COUNT(*)
FROM information_schema.processlist
WHERE info LIKE @appName OR host LIKE @appName";
var totalConnections = await ExecuteScalarAsync<long>(totalSql, parameters);
// 获取活跃连接数
var activeSql = @"SELECT COUNT(*)
FROM information_schema.processlist
WHERE Command != 'Sleep'
AND (info LIKE @appName OR host LIKE @appName)";
var activeConnections = await ExecuteScalarAsync<long>(activeSql, parameters);
// 获取睡眠连接数
var sleepingSql = @"SELECT COUNT(*)
FROM information_schema.processlist
WHERE Command = 'Sleep'
AND (info LIKE @appName OR host LIKE @appName)";
var sleepingConnections = await ExecuteScalarAsync<long>(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";
var connections = await ExecuteQueryAsync<ProcessListInfo>(detailSql, parameters);
return new ConnectionPoolInfo
{
TotalConnections = totalConnections,
ActiveConnections = activeConnections,
SleepingConnections = sleepingConnections,
MaxPoolSize = 50, // 从配置中获取的最大连接数
Connections = connections
};
}
catch (Exception ex)
{
_logger.Error($"获取连接池信息失败: {ex.Message}");
throw;
}
}
}
}

View file

@ -39,7 +39,7 @@ namespace iFileProxy.Services
public void CheckAndCleanCache(object state)
{
// 初始化并打开一个MySQL连接 防止后续数据过多导致程序crush
var dbConn = _dbGateService.GetAndOpenDBConn(DbConfigName.iFileProxy);
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);
@ -67,6 +67,8 @@ namespace iFileProxy.Services
_dbGateService.UpdateTaskStatus(taskInfo,dbConn);
}
}
}
/// <summary>