最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c# - Health check of a background service via REST call - Stack Overflow

programmeradmin1浏览0评论

I have a background service in C# that I want to check the health of the background service.

Whether it is running or in error or stopped, then I want to see the status of the background service health via a Web API GET method. In the background service process, when the service starts status is set to running, if stops it is set to stopped (and so on).

I tried using

webBuilder.UseKestrel(options => options.ListenAnyIP(5000)}); 

via CreateWebHostBuilder, use singleton to register the worker process.

In the other Web API project, I have a project reference to this worker process and register it in the program.cs as a singleton.

public class Worker: BackgroundService {}

The issue is the controller get method I have doesn't show the right value, I inject the worker process and access the status variable, it always shows the same value.

In the controller method:

public IActionResult Get()
{
    return Ok(_worker.Status);
}

How can I get real time health status from a background service through a http call that happens outside of the worker process project?

Is there a better option/approach for this?

Code - Worker.cs:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace MyBackgroundServiceHealth
{
    public class Worker : BackgroundService
    {
        private readonly ILogger<Worker> _logger;
        private IHost _webHost = null!;
        private 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...");
                _webHost = CreateWebHostBuilder().Build();
                await _webHost.StartAsync(cancellationToken);
                _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 _webHost.StopAsync(cancellationToken);
            await base.StopAsync(cancellationToken);
            _status = "Stopped";
            _logger.LogInformation("Service stopped. Status: {status}", _status);
        }

        private IHostBuilder CreateWebHostBuilder() =>
            Host.CreateDefaultBuilder()
                .ConfigureWebHost(webBuilder =>
                {
                    webBuilder.ConfigureServices(services =>
                    {
                        services.AddSingleton<Worker>();
                        services.AddControllers();
                    });
                    webBuilder.Configure(app =>
                    {
                        app.UseRouting();
                        app.UseEndpoints(endpoints =>
                        {
                            endpoints.MapControllers();
                        });
                    });
                    webBuilder.UseKestrel(options =>
                    {
                        options.ListenAnyIP(5000);// Listen on port 5000
                    });
                });
    }
}

In program.cs of the background service, I have this code:

using MyBackgroundServiceHealth;

var builder = Host.CreateApplicationBuilder(args);

// Register the Worker as a singleton service for dependency injection
builder.Services.AddSingleton<Worker>();
builder.Services.AddSingleton<IHostedService>(sp=>sp.GetRequiredService<Worker>());

var host = builder.Build();
host.Run();

Controller:

 [Route("api/[controller]")]
 [ApiController]
 public class HealthController : ControllerBase
 {
     private readonly Worker _worker;
     private readonly ILogger<HealthController> _logger;

     public HealthController(Worker worker, ILogger<HealthController> logger)
     {
         _worker = worker;
         _logger = logger;
     }

     [HttpGet]
     public IActionResult Get()
     {
         _logger.LogInformation("Health check status: {status}", _worker.Status);
         return Ok(_worker.Status);
     }
 }

In the Web API program.cs:

builder.Services.AddSingleton<Worker>();
builder.Services.AddSingleton<IHostedService>(sp => sp.GetRequiredService<Worker>());

I have a background service in C# that I want to check the health of the background service.

Whether it is running or in error or stopped, then I want to see the status of the background service health via a Web API GET method. In the background service process, when the service starts status is set to running, if stops it is set to stopped (and so on).

I tried using

webBuilder.UseKestrel(options => options.ListenAnyIP(5000)}); 

via CreateWebHostBuilder, use singleton to register the worker process.

In the other Web API project, I have a project reference to this worker process and register it in the program.cs as a singleton.

public class Worker: BackgroundService {}

The issue is the controller get method I have doesn't show the right value, I inject the worker process and access the status variable, it always shows the same value.

In the controller method:

public IActionResult Get()
{
    return Ok(_worker.Status);
}

How can I get real time health status from a background service through a http call that happens outside of the worker process project?

Is there a better option/approach for this?

Code - Worker.cs:

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System;
using System.Threading;
using System.Threading.Tasks;

namespace MyBackgroundServiceHealth
{
    public class Worker : BackgroundService
    {
        private readonly ILogger<Worker> _logger;
        private IHost _webHost = null!;
        private 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...");
                _webHost = CreateWebHostBuilder().Build();
                await _webHost.StartAsync(cancellationToken);
                _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 _webHost.StopAsync(cancellationToken);
            await base.StopAsync(cancellationToken);
            _status = "Stopped";
            _logger.LogInformation("Service stopped. Status: {status}", _status);
        }

        private IHostBuilder CreateWebHostBuilder() =>
            Host.CreateDefaultBuilder()
                .ConfigureWebHost(webBuilder =>
                {
                    webBuilder.ConfigureServices(services =>
                    {
                        services.AddSingleton<Worker>();
                        services.AddControllers();
                    });
                    webBuilder.Configure(app =>
                    {
                        app.UseRouting();
                        app.UseEndpoints(endpoints =>
                        {
                            endpoints.MapControllers();
                        });
                    });
                    webBuilder.UseKestrel(options =>
                    {
                        options.ListenAnyIP(5000);// Listen on port 5000
                    });
                });
    }
}

In program.cs of the background service, I have this code:

using MyBackgroundServiceHealth;

var builder = Host.CreateApplicationBuilder(args);

// Register the Worker as a singleton service for dependency injection
builder.Services.AddSingleton<Worker>();
builder.Services.AddSingleton<IHostedService>(sp=>sp.GetRequiredService<Worker>());

var host = builder.Build();
host.Run();

Controller:

 [Route("api/[controller]")]
 [ApiController]
 public class HealthController : ControllerBase
 {
     private readonly Worker _worker;
     private readonly ILogger<HealthController> _logger;

     public HealthController(Worker worker, ILogger<HealthController> logger)
     {
         _worker = worker;
         _logger = logger;
     }

     [HttpGet]
     public IActionResult Get()
     {
         _logger.LogInformation("Health check status: {status}", _worker.Status);
         return Ok(_worker.Status);
     }
 }

In the Web API program.cs:

builder.Services.AddSingleton<Worker>();
builder.Services.AddSingleton<IHostedService>(sp => sp.GetRequiredService<Worker>());
Share Improve this question edited Mar 28 at 8:32 marc_s 756k184 gold badges1.4k silver badges1.5k bronze badges asked Mar 28 at 5:34 Sharpeye500Sharpeye500 9,09326 gold badges97 silver badges148 bronze badges 6
  • There is not enough code to do anything but wildly guess what may have gone wrong. Please add a minimal example demonstrating this problem. – nvoigt Commented Mar 28 at 7:12
  • Wait, what are you trying to do here. Use a generic host & background service... to start another host inside it? Why add the extra layer of indirection? – Jeremy Lakeman Commented Mar 28 at 8:08
  • That's Phase II; for Phase I, I send myself email messages when the "health monitor" detects an issue. I prefer to have the problem find me; instead of the other way around. – Gerry Schmitz Commented Mar 28 at 14:33
  • I want to check the status of a worker backgroundservice say MySampleWorker which implements backgroundservice(running/stopped) via a http call(via API Controller), so, i can check the status outside independently. – Sharpeye500 Commented Mar 28 at 14:34
  • I want to build a dashboard page that displays status of different worker processes, to me this is one approach, to make a rest call and then consume that in the ui. If you have other options, you can share that. – Sharpeye500 Commented Mar 28 at 15:34
 |  Show 1 more comment

1 Answer 1

Reset to default 2

Let me guess, you registered the same service twice;

services.AddSingleton<Worker>();
services.AddHostedService<Worker>();

Which will result in two singleton instances, registered with different interfaces. You can ensure there's only one instance, by registering the other with a factory method;

services.AddSingleton<Worker>();
services.AddSingleton<IHostedService>(sp => sp.GetService<Worker>());

You could instead inject a IHostApplicationLifetime dependency and .StopApplication(); if you catch any exceptions.

发布评论

评论列表(0)

  1. 暂无评论