You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过外部HTTP调用获取C#后台服务的实时健康状态?

问题诊断与解决方案

核心问题:实例不唯一

你当前的代码存在Worker实例重复创建的问题:

  • 外部Host注册了一个Worker单例作为后台服务
  • Worker内部的CreateWebHostBuilder又在WebHost的服务容器里注册了另一个Worker单例
  • 控制器拿到的是WebHost内部的Worker实例,而非实际运行的后台服务实例,所以状态始终不变

修复方案:整合Web与后台服务到同一Host

将Web API和后台服务托管在同一个ASP.NET Core Host中,确保Worker实例唯一,同时解决线程安全问题。

1. 重构Worker.cs(仅保留后台服务逻辑)

using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace MyBackgroundServiceHealth
{
    public class Worker : BackgroundService
    {
        private readonly ILogger<Worker> _logger;
        // 用volatile保证多线程下的状态可见性
        private volatile string _status = "Healthy";

        public string Status => _status;

        public Worker(ILogger<Worker> logger)
        {
            _logger = logger;
        }

        public override async Task StartAsync(CancellationToken cancellationToken)
        {
            try
            {
                _logger.LogInformation("Starting the service...");
                _status = "Running";
                _logger.LogInformation("Service started. Status: {status}", _status);
                await base.StartAsync(cancellationToken);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to start the service.");
                _status = "Not Running";
                _logger.LogInformation("Service failed to start. Status: {status}", _status);
            }
        }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            try
            {
                while (!stoppingToken.IsCancellationRequested)
                {
                    if (_logger.IsEnabled(LogLevel.Information))
                    {
                        _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
                    }
                    await Task.Delay(180000, stoppingToken);
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "An error occurred during execution.");
                _status = "Error";
                _logger.LogInformation("Service encountered an error. Status: {status}", _status);
            }
        }

        public override async Task StopAsync(CancellationToken cancellationToken)
        {
            _logger.LogInformation("Stopping the service...");
            _status = "Stopping";
            _logger.LogInformation("Service is stopping. Status: {status}", _status);
            await base.StopAsync(cancellationToken);
            _status = "Stopped";
            _logger.LogInformation("Service stopped. Status: {status}", _status);
        }
    }
}

2. 修改Program.cs(整合Web API与后台服务)

using MyBackgroundServiceHealth;

var builder = WebApplication.CreateBuilder(args);

// 注册Controllers
builder.Services.AddControllers();
// 注册Worker为单例,同时作为IHostedService
builder.Services.AddSingleton<Worker>();
builder.Services.AddHostedService(sp => sp.GetRequiredService<Worker>());

var app = builder.Build();

// 配置路由
app.UseRouting();
app.UseEndpoints(endpoints =>
{
    endpoints.MapControllers();
});

// 配置Kestrel监听端口
app.Urls.Add("http://0.0.0.0:5000");

app.Run();

3. HealthController保持不变

控制器注入的Worker现在和后台服务是同一个实例,能实时获取状态。

更优方案:使用.NET官方健康检查框架

对于生产环境,推荐使用Microsoft.Extensions.Diagnostics.HealthChecks来标准化健康状态暴露:

1. 添加NuGet包

Install-Package Microsoft.Extensions.Diagnostics.HealthChecks
Install-Package AspNetCore.HealthChecks.UI.Client

2. 注册健康检查

在Program.cs中添加:

builder.Services.AddHealthChecks()
    .AddCheck<WorkerHealthCheck>("worker_status");

3. 实现自定义健康检查

using Microsoft.Extensions.Diagnostics.HealthChecks;
using System.Threading;
using System.Threading.Tasks;

namespace MyBackgroundServiceHealth
{
    public class WorkerHealthCheck : IHealthCheck
    {
        private readonly Worker _worker;

        public WorkerHealthCheck(Worker worker)
        {
            _worker = worker;
        }

        public Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context, CancellationToken cancellationToken = default)
        {
            return _worker.Status switch
            {
                "Running" => Task.FromResult(HealthCheckResult.Healthy("Worker is running")),
                "Error" => Task.FromResult(HealthCheckResult.Unhealthy("Worker encountered an error")),
                "Stopping" => Task.FromResult(HealthCheckResult.Degraded("Worker is stopping")),
                "Stopped" => Task.FromResult(HealthCheckResult.Unhealthy("Worker is stopped")),
                "Not Running" => Task.FromResult(HealthCheckResult.Unhealthy("Worker failed to start")),
                _ => Task.FromResult(HealthCheckResult.Healthy("Worker is healthy"))
            };
        }
    }
}

4. 暴露健康检查端点

在Program.cs的中间件配置中添加:

app.MapHealthChecks("/health", new Microsoft.AspNetCore.Diagnostics.HealthChecks.HealthCheckOptions
{
    ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});

现在访问/health就能拿到标准化的健康状态响应,更适合生产环境的监控集成。

内容的提问来源于stack exchange,提问作者Sharpeye500

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 16:25:54