C# 在.NET核心工作程序服务中执行运行状况检查

C# 在.NET核心工作程序服务中执行运行状况检查,c#,docker,.net-core,service,C#,Docker,.net Core,Service,如何在.NET核心工作者服务中实现健康检查 该服务将在Docker内部运行,并且需要能够检查服务的运行状况。我所做的是将Microsoft.NET.Sdk.Web添加到我的工作程序中,然后配置一个Web主机与工作程序一起运行: Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(builder => { builder.UseStartup<Startup>(); })

如何在.NET核心工作者服务中实现健康检查


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

我所做的是将Microsoft.NET.Sdk.Web添加到我的工作程序中,然后配置一个Web主机与工作程序一起运行:

Host.CreateDefaultBuilder(args)
    .ConfigureWebHostDefaults(builder =>
    {
        builder.UseStartup<Startup>();
    })
    .ConfigureServices((hostContext, services) =>
    {
        services.AddHostedService<Worker>();
        services.AddLogging(builder =>
            builder
                .AddDebug()
                .AddConsole()
        );
    });
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefault(生成器=>
{
builder.UseStartup();
})
.ConfigureServices((主机上下文,服务)=>
{
services.AddHostedService();
services.AddLogging(builder=>
建设者
.AddDebug()
.AddConsole()
);
});

这样做,剩下的就是和ASP.NET内核一样。

< P>我认为你也应该考虑保留微软.NET.SDK.Works./P> 不要仅仅因为运行状况检查而更改整个sdk

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

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);
        }
    }
}
然后可以添加如下扩展方法:

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

另一种方法是实现
IHealthCheckPublisher

这种方法的好处是能够重复使用现有的
IHealthCheck
s或与依赖
IHealthCheck
接口(如)的第三方库集成

尽管您仍然将
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;
  }
}
公共静态IHostBuilder CreateHostBuilder(字符串[]args)
{
返回主机
.CreateDefaultBuilder(args)
.ConfigureServices((主机上下文,服务)=>
{
服务
.AddHealthChecks()
.AddCheck(“redis\u health\u check”)
.AddCheck(“rfa_健康检查”);
services.AddSingleton();
配置(选项=>
{
选项。延迟=时间跨度。从秒(5);
options.Period=TimeSpan.FromSeconds(5);
});
});
}
公共类HealthCheckPublisher:IHealthCheckPublisher
{
私有只读字符串\u文件名;
private HealthStatus\u prevStatus=HealthStatus.不健康;
公共健康检查发布者()
{
_fileName=Environment.GetEnvironmentVariable(envariablenames.DOCKER\u HEALTHCHECK\u FILEPATH)??
Path.GetTempFileName();
}
公共任务PublishAsync(HealthReport报告,CancellationToken CancellationToken)
{
//AWS将使用命令检查该文件是否存在于容器内
//测试-f$DOCKER\u运行状况\u检查\u文件路径
var fileExists=\u prevStatus==HealthStatus.health;
if(report.Status==HealthStatus.health)
{
如果(!fileExists)
{
使用var=File.Create(_fileName);
}
}
else if(文件存在)
{
删除(_文件名);
}
_prevStatus=报告状态;
返回Task.CompletedTask;
}
}

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

您可以使用不同的协议,如TCP

总的想法是:

  • 创建单独的backgorund服务,创建TCP服务器(请看)
  • 当您收到请求时,您有两个选择:1。如果应用程序是健康的,则接受TCP连接,如果不拒绝它
  • 如果您使用容器,Orchestrator工具应该有一个选项可以通过TCP调用is(在k8s中有属性)
  • 如果您需要更详细的信息,您可以查看:


    干杯

    这会报告辅助服务的错误线程吗?我的意思是,由于未处理的异常和卡住,WorkService无法执行任务,而/HealthEndoPoT不认为它总是响应于200 OK,因为它在一起运行在不同的线程中。不,您必须以某种方式从健康检查中检测到服务停止响应。也许是工作人员的心跳,但即使如此,我也看到过不完全准确的情况。我认为这是在使用Kubernetes时不使用Microsoft.NET.Sdk.Web的最优雅的方法。您甚至可以打开两个不同的TCP端口,一个用于live,另一个用于ready端点。
    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;
      }
    }