ヒント
このコンテンツは、.NET Docs で入手できる、またはオフラインで読み取ることができる無料のダウンロード可能な PDF として入手できる、コンテナー化された .NET アプリケーションの電子ブックである .NET マイクロサービス アーキテクチャからの抜粋です。
バックグラウンド タスクとスケジュールされたジョブは、マイクロサービス アーキテクチャ パターンに従っているかどうかにかかわらず、任意のアプリケーションで使用する必要がある場合があります。 マイクロサービス アーキテクチャを使用する場合の違いは、ホスティング用の別のプロセス/コンテナーにバックグラウンド タスクを実装して、ニーズに基づいてスケールダウンまたはスケールアップできることです。
一般的な観点から、.NET では、ホスト/アプリケーション/マイクロサービス内でホストするサービス/ロジックであるため、これらの種類のタスクを Hosted Services と呼びます。 この場合、ホストされるサービスは、単にバックグラウンド タスク ロジックを持つクラスを意味します。
.NET Core 2.0 以降、フレームワークには、ホストされたサービスを簡単に実装するのに役立つ IHostedService という名前の新しいインターフェイスが用意されています。 基本的な考え方は、イメージ 6-26 に示すように、Web ホストまたはホストの実行中にバックグラウンドで実行される複数のバックグラウンド タスク (ホステッド サービス) を登録できることです。
図 6-26 WebHost と Host で IHostedService を使用した場合の比較
ASP.NET Core 1.x および 2.x では、Web アプリのバックグラウンド プロセスの IWebHost
がサポートされます。 .NET Core 2.1 以降のバージョンでは、プレーンコンソール アプリを使用したバックグラウンド プロセスの IHost
がサポートされています。
WebHost
とHost
の違いに注意してください。
ASP.NET Core 2.0 の WebHost
( IWebHost
を実装する基本クラス) は、MVC Web アプリや Web API サービスを実装する場合など、プロセスに HTTP サーバー機能を提供するために使用するインフラストラクチャ成果物です。 ASP.NET Core のすべての新しいインフラストラクチャの優れた機能が提供されるため、依存関係の挿入、要求パイプラインへのミドルウェアの挿入などを使用できます。
WebHost
では、バックグラウンド タスクにこれらのまったく同じIHostedServices
が使用されます。
.NET Core 2.1 では、 Host
( IHost
を実装する基本クラス) が導入されました。 基本的に、 Host
を使用すると、 WebHost
(依存関係の挿入、ホストされたサービスなど) と同様のインフラストラクチャを使用できますが、この場合は、MVC、Web API、または HTTP サーバーの機能に関係なく、単純で軽量なプロセスをホストとして持つ必要があります。
そのため、ホストされたサービスを処理する IHost
を使用して特殊なホスト プロセスを作成することも、 IHostedServices
をホストするためだけに作成されたマイクロサービスを作成することも、既存の ASP.NET Core WebHost
(既存の ASP.NET Core Web API や MVC アプリなど) を拡張することもできます。
各アプローチには、ビジネスとスケーラビリティのニーズに応じて長所と短所があります。 基本的に、バックグラウンド タスクが HTTP (IWebHost
) と関係がない場合は、 IHost
を使用する必要があります。
WebHost またはホストにホステッド サービスを登録する
IHostedService
やWebHost
では使用が非常に似ているため、Host
インターフェイスをさらに詳しく見ていきましょう。
SignalR は、ホストされたサービスを使用する成果物の例の 1 つですが、次のような簡単な場合にも使用できます。
- 変更を探しているデータベースをポーリングするバックグラウンド タスク。
- 一部のキャッシュを定期的に更新するスケジュールされたタスク。
- バックグラウンド スレッドでタスクを実行できるようにする QueueBackgroundWorkItem の実装。
-
ILogger
などの一般的なサービスを共有しながら、Web アプリのバックグラウンドでメッセージ キューからのメッセージを処理する。 -
Task.Run()
で開始されたバックグラウンド タスク。
基本的には、これらのアクションのいずれかを、 IHostedService
を実装するバックグラウンド タスクにオフロードできます。
IHostedServices
またはWebHost
に 1 つまたは複数のHost
を追加する方法は、ASP.NET Core AddHostedService (または .NET Core 2.1 以降のWebHost
) でHost
拡張メソッドを使用して登録することです。 基本的には、Program.csでアプリケーションの起動時にホストされる サービスを登録する必要があります。
//Other DI registrations;
// Register Hosted Services
builder.Services.AddHostedService<GracePeriodManagerService>();
builder.Services.AddHostedService<MyHostedServiceB>();
builder.Services.AddHostedService<MyHostedServiceC>();
//...
このコードでは、 GracePeriodManagerService
ホステッド サービスは eShopOnContainers の Ordering ビジネス マイクロサービスの実際のコードですが、他の 2 つは追加の 2 つのサンプルにすぎません。
IHostedService
バックグラウンド タスクの実行は、アプリケーションの有効期間 (ホストまたはマイクロサービス) と調整されます。 アプリケーションの起動時にタスクを登録すると、アプリケーションのシャットダウン時に適切なアクションやクリーンアップを実行できます。
IHostedService
を使用しないと、いつでもバックグラウンド スレッドを開始してタスクを実行できます。 正常なクリーンアップ アクションを実行する機会がないままスレッドが単に強制終了される場合、差はちょうどアプリのシャットダウン時間になります。
IHostedService インターフェイス
IHostedService
を登録すると、.NET は、アプリケーションの開始時と停止時に、StartAsync()
の種類のStopAsync()
メソッドとIHostedService
メソッドをそれぞれ呼び出します。 詳細については、 IHostedService インターフェイスを参照してください。
ご想像のとおり、IHostedService の複数の実装を作成し、前に示したように各実装を Program.csに登録できます。 これらのホストされているすべてのサービスは、アプリケーション/マイクロサービスと共に開始および停止されます。
開発者は、 StopAsync()
メソッドがホストによってトリガーされたときに、サービスの停止アクションを処理する責任があります。
BackgroundService 基底クラスから派生するカスタム ホステッド サービス クラスを使用した IHostedService の実装
.NET Core 2.0 以降の使用時に行う必要がある場合と同様に、カスタムホステッド サービス クラスを最初から作成し、 IHostedService
を実装できます。
ただし、ほとんどのバックグラウンド タスクにはキャンセル トークンの管理やその他の一般的な操作に関して同様のニーズがあるため、 BackgroundService
という名前で派生できる便利な抽象基本クラスがあります (.NET Core 2.1 以降で使用できます)。
そのクラスは、バックグラウンド タスクを設定するために必要な主な作業を提供します。
次のコードは、.NET に実装されている抽象 BackgroundService 基底クラスです。
// Copyright (c) .NET Foundation. Licensed under the Apache License, Version 2.0.
/// <summary>
/// Base class for implementing a long running <see cref="IHostedService"/>.
/// </summary>
public abstract class BackgroundService : IHostedService, IDisposable
{
private Task _executingTask;
private readonly CancellationTokenSource _stoppingCts =
new CancellationTokenSource();
protected abstract Task ExecuteAsync(CancellationToken stoppingToken);
public virtual Task StartAsync(CancellationToken cancellationToken)
{
// Store the task we're executing
_executingTask = ExecuteAsync(_stoppingCts.Token);
// If the task is completed then return it,
// this will bubble cancellation and failure to the caller
if (_executingTask.IsCompleted)
{
return _executingTask;
}
// Otherwise it's running
return Task.CompletedTask;
}
public virtual async Task StopAsync(CancellationToken cancellationToken)
{
// Stop called without start
if (_executingTask == null)
{
return;
}
try
{
// Signal cancellation to the executing method
_stoppingCts.Cancel();
}
finally
{
// Wait until the task completes or the stop token triggers
await Task.WhenAny(_executingTask, Task.Delay(Timeout.Infinite,
cancellationToken));
}
}
public virtual void Dispose()
{
_stoppingCts.Cancel();
}
}
前の抽象基底クラスから派生する場合、その継承された実装により、必要に応じてデータベースをポーリングし、統合イベントを Event Bus に発行する eShopOnContainers の次の簡略化されたコードのように、独自のカスタム ホステッド サービス クラスに ExecuteAsync()
メソッドを実装するだけで済みます。
public class GracePeriodManagerService : BackgroundService
{
private readonly ILogger<GracePeriodManagerService> _logger;
private readonly OrderingBackgroundSettings _settings;
private readonly IEventBus _eventBus;
public GracePeriodManagerService(IOptions<OrderingBackgroundSettings> settings,
IEventBus eventBus,
ILogger<GracePeriodManagerService> logger)
{
// Constructor's parameters validations...
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogDebug($"GracePeriodManagerService is starting.");
stoppingToken.Register(() =>
_logger.LogDebug($" GracePeriod background task is stopping."));
while (!stoppingToken.IsCancellationRequested)
{
_logger.LogDebug($"GracePeriod task doing background work.");
// This eShopOnContainers method is querying a database table
// and publishing events into the Event Bus (RabbitMQ / ServiceBus)
CheckConfirmedGracePeriodOrders();
try {
await Task.Delay(_settings.CheckUpdateTime, stoppingToken);
}
catch (TaskCanceledException exception) {
_logger.LogCritical(exception, "TaskCanceledException Error", exception.Message);
}
}
_logger.LogDebug($"GracePeriod background task is stopping.");
}
.../...
}
eShopOnContainers のこの特定のケースでは、特定の状態の注文を検索するデータベース テーブルのクエリを実行するアプリケーション メソッドが実行され、変更を適用するときに、イベント バスを介して統合イベントが発行されます (その下には RabbitMQ または Azure Service Bus を使用できます)。
もちろん、代わりに他のビジネス バックグラウンド タスクを実行することもできます。
既定では、キャンセル トークンは 5 秒のタイムアウトで設定されますが、WebHost
のUseShutdownTimeout
拡張機能を使用してIWebHostBuilder
をビルドするときにその値を変更できます。 つまり、サービスは 5 秒以内に取り消されることが予想されます。そうしないと、より突然強制終了されます。
次のコードは、その時間を 10 秒に変更します。
WebHost.CreateDefaultBuilder(args)
.UseShutdownTimeout(TimeSpan.FromSeconds(10))
...
概要クラス図
次の図は、IHostedServices を実装するときに関係するクラスとインターフェイスの視覚的な概要を示しています。
図 6-27 IHostedService に関連する複数のクラスとインターフェイスを示すクラス図
クラス図: IWebHost と IHost は、IHostedService を実装する BackgroundService を継承する多くのサービスをホストできます。
デプロイに関する考慮事項と注意事項
ASP.NET Core WebHost
または .NET Host
をデプロイする方法が最終的なソリューションに影響する可能性があることに注意してください。 たとえば、IIS または通常の Azure App Service に WebHost
をデプロイする場合、アプリ プールのリサイクルのためにホストをシャットダウンできます。 ただし、Kubernetes などのオーケストレーターにコンテナーとしてホストをデプロイする場合は、ホストのライブ インスタンスの確実な数を制御できます。 さらに、特にこれらのシナリオに対して行われたクラウドの他のアプローチ (Azure Functions など) を検討することもできます。 最後に、サービスを常に実行し、Windows Server に展開する必要がある場合は、Windows サービスを使用できます。
ただし、アプリ プールにデプロイされた WebHost
の場合でも、アプリケーションのメモリ内キャッシュのリポジトリやフラッシュなどのシナリオは引き続き適用されます。
IHostedService
インターフェイスは、ASP.NET Core Web アプリケーション (.NET Core 2.0 以降のバージョン) または任意のプロセス/ホスト (.NET Core 2.1 IHost
以降) でバックグラウンド タスクを開始する便利な方法を提供します。 その主な利点は、ホスト自体がシャットダウンしているときにバックグラウンド タスクのコードをクリーンアップするグレースフル キャンセルを利用できる機会です。
その他のリソース
ASP.NET Core/Standard 2.0 でスケジュールされたタスクをビルドする
https://blog.maartenballiauw.be/post/2017/08/01/building-a-scheduled-cache-updater-in-aspnet-core-2.htmlASP.NET Core 2.0 での IHostedService の実装
https://www.stevejgordon.co.uk/asp-net-core-2-ihostedserviceASP.NET Core 2.1 を使用した GenericHost サンプル
https://github.com/aspnet/Hosting/tree/release/2.1/samples/GenericHostSample
.NET