在 .NET Core Worker Service 中执行健康检查

zby*_*zek 23 c# service docker .net-core

如何在 .NET Core Worker Service 中实现健康检查?

该服务将在 Docker 内部运行,并且需要能够检查服务的健康状况。

chu*_*1ty 13

我认为将 SDK 更改为Microsoft.NET.Sdk.Web不值得。您会仅仅因为一项健康检查而包含额外的中间件吗?不,谢谢 ...

您可以做的是使用不同的协议,例如 TCP。

总体思路是:

  1. 创建一个单独的后台服务来创建 TCP 服务器(查看TcpListener.cs
  2. 当您收到请求时,您有两个选择:如果应用程序运行状况良好,则接受 TCP 连接,否则拒绝它。
  3. 如果你使用容器,你的编排器应该有一个通过 TCP 调用它的选项(在 k8s 中有一个属性tcpSocket

如果您需要更详细的信息,您可以查看:Monitoring Health of ASP.NET Core Background Services With TCP Probes on Kubernetes

干杯!


kam*_*nzo 10

添加 HTTPListener 并公开运行状况检查端点。

使用HTTPListener不需要添加Microsoft.NET.Sdk.Web SDK。

程序.cs

    using Consumer;
    
    IHost host = Host.CreateDefaultBuilder(args)
        .ConfigureServices(services =>
        {
            services.AddHostedService<Worker>();
            services.AddHostedService<HttpHealthcheck>();
        })
        .Build();
    
    await host.RunAsync();
Run Code Online (Sandbox Code Playgroud)

HttpHealthcheck.cs

    using System.Net;
    using System.Text;
    
    namespace Consumer;
    
    public class HttpHealthcheck : BackgroundService
    {
        private readonly ILogger<Worker> _logger;
        private readonly HttpListener _httpListener;
        private readonly IConfiguration _configuration;
    
    
        public HealthcheckHttpListener(ILogger<Worker> logger, IConfiguration configuration)
        {
            _logger = logger;
            _configuration = configuration;
            _httpListener = new HttpListener();
        }
    
    
        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
    
            _httpListener.Prefixes.Add($"http://*:5001/healthz/live/");    
            _httpListener.Prefixes.Add($"http://*:5001/healthz/ready/");
    
            _httpListener.Start();
            _logger.LogInformation($"Healthcheck listening...");
    
            while (!stoppingToken.IsCancellationRequested)
            {
                HttpListenerContext ctx = null;
                try
                {
                    ctx = await _httpListener.GetContextAsync();
                }
                catch (HttpListenerException ex)
                {
                    if (ex.ErrorCode == 995) return;
                }
    
                if (ctx == null) continue;
    
                var response = ctx.Response;
                response.ContentType = "text/plain";
                response.Headers.Add(HttpResponseHeader.CacheControl, "no-store, no-cache");
                response.StatusCode = (int)HttpStatusCode.OK;
    
                var messageBytes = Encoding.UTF8.GetBytes("Healthy");
                response.ContentLength64 = messageBytes.Length;
                await response.OutputStream.WriteAsync(messageBytes, 0, messageBytes.Length);
                response.OutputStream.Close();
                response.Close();
            }
        }
    }
Run Code Online (Sandbox Code Playgroud)

  • 就我而言,它是 Kafka Consumer 的实现。您可以在此处阅读有关工作人员服务的更多信息:https://learn.microsoft.com/en-us/dotnet/core/extensions/workers (2认同)

Vei*_*edo 7

另一种方法是实现IHealthCheckPublisher.

这种方法的好处是能够重用您现有的IHealthChecks 或与依赖于IHealthCheck接口的3rd 方库(如这个)集成。

尽管您仍然Microsoft.NET.Sdk.Web以 SDK 为目标,但您不需要添加任何 asp.net 细节。

下面是一个例子:

public static IHostBuilder CreateHostBuilder(string[] args)
{
  return Host
    .CreateDefaultBuilder(args)
    .ConfigureServices((hostContext, services) =>
    {
      services
        .AddHealthChecks()
        .AddCheck<RedisHealthCheck>("redis_health_check")
        .AddCheck<RfaHealthCheck>("rfa_health_check");

      services.AddSingleton<IHealthCheckPublisher, HealthCheckPublisher>();
      services.Configure<HealthCheckPublisherOptions>(options =>
      {
        options.Delay = TimeSpan.FromSeconds(5);
        options.Period = TimeSpan.FromSeconds(5);
      });
    });
}

public class HealthCheckPublisher : IHealthCheckPublisher
{
  private readonly string _fileName;
  private HealthStatus _prevStatus = HealthStatus.Unhealthy;

  public HealthCheckPublisher()
  {
    _fileName = Environment.GetEnvironmentVariable(EnvVariableNames.DOCKER_HEALTHCHECK_FILEPATH) ??
                Path.GetTempFileName();
  }

  public Task PublishAsync(HealthReport report, CancellationToken cancellationToken)
  {
    // AWS will check if the file exists inside of the container with the command
    // test -f $DOCKER_HEALTH_CHECK_FILEPATH

    var fileExists = _prevStatus == HealthStatus.Healthy;

    if (report.Status == HealthStatus.Healthy)
    {
      if (!fileExists)
      {
        using var _ = File.Create(_fileName);
      }
    }
    else if (fileExists)
    {
      File.Delete(_fileName);
    }

    _prevStatus = report.Status;

    return Task.CompletedTask;
  }
}
Run Code Online (Sandbox Code Playgroud)


dko*_*nos 5

我认为您还应该考虑保留 Microsoft.NET.Sdk.Worker。

不要仅仅因为健康检查而改变整个sdk。

然后您可以创建一个后台服务(就像主工作人员一样),以便更新文件以写入例如当前时间戳。后台健康检查工作人员的一个示例是:

public class HealthCheckWorker : BackgroundService
{
    private readonly int _intervalSec;
    private readonly string _healthCheckFileName;

    public HealthCheckWorker(string healthCheckFileName, int intervalSec)
    {
        this._intervalSec = intervalSec;
        this._healthCheckFileName = healthCheckFileName;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (true)
        {
            File.WriteAllText(this._healthCheckFileName, DateTime.UtcNow.ToString());
            await Task.Delay(this._intervalSec * 1000, stoppingToken);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后你可以添加一个扩展方法,如下所示:

public static class HealthCheckWorkerExtensions
{
    public static void AddHealthCheck(this IServiceCollection services,
        string healthCheckFileName, int intervalSec)
    {
        services.AddHostedService<HealthCheckWorker>(x => new HealthCheckWorker(healthCheckFileName, intervalSec));
    }
}
Run Code Online (Sandbox Code Playgroud)

这样您就可以在服务中添加健康检查支持

.ConfigureServices(services =>
{
    services.AddHealthCheck("hc.txt", 5);
})
Run Code Online (Sandbox Code Playgroud)


小智 2

为了实现此目的,我所做的是将 Microsoft.NET.Sdk.Web 添加到我的 Worker,然后配置一个 Web 主机与该 Worker 一起运行:

Host.CreateDefaultBuilder(args)
    .ConfigureWebHostDefaults(builder =>
    {
        builder.UseStartup<Startup>();
    })
    .ConfigureServices((hostContext, services) =>
    {
        services.AddHostedService<Worker>();
        services.AddLogging(builder =>
            builder
                .AddDebug()
                .AddConsole()
        );
    });
Run Code Online (Sandbox Code Playgroud)

完成此操作后,剩下要做的就是映射运行状况检查端点,就像通常使用 ASP.NET Core 所做的那样。