文章目錄
ASP.NET Core 避免工作執行到一半強制被關閉
最近同事反應部份 application 在不同環境間,有 stop log 沒有正常紀錄下來的情況,雖然只是 log,但很有可能就連原本預期執行完畢的程式碼都沒有順利完成,這樣一來影響範圍可能很大,為了避免情況持續惡化,模擬目前團隊的程式碼結構,找出可能的解決方案,順手紀錄一下
基本環境說明
- macOS Big Sur 11.2.1
- .NET Core SDK 3.1.406
ASP.NET Core WebAPI 預設專案範本
微調 program.cs 中,generic host 的用法:在執行主要程式的前後加上 log
public static void Main(string[] args) { var host=CreateHostBuilder(args).Build(); Console.WriteLine("application start"); host.Run(); Console.WriteLine("application stop"); }
模擬較長的 shutdown 處理
使用 HostedService
建立 HostedService
SlowHostedService.cs
public class SlowHostedService: IHostedService { private readonly ILogger<SlowHostedService> _logger; public SlowHostedService(ILogger<SlowHostedService> logger) { _logger = logger; } public Task StartAsync(CancellationToken cancellationToken) { _logger.LogInformation("SlowHostedService started"); return Task.CompletedTask; } public async Task StopAsync(CancellationToken cancellationToken) { _logger.LogInformation($"SlowHostedService stopping..."); await Task.Delay(10_000, cancellationToken); _logger.LogInformation("SlowHostedService stopped"); } }
註冊 HostedService
Startup.cs
public void ConfigureServices(IServiceCollection services) { //原生註冊,如: services.AddControllers(); //將執行關閉較耗時的 service 最後註冊,中止時會優先執行 services.AddHostedService<SlowHostedService>(); }
使用 一般 class
這邊偷懶:延用上面的 HostedService,把其註冊成 singleton service ,用來模擬需要較長時間處理關閉作業的動作
public void ConfigureServices(IServiceCollection services) { services.AddControllers(); services.AddSingleton<SlowHostedService>(); }
設定方式
使用 HostedService
修改 HostService
ShutdownTimeout
時間 (預設為5秒
HostOptions.cs)public void ConfigureServices(IServiceCollection services) { //原生註冊,如: services.AddControllers(); //將執行關閉較耗時的 service 最後註冊,中止時會優先執行 services.AddHostedService<SlowHostedService>(); services.Configure<HostOptions>(opts => opts.ShutdownTimeout = TimeSpan.FromSeconds(15)); }
修改前
HostedService 未執行到 stopped 就拋出錯誤了
修改後
可以正常執行至 Program.cs 的 “application stop”
使用 一般 class
透過
IHostApplicationLifetime
註冊ApplicationStopped
事件來進行關閉服務前的處理;這邊也可以直接指定等待時間;簡言之,就是會等待life.ApplicationStopped.Register
區塊都完成後才會往下執行host.Run()
的中止動作public static void Main(string[] args) { var host=CreateHostBuilder(args).Build(); Console.WriteLine("application start"); var life = host.Services.GetRequiredService<IHostApplicationLifetime>(); life.ApplicationStopped.Register(() => { Console.WriteLine("Application is shut down"); // long time works var _service = host.Services.GetRequiredService<SlowHostedService>(); _service.StopAsync(new CancellationToken()).GetAwaiter().GetResult(); //也可以直接指定等待時間 //Thread.Sleep(10_1000); }); host.Run(); Console.WriteLine("application stop"); }
修改前
有執行到正常執行至 Program.cs 的 “application stop”,但沒有 service stopped 的訊息,說明並未等待至 service 執行完畢
修改後
有執行到正常執行至 Program.cs 的 “application stop”,也有 service stopped 的訊息
心得
該用哪種方式?
如果是透過 HostedService 來處理相關作業,選
放寬 HostService ShutdownTimeout 時間
;如果是其他方式才用IHostApplicationLifetime ApplicationStopped 事件
;程式碼會比較簡潔清爽為什麼是 IHostApplicationLifetime
ApplicationStopped 事件
而非ApplicationStopping
我一開始是用
ApplicationStopping
後來發現沒有效果,錯誤如下ASPNETCORE_SHUTDOWNTIMEOUTSECONDS
環境變數用法可能已失效這是在 Lybecker/k8s-friendly-aspnetcore 看到的,我實測下並無法成功避免錯誤,據官方文件 ASP.NET Core Web Host 與 HostingAbstractionsWebHostBuilderExtensions.UseShutdownTimeout(IWebHostBuilder, TimeSpan) Method 所示,很可能只適用於 ASP.NET Core Web Host
參考資訊
文章作者 Yowko Tsai
上次更新 2021-02-27
授權合約
本部落格 (Yowko's Notes) 所有的文章內容(包含圖片),任何轉載行為,必須通知並獲本部落格作者 (Yowko Tsai) 的同意始得轉載,且轉載皆須註明出處與作者。
Yowko's Notes 由 Yowko Tsai 製作,以創用CC 姓名標示-非商業性-相同方式分享 3.0 台灣 授權條款 釋出。