@@ -0,0 +1,85 @@
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Diagnostics.HealthChecks;
|
||||
using StackExchange.Redis;
|
||||
using YhCore.Repository;
|
||||
|
||||
namespace YhCore.WebApi.HealthChecks
|
||||
{
|
||||
/// <summary>
|
||||
/// 数据库连通性检查:复用已注册的 YHDbContext(随 DatabaseProvider 自动适配 SqlServer/MySql)。
|
||||
/// 每个检查自带 2 秒超时,防止连接池耗尽时探针自身挂死、反而探不出来。
|
||||
/// </summary>
|
||||
public class DbContextHealthCheck : IHealthCheck
|
||||
{
|
||||
private readonly YHDbContext _dbContext;
|
||||
private static readonly TimeSpan Timeout = TimeSpan.FromSeconds(2);
|
||||
|
||||
public DbContextHealthCheck(YHDbContext dbContext)
|
||||
{
|
||||
_dbContext = dbContext;
|
||||
}
|
||||
|
||||
public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
var probe = Task.Run(async () =>
|
||||
{
|
||||
await _dbContext.Database.OpenConnectionAsync(cancellationToken);
|
||||
using var command = _dbContext.Database.GetDbConnection().CreateCommand();
|
||||
command.CommandText = "SELECT 1";
|
||||
await command.ExecuteScalarAsync(cancellationToken);
|
||||
}, CancellationToken.None);
|
||||
|
||||
var finished = await Task.WhenAny(probe, Task.Delay(Timeout, CancellationToken.None));
|
||||
if (finished != probe)
|
||||
return HealthCheckResult.Unhealthy("database probe timed out after 2s");
|
||||
|
||||
await probe; // 传播异常
|
||||
return HealthCheckResult.Healthy();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return HealthCheckResult.Unhealthy("database probe failed: " + ex.Message, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Redis 连通性检查:复用全局单例 IConnectionMultiplexer(YhCore.Common.Cache 注册)。
|
||||
/// </summary>
|
||||
public class RedisHealthCheck : IHealthCheck
|
||||
{
|
||||
private readonly IConnectionMultiplexer _multiplexer;
|
||||
private static readonly TimeSpan Timeout = TimeSpan.FromSeconds(2);
|
||||
|
||||
public RedisHealthCheck(IConnectionMultiplexer multiplexer)
|
||||
{
|
||||
_multiplexer = multiplexer;
|
||||
}
|
||||
|
||||
public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!_multiplexer.IsConnected)
|
||||
return HealthCheckResult.Unhealthy("redis connection is not established");
|
||||
|
||||
var probe = _multiplexer.GetDatabase().PingAsync();
|
||||
var finished = await Task.WhenAny(probe, Task.Delay(Timeout, CancellationToken.None));
|
||||
if (finished != probe)
|
||||
return HealthCheckResult.Unhealthy("redis ping timed out after 2s");
|
||||
|
||||
var latency = await probe;
|
||||
return HealthCheckResult.Healthy("ping " + latency);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return HealthCheckResult.Unhealthy("redis probe failed: " + ex.Message, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -205,6 +205,9 @@ namespace YhCore.WebApi
|
||||
{
|
||||
endpoints.MapControllers();
|
||||
|
||||
// 免认证健康探针:健康 200 / 不健康 503(容器与负载均衡用)
|
||||
endpoints.MapHealthChecks("/health");
|
||||
|
||||
// 根路径登录状态页:SSO 开启时未登录会先被中间件 302 到 CAS;登录后在此显示当前用户。
|
||||
endpoints.MapGet("/", async context =>
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user