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

ASP.NET Core应用作为Linux服务无法正常停止的问题求助

问题描述

在Linux Ubuntu机器上创建了一个简单的ASP.NET Core Web应用并作为systemd服务运行:

  • systemctl start webapp.service能正常启动应用
  • 执行systemctl stop webapp.service时会长时间卡住,最终超时
  • 查看停止时的stdout,程序卡在await this.app.StopAsync()语句,未输出预期的"Stopped"日志

停止服务时的输出

Nov 21 08:59:57 LinxServer systemd[1]: Stopping WebApp...
Nov 21 08:59:57 LinxServer dotnet[6746]: info: Microsoft.Hosting.Lifetime[0]
Nov 21 08:59:57 LinxServer dotnet[6746]:       Application is shutting down...

直接运行时的正常关闭输出

用dotnet WebApplication1.dll直接启动,按下Ctrl+C可正常关闭:

info: Microsoft.Hosting.Lifetime[0]
      Application is shutting down...
Stopping...
Stopped.

应用代码

await Host
    .CreateDefaultBuilder(args)
    .ConfigureServices((hostContext, services) =>
    {
        services.AddHostedService<Application>();
    })
    .UseSystemd()
    .Build()
    .RunAsync();


class Application : IHostedService
{
    private WebApplication app;

    public async Task StartAsync(CancellationToken cancellationToken)
    {
        Console.WriteLine("Starting...");
        var builder = WebApplication.CreateBuilder();
        builder.WebHost.UseKestrel(options => options.ListenAnyIP(8010));
        this.app = builder.Build();

        this.app.MapGet("/", () => "Hello World!");

        await this.app.StartAsync();
        Console.WriteLine("Started.");
    }

    public async Task StopAsync(CancellationToken cancellationToken)
    {
        Console.WriteLine("Stopping...");
        await this.app.StopAsync();
        Console.WriteLine("Stopped.");
    }
}

Systemd服务单元配置

[Unit]
Description=WebApp

[Service]
Type=notify
ExecStart=dotnet WebApplication1.dll
WorkingDirectory=/home/myuser/webapp
KillSignal=SIGINT
User=myuser
Environment=ASPNETCORE_ENVIRONMENT=Production

[Install]
WantedBy=multi-user.target

已尝试的无效修改

改用同步启动方式,问题依旧:

using var host = Host
    .CreateDefaultBuilder(args)
    .ConfigureServices((hostContext, services) =>
    {
        services.AddHostedService<Application>();
    })
    .UseSystemd()
    .Build();
host.Start();
host.WaitForShutdown();

解决思路

1. 修复嵌套主机的生命周期冲突

你在IHostedService内部手动创建并启动了独立的WebApplication实例,导致两个主机生命周期管理流程冲突:外部Host发起停止信号时,内部的WebApplication无法正确接收取消信号,最终卡住StopAsync。

修复代码:直接将Web服务集成到主Host中,不要嵌套创建:

var builder = WebApplication.CreateBuilder(args);
// 将systemd集成直接绑定到WebHost
builder.WebHost.UseKestrel(options => options.ListenAnyIP(8010))
               .UseSystemd();

builder.Services.AddHostedService<Application>();

var app = builder.Build();
app.MapGet("/", () => "Hello World!");

await app.RunAsync();

class Application : IHostedService
{
    public async Task StartAsync(CancellationToken cancellationToken)
    {
        Console.WriteLine("Starting...");
        // 无需手动启动WebApplication,app.RunAsync会统一管理
        Console.WriteLine("Started.");
    }

    public async Task StopAsync(CancellationToken cancellationToken)
    {
        Console.WriteLine("Stopping...");
        // 无需手动调用StopAsync,主Host会处理Web服务的停止
        await Task.CompletedTask;
        Console.WriteLine("Stopped.");
    }
}

2. 为StopAsync添加取消令牌支持

当前StopAsync没有使用传入的cancellationToken,如果app.StopAsync()卡住,无法响应外部的超时取消信号,修改后:

public async Task StopAsync(CancellationToken cancellationToken)
{
    Console.WriteLine("Stopping...");
    // 将取消令牌传入StopAsync,确保能响应终止信号
    await this.app.StopAsync(cancellationToken);
    Console.WriteLine("Stopped.");
}

3. 调整Systemd服务配置

  • 将Type=notify改为Type=simple:嵌套Host场景下,notify信号传递容易出现异常
  • 添加TimeoutStopSec=10:明确停止超时时间,避免无限等待
  • 设置KillMode=mixed:让systemd先发送终止信号,超时后强制杀死进程

修改后的配置:

[Service]
Type=simple
ExecStart=dotnet WebApplication1.dll
WorkingDirectory=/home/myuser/webapp
KillSignal=SIGINT
TimeoutStopSec=10
KillMode=mixed
User=myuser
Environment=ASPNETCORE_ENVIRONMENT=Production

4. 配置Kestrel的关闭超时

如果存在未关闭的长连接,Kestrel会等待连接超时才停止,可手动设置关闭超时:

builder.WebHost.UseKestrel(options =>
{
    options.ListenAnyIP(8010);
    // 设置Kestrel关闭时的等待超时
    options.Limits.ShutdownTimeout = TimeSpan.FromSeconds(5);
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 06:01:01