aremmell
aremmell

Reputation: 1362

How can I know when Kestrel has started listening?

I need to notify systemd that my service has started up successfully, and a task it needs to run after startup requires that the server is already listening on the target Unix domain socket.

I am using IWebHost::Run to start the server, and that is a blocking call. Additionally, I am unable to find any obvious way to set a delegate or callback event for successful initialization.

Anyone?

Upvotes: 11

Views: 4075

Answers (5)

Cee McSharpface
Cee McSharpface

Reputation: 8725

A working sample from .NET Core 6:

In your Program.cs, you will have the following (with more or less embellishment, depending on the middleware):

var builder = WebApplication.CreateBuilder(args);

/* ... kestrel configuration, middleware setup ... */

var app = builder.Build();

app.Services.GetService<IHostApplicationLifetime>()!.ApplicationStarted.Register(() =>
{
    /* any code you put here will execute
     * after the host has started listening */
    Console.WriteLine("Kestrel has started listening");
});

/* this is a blocking call.
 * the start event occurs from "in there" */
app.Run();

Working principle: The event handler with the Console.WriteLine call will be called after app.Run() is already blocking the main thread, at the exact moment when the server is first ready and will accept requests.

Upvotes: 1

Alper Ebicoglu
Alper Ebicoglu

Reputation: 9634

This is what I did to overcome the issue.

1- I registered ApplicationStopped event. So that it brute force terminates the app by calling Kill() method of the current process.

public void Configure(IHostApplicationLifetime appLifetime) {
 appLifetime.ApplicationStarted.Register(() => {
  Console.WriteLine("Press Ctrl+C to shut down.");
 });

 appLifetime.ApplicationStopped.Register(() => {
  Console.WriteLine("Terminating application...");
  System.Diagnostics.Process.GetCurrentProcess().Kill();
 });
}

See IHostApplicationLifetime docs


2- Don't forget to use the UseConsoleLifetime() while building the host.

Host.CreateDefaultBuilder(args).UseConsoleLifetime(opts => opts.SuppressStatusMessages = true);

See useconsolelifetime docs

Upvotes: 0

aremmell
aremmell

Reputation: 1362

This is what I ended up going with, for now. Seems to be working fine:

host.Start();

Log.Information("Press Ctrl+C to shut down...");
Console.CancelKeyPress += OnConsoleCancelKeyPress;

var waitHandles = new WaitHandle[] {
    CancelTokenSource.Token.WaitHandle
};

WaitHandle.WaitAll(waitHandles);
Log.Information("Shutting down...");

Then, in the Ctrl+C event handler:

private static void OnConsoleCancelKeyPress(object sender, ConsoleCancelEventArgs e)
{
    Log.Debug("Got Ctrl+C from console.");
    CancelTokenSource.Cancel();
}

Upvotes: 1

Set
Set

Reputation: 49789

You may use Microsoft.AspNetCore.Hosting.IApplicationLifetime:

/// <summary>
/// Triggered when the application host has fully started and is about to wait
/// for a graceful shutdown.
/// </summary>
CancellationToken ApplicationStarted { get; }

Look into this SO post for the configuration example.

Upvotes: 4

Gerardo Grignoli
Gerardo Grignoli

Reputation: 15207

  • On .Net Core 1.x it is safe to just run IWebHost.Start() and assume that the server is initialized afterwards (instead of Run() that blocks the thread). Check the source.

    var host = new WebHostBuilder()
        .UseKestrel()
        (...)
        .Build();
    
    host.Start();
    
  • If you are using .NET Core 2.0 Preview 1 (or later), the source is different, the synchronous method is not available anymore so you should await IWebHost.StartAsync() and assume everything is ready afterwards.

Upvotes: 3

Related Questions