I am trying to get info on how asp net core handles stopping IHostedService.
On my machine, if I have an app executing with an IHostedService, and if I send to it SIGTERM, I observe that StopAsync() method is called as expected, with cancellation tokens changed etc, so my code can gracefully handle the SIGTERM. However, if I try on another environment, it seems that on SIGTERM the IHostedService is brutally stopped, without calling the StopAsync() code. So I have a feeling this depends on the version and potentially env (macos vs linux)
Is there a documented behavior for what Asp .net core will consider "graceful" shutdown demand, depending on the asp .net core version etc ? I couldn't find anything. Ideally it should be documented what's the behavior of IHostedService when SIGTERM is sent
CodePudding user response:
Short Version
Use RunConsoleAsync() instead of Run
or RunAsync
.
Another option is to call UseConsoleLifetime() before calling one of the other methods. RunConsoleAsync()
does this internally
Since .NET 6, graceful shutdown isn't used when Environment.Exit()
called from the application itself.
Explanation
StopAsync
will be called when the Host shuts down. Normally, SIGTERM or SIGKILL aren't monitored unless the host runs with a Console lifetime. This is a feature of the Generic Host that was added in .NET Core 3 and replaced the older WebHost. The ASP.NET Core docs were updated and new docs added in the generic .NET sections
Unfortunately, this means that Host and middleware behavior is duplicated and even spread across different sections.
The shutdown behavior, especially regarding SIGTERM, MacOS, Linux etc is described in the Host Shutdown section. If ConsoleLifetime
is used, SIGTERM, SIGKILL etc are monitored. That section doesn't say how to do this though.
This is documented in the .NET Generic Host for ASP.NET Core page, in the RunConsoleAsync section :
RunConsoleAsync enables console support, builds and starts the host, and waits for Ctrl C/SIGINT (Windows), ⌘ C (macOS), or SIGTERM to shut down.
When Console Lifetime is used, SIGINT, SIGTERM, SIGKILL, will result in a graceful shutdown :
If ConsoleLifetime is used, it listens for the following signals and attempts to stop the host gracefully.
- SIGINT (or CTRL C).
- SIGQUIT (or CTRL BREAK on Windows, CTRL \ on Unix).
- SIGTERM (sent by other apps, such as docker stop).
Environment.Exit
though isn't a graceful shutdown. This caused problems before .NET 6 :
Before .NET 6, there wasn't a way for .NET code to gracefully handle SIGTERM. To work around this limitation, ConsoleLifetime would subscribe to System.AppDomain.ProcessExit. When ProcessExit was raised, ConsoleLifetime would signal the host to stop and block the ProcessExit thread, waiting for the host to stop.
This caused other issues because SIGTERM wasn't the only way ProcessExit was raised. It is also raised by code in the application calling Environment.Exit. Environment.Exit isn't a graceful way of shutting down a process in the Microsoft.Extensions.Hosting app model.
This changed in .NET 6:
In .NET 6, POSIX signals are supported and handled. This allows for ConsoleLifetime to handle SIGTERM gracefully, and no longer get involved when Environment.Exit is invoked.
And
For .NET 6 , ConsoleLifetime no longer has logic to handle scenario Environment.Exit. Apps that call Environment.Exit and need to perform clean-up logic can subscribe to ProcessExit themselves. Hosting will no longer attempt to gracefully stop the host in this scenario.