【发布时间】:2020-10-20 20:57:26
【问题描述】:
我正在构建一个 .NET Core 3.1 应用程序,它将在 Docker 容器中运行 BackgroundService。虽然我已经为 BackgroundService 实现了启动和关闭任务,并且在通过 SIGTERM 触发时服务肯定会关闭,但我发现 await host.RunAsync() 调用永远不会完成 - 这意味着我的 Main() 块中的剩余代码没有被执行。
我是否遗漏了什么,或者我不应该期望在后台服务完成停止后调用RunAsync() 来返回控制权?
(更新了我能想到的最简单的重现...)
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace BackgroundServiceTest
{
class Program
{
static async Task Main(string[] args)
{
Console.WriteLine("Main: starting");
try
{
using var host = CreateHostBuilder(args).Build();
Console.WriteLine("Main: Waiting for RunAsync to complete");
await host.RunAsync();
Console.WriteLine("Main: RunAsync has completed");
}
finally
{
Console.WriteLine("Main: stopping");
}
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseConsoleLifetime()
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<Worker>();
// give the service 120 seconds to shut down gracefully before whacking it forcefully
services.Configure<HostOptions>(options => options.ShutdownTimeout = TimeSpan.FromSeconds(120));
});
}
class Worker : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
Console.WriteLine("Worker: ExecuteAsync called...");
try
{
while (!stoppingToken.IsCancellationRequested)
{
await Task.Delay(TimeSpan.FromSeconds(10), stoppingToken);
Console.WriteLine("Worker: ExecuteAsync is still running...");
}
}
catch (OperationCanceledException) // will get thrown if TaskDelay() gets cancelled by stoppingToken
{
Console.WriteLine("Worker: OperationCanceledException caught...");
}
finally
{
Console.WriteLine("Worker: ExecuteAsync is terminating...");
}
}
public override Task StartAsync(CancellationToken cancellationToken)
{
Console.WriteLine("Worker: StartAsync called...");
return base.StartAsync(cancellationToken);
}
public override async Task StopAsync(CancellationToken cancellationToken)
{
Console.WriteLine("Worker: StopAsync called...");
await base.StopAsync(cancellationToken);
}
public override void Dispose()
{
Console.WriteLine("Worker: Dispose called...");
base.Dispose();
}
}
}
Dockerfile:
#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging.
FROM mcr.microsoft.com/dotnet/core/runtime:3.1-buster-slim AS base
WORKDIR /app
FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build
WORKDIR /src
COPY ["BackgroundServiceTest.csproj", "./"]
RUN dotnet restore "BackgroundServiceTest.csproj"
COPY . .
WORKDIR "/src/"
RUN dotnet build "BackgroundServiceTest.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "BackgroundServiceTest.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "BackgroundServiceTest.dll"]
docker-compose.yml:
version: '3.4'
services:
backgroundservicetest:
image: ${DOCKER_REGISTRY-}backgroundservicetest
build:
context: .
dockerfile: Dockerfile
通过docker-compose up --build 运行它,然后在第二个窗口中运行docker stop -t 90 backgroundservicetest_backgroundservicetest_1
控制台输出显示 Worker 已关闭并被释放,但应用程序(显然)在 RunAsync() 返回之前终止。
Successfully built 3aa605d4798f
Successfully tagged backgroundservicetest:latest
Recreating backgroundservicetest_backgroundservicetest_1 ... done
Attaching to backgroundservicetest_backgroundservicetest_1
backgroundservicetest_1 | Main: starting
backgroundservicetest_1 | Main: Waiting for RunAsync to complete
backgroundservicetest_1 | Worker: StartAsync called...
backgroundservicetest_1 | Worker: ExecuteAsync called...
backgroundservicetest_1 | info: Microsoft.Hosting.Lifetime[0]
backgroundservicetest_1 | Application started. Press Ctrl+C to shut down.
backgroundservicetest_1 | info: Microsoft.Hosting.Lifetime[0]
backgroundservicetest_1 | Hosting environment: Production
backgroundservicetest_1 | info: Microsoft.Hosting.Lifetime[0]
backgroundservicetest_1 | Content root path: /app
backgroundservicetest_1 | Worker: ExecuteAsync is still running...
backgroundservicetest_1 | Worker: ExecuteAsync is still running...
backgroundservicetest_1 | info: Microsoft.Hosting.Lifetime[0]
backgroundservicetest_1 | Application is shutting down...
backgroundservicetest_1 | Worker: StopAsync called...
backgroundservicetest_1 | Worker: OperationCanceledException caught...
backgroundservicetest_1 | Worker: ExecuteAsync is terminating...
backgroundservicetest_1 | Worker: Dispose called...
backgroundservicetest_backgroundservicetest_1 exited with code 0
【问题讨论】:
-
你可以按
CTRL+C测试本地化吗? --- 如果它没有停止;很可能您的资源之一无法停止。如果您启动了前台线程,就会发生这种情况。 -
@Stefan BackgroundService 按预期完成关闭。但是
Main()中的剩余代码永远不会执行。 -
您能分享您的
Worker代码吗?也许在某个地方它不处理取消。只是为了实验,请尝试将其注释掉。
标签: c# .net-core-3.0