【发布时间】:2021-01-14 16:56:51
【问题描述】:
以下是 Visual Studio 2019 中用于创建工作器服务的基本模板的代码。将此服务部署到 linux 容器时,当容器被请求停止时,ExecuteAsync 方法中的代码 CancellationToken 永远不会被取消。以各种方式停止容器后,我看到另外 10 个“Worker running at”日志条目,然后容器停止。这与我读到的一致:docker 发送一个 SIGTERM 命令,等待 10 秒,然后如果容器没有自行停止,则发送一个 SIGKILL 命令。
我正在寻找的是当容器被请求停止时 CancellationToken 应该如何设置为 Canceled 的答案?我的最终目标是确保在容器突然停止之前完成或回滚任何正在运行的事务以及其他此类清理工作。
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((hostContext, services) =>
{
services.AddHostedService<Worker>();
});
}
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
public Worker(ILogger<Worker> logger)
{
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
_logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
await Task.Delay(1000, stoppingToken);
}
}
}
我已尝试订阅 AppDomain.CurrentDomain.ProcessExit 事件,因为这似乎是网络上大多数人所说的(没有任何示例),但是我实现它的方式不起作用或者只是这不是正确的做法。从 VS2019 运行容器,事件处理程序的代码永远不会执行。以下是我尝试实现的方法:
public static void Main(string[] args)
{
using (CancellationTokenSource cts = new CancellationTokenSource())
{
AppDomain.CurrentDomain.ProcessExit += (sender, eventArgs) =>
{
cts.Cancel();
};
var host = CreateHostBuilder(args).Build();
Task task;
task = host.RunAsync(cts.Token);
task.Wait(cts.Token);
}
}
更新:
我猜这个问题与模板构建 docker 文件和启动容器的方式有关。 SIGTERM 被发送到 PID 1 并查看容器上 ps -ef 的结果,tail 的调用是 PID 1 而不是 .netcore 解决方案中的任何内容。我想下一个问题是如何解决这个问题?
更新(请求的 Docker 文件) docker 文件是 Visual Studio 为此模板创建的默认 docker 文件。
#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/runtime:5.0 AS base
WORKDIR /app
FROM mcr.microsoft.com/dotnet/sdk:5.0 AS build
WORKDIR /src
COPY ["WorkerService2/WorkerService2.csproj", "WorkerService2/"]
RUN dotnet restore "WorkerService2/WorkerService2.csproj"
COPY . .
WORKDIR "/src/WorkerService2"
RUN dotnet build "WorkerService2.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "WorkerService2.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "WorkerService2.dll"]
【问题讨论】:
-
您是否尝试实现
IDisposable,如docs 中所述? -
似乎 Dispose 方法对于围绕完成或回滚进行中事务的业务逻辑来说是错误的位置,因为您的服务已被要求停止。有一个 CancellationToken 传递给 ExecuteAsync 方法,应该有办法实际使用它。
-
尝试将
ConfigureAwait(false)添加到Task.Delay -
Task.Delay(1000, stoppingToken).ConfigureAwait(false);没有改变我能看到的任何东西。当我请求容器停止时,我仍然会再获得 10 秒的“Worker Running ...”日志条目,然后服务停止,而没有来自 StopAsync 的任何日志条目,或者 Worker 中的 Dispose 覆盖。 -
I'm guessing the problem has something to do with the way template is building你的Dockerfile是什么?
标签: c# .net-core containers