修复数据库连接数过多导致程序崩溃问题
This commit is contained in:
parent
871e7d47a3
commit
5bf3549522
5 changed files with 193 additions and 18 deletions
|
@ -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 = "获取连接池信息失败"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in a new issue