IHostedService .NET 6 not starting when deployed
Asked Answered
O

2

7

I have an API which contains a HostedService that is built in VS2022 with .Net 6.

When I run locally the service is called as expected and everything works fine but when deplyed, the service doesn't seem to start.

I have tried many different configurations and even tried using a background service but it all has the same result. Here is my code:

I have an existing app build in VS2019 .Net Core 3.1 that has a HostedService and is working fine. I noticed that when I converted my .Net Core app to .Net 6, the service did not start when I deployed so I decided to just build a little app to try and find what's causing the issue.

Program.cs

using HostedServices.Services;

var builder = WebApplication.CreateBuilder(args);

builder.Host.UseSerilog((context, loggerConfiguration) => loggerConfiguration
        .ReadFrom.Configuration(context.Configuration)
        .Enrich.FromLogContext()
        .Enrich.WithMachineName());

// Add services to the container.

builder.Services.AddControllers();

builder.Services.AddHostedService<MainService>();
var app = builder.Build();

// Configure the HTTP request pipeline.

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Here is the Hosted Service

namespace HostedServices.Services
{
    public class MainService : IHostedService, IDisposable
    {
        private int executionCount = 0;
        private readonly ILogger<MainService> _logger;
        private Timer _timer;
        private Task _executingTask;
        private readonly CancellationTokenSource _stoppingCts = new CancellationTokenSource();

        public MainService(ILogger<MainService> logger)
        {
            _logger = logger;
        }
        public Task StartAsync(CancellationToken cancellationToken)
        {
   
            _logger.LogInformation($"Test Hosted Service Started {DateTime.Now}.");
            _timer = new Timer(DoWork, null, TimeSpan.Zero,
                    TimeSpan.FromMinutes(1));

            return Task.CompletedTask;

        }

        private void DoWork(object state)
        {
            _executingTask = DoWorkAsync(_stoppingCts.Token);
        }

        private async Task DoWorkAsync(CancellationToken token)
        {
            _logger.LogInformation(
                    "Doing work: {0}", DateTime.Now);

        }

        public Task StopAsync(CancellationToken cancellationToken)
        {
            _logger.LogInformation("Service is stopping.");

            _timer?.Change(Timeout.Infinite, 0);

            return Task.CompletedTask;
        }

        public void Dispose()
        {
            _timer?.Dispose();
        }


    }
}

Everything runs fine locally but when I deploy it, the service doesn't seem to start, no log files are generated and I can't seem to find any errors reported.

Any ideas?

Osmious answered 27/4, 2022 at 19:2 Comment(7)
How are you hosting this when deployed? E.g. IIS on a VM, Azure App Service, Window Service? Do you actually have API endpoints within the app as well?Shiftless
I am hosting in IIS, no I don't have an endpoint at this time. I was just trying to keep it as minimal as possible so I just have the hosted service. I do have another project that does have endpoints with the hosted service and I do notice that when I call the endpoint I see log entries that show that the service did start but it does not continue to run.Osmious
Under IIS, nothing in your app will run until the first request arrives. This includes your IHostedService, so if you don't have an endpoint to hit, it will never start. Once it's started you're then subject to the IIS App Pool's idle timeout (and whatever other IIS configuration applies).Shiftless
Yes, after lots of testing and going back to my .NET Core app, I realized that until your app/site is called then the service won't start. I was under the impression that once your app starts in IIS then this would trigger the hosted service but I guess it's not until your app gets a request.. Thank you so much for your response. I think I will get rid of the hosted service and just make it an endpoint and have a scheduled task call it.Osmious
IHostedService is quite generic; you can host in other ways as well; e.g. a Windows Service (pretty easily).Shiftless
Just wanted to post an update. I decided to split out my functionality and create a windows service which will call my API. This structure gave me the control I needed over the hosted service. The real issue is that the API doesn't start until a request is made and I needed something to start as soon as it was deployed. Thank you all for your help.Osmious
I am using .Net6.0 on Linux and Docker and I suspect I have the same problem - hosted service doesn't get started when deployed. I do have endpoint that gets hit every 5 seconds from kubernetes (api/health)Patty
R
4

The problem is that while IHostedService start when the site starts when does IIS start the site? By default, it doesn't until a request is made. If your site goes idle for a while IIS will kill the thread as well.

IIS has a feature you can enable called "Application Initialization". You then need to configure the Application Pool to always start (startMode="AlwaysRunning").

Lastly, you configure the site's application to preload which sends a fake HTTP request to the site on startup (preloadEnabled="true").

The docs for this are here: https://learn.microsoft.com/en-us/iis/get-started/whats-new-in-iis-8/iis-80-application-initialization

Rossie answered 17/1, 2023 at 19:13 Comment(1)
Any idea what setting to use in kestrel?Leola
G
1

We had the same problem and resolved it by following IIS settings:

  • Under "Page" Advanced Settings: "Preload Enabled" must be set to "true" (this forces app to run after every app pool recycle and on startup)
  • Under "App Pool" Advanced settings: "Start mode" must be set to "AlwaysRunning"
  • "Page" in IIS must have http allowed (since preload enabled works only on http and not via https)
Griffon answered 6/10, 2022 at 8:0 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.