@@ -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();
|
endpoints.MapControllers();
|
||||||
|
|
||||||
|
// 免认证健康探针:健康 200 / 不健康 503(容器与负载均衡用)
|
||||||
|
endpoints.MapHealthChecks("/health");
|
||||||
|
|
||||||
// 根路径登录状态页:SSO 开启时未登录会先被中间件 302 到 CAS;登录后在此显示当前用户。
|
// 根路径登录状态页:SSO 开启时未登录会先被中间件 302 到 CAS;登录后在此显示当前用户。
|
||||||
endpoints.MapGet("/", async context =>
|
endpoints.MapGet("/", async context =>
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user