実行時間の長いサービスを作成するには、次のような多くの理由があります。
- CPU 負荷の高いデータの処理。
- バックグラウンドで作業項目をキューに登録する。
- スケジュールに基づいて時間ベースの操作を実行する。
バックグラウンド サービスの処理には通常、ユーザー インターフェイス (UI) は含まれませんが、UI はそれらの周囲に構築できます。 .NET Framework の初期の段階では、Windows 開発者はこれらの目的で Windows サービスを作成できました。 .NET では、BackgroundServiceの実装であるIHostedServiceを使用したり、独自の実装を行ったりできるようになりました。
.NET では、Windows に制限されなくなりました。 クロスプラットフォームのバックグラウンド サービスを開発できます。 ホストされるサービスは、ログ記録、構成、依存関係の挿入 (DI) の準備ができています。 これらはライブラリの拡張スイートの一部であり、 汎用ホストで動作するすべての .NET ワークロードの基礎となります。
重要
.NET SDK をインストールすると、 Microsoft.NET.Sdk.Worker
と worker テンプレートもインストールされます。 つまり、.NET SDK をインストールした後、dotnet new worker コマンドを使用して 新しい worker を作成できます。 Visual Studio を使用している場合、テンプレートはオプションの ASP.NET と Web 開発ワークロードがインストールされるまで非表示になります。
用語
多くの用語は誤って同義語として使用されます。 このセクションでは、この記事の意図をより明確にするために、これらの用語の一部を定義します。
- バックグラウンド サービス: BackgroundService の種類。
- ホステッド サービス: IHostedServiceの実装、または IHostedService 自体。
- 実行時間の長いサービス: 継続的に実行されるサービス。
- Windows サービス: Windows サービス インフラストラクチャ。もともとは .NET Framework 中心ですが、.NET 経由でアクセスできるようになりました。
- ワーカー サービス: Worker サービス テンプレート。
ワーカー サービス テンプレート
ワーカー サービス テンプレートは、.NET CLI と Visual Studio で使用できます。 詳細については、「 .NET CLI、 dotnet new worker
- テンプレート」を参照してください。 テンプレートは、 Program
クラスと Worker
クラスで構成されます。
using App.WorkerService;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddHostedService<Worker>();
IHost host = builder.Build();
host.Run();
上記の Program
クラス:
- HostApplicationBuilderを作成します。
-
AddHostedServiceを呼び出して、
Worker
をホステッド サービスとして登録します。 - ビルダーから IHost をビルドします。
- アプリを実行する
Run
インスタンスでhost
を呼び出します。
テンプレートの既定値
Worker テンプレートでは、サーバー ガベージ コレクション (GC) は既定では有効になりません。これは、その必要性を判断する際に役割を果たす多数の要因があるためです。 実行時間の長いサービスを必要とするすべてのシナリオでは、この既定値のパフォーマンスへの影響を考慮する必要があります。 サーバー GC を有効にするには、プロジェクト ファイルに ServerGarbageCollection
ノードを追加します。
<PropertyGroup>
<ServerGarbageCollection>true</ServerGarbageCollection>
</PropertyGroup>
トレードオフと考慮事項
有効化済み | 障害者 |
---|---|
効率的なメモリ管理: メモリ リークを防ぎ、リソース使用量を最適化するために、未使用のメモリを自動的に再利用します。 | リアルタイム パフォーマンスの向上: 待機時間の影響を受けやすいアプリケーションでガベージ コレクションによって発生する可能性のある一時停止または中断を回避します。 |
長期的な安定性: 長時間にわたってメモリを管理することで、実行時間の長いサービスで安定したパフォーマンスを維持するのに役立ちます。 | リソース効率: リソースが制約された環境で CPU とメモリのリソースを節約できます。 |
メンテナンスの削減: 手動によるメモリ管理の必要性を最小限に抑え、メンテナンスを簡素化します。 | 手動メモリ制御: 特殊なアプリケーションのメモリをきめ細かく制御できます。 |
予測可能な動作: 一貫性のある予測可能なアプリケーション動作に貢献します。 | 有効期間の短いプロセスに適しています:有効期間の短いプロセスまたは一時的なプロセスのガベージ コレクションのオーバーヘッドを最小限に抑えます。 |
パフォーマンスに関する考慮事項の詳細については、「 サーバー GC」を参照してください。 サーバー GC の構成の詳細については、 サーバー GC の構成例を参照してください。
労働者階級
Worker
に関しては、テンプレートは簡単な実装を提供します。
namespace App.WorkerService;
public sealed class Worker(ILogger<Worker> logger) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
await Task.Delay(1_000, stoppingToken);
}
}
}
上記の Worker
クラスは、BackgroundServiceを実装する IHostedService のサブクラスです。
BackgroundServiceはabstract class
であり、サブクラスがBackgroundService.ExecuteAsync(CancellationToken)を実装する必要があります。 テンプレート実装では、 ExecuteAsync
は 1 秒に 1 回ループし、プロセスがキャンセルを通知されるまで現在の日時をログに記録します。
プロジェクト ファイル
Worker テンプレートは、次のプロジェクト ファイル Sdk
に依存します。
<Project Sdk="Microsoft.NET.Sdk.Worker">
詳細については、「 .NET プロジェクト SDK」を参照してください。
NuGet パッケージ
Worker テンプレートに基づくアプリは、 Microsoft.NET.Sdk.Worker
SDK を使用し、 Microsoft.Extensions.Hosting パッケージへの明示的なパッケージ参照を持っています。
コンテナーとクラウドの適応性
最新の .NET ワークロードのほとんどでは、コンテナーは実行可能なオプションです。 Visual Studio で Worker テンプレートから実行時間の長いサービスを作成する場合は、 Docker サポートをオプトインできます。 これにより、.NET アプリをコンテナー化する Dockerfile が作成されます。 Dockerfile は、イメージをビルドするための一連の手順です。 .NET アプリの場合、 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/runtime:8.0@sha256:e6b552fd7a0302e4db30661b16537f7efcdc0b67790a47dbf67a5e798582d3a5 AS base
WORKDIR /app
FROM mcr.microsoft.com/dotnet/sdk:8.0@sha256:35792ea4ad1db051981f62b313f1be3b46b1f45cadbaa3c288cd0d3056eefb83 AS build
WORKDIR /src
COPY ["background-service/App.WorkerService.csproj", "background-service/"]
RUN dotnet restore "background-service/App.WorkerService.csproj"
COPY . .
WORKDIR "/src/background-service"
RUN dotnet build "App.WorkerService.csproj" -c Release -o /app/build
FROM build AS publish
RUN dotnet publish "App.WorkerService.csproj" -c Release -o /app/publish
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "App.WorkerService.dll"]
上記の Dockerfile の手順は次のとおりです。
-
mcr.microsoft.com/dotnet/runtime:8.0
の基本イメージをエイリアスbase
として設定します。 - 作業ディレクトリを /app に変更します。
-
build
イメージからmcr.microsoft.com/dotnet/sdk:8.0
エイリアスを設定します。 - 作業ディレクトリを /src に変更します。
- 内容のコピーと .NET アプリの発行:
- アプリは、
dotnet publish
コマンドを使用して発行されます。
- アプリは、
-
mcr.microsoft.com/dotnet/runtime:8.0
の .NET SDK イメージの再階層化 (base
エイリアス)。 - 発行されたビルド出力を /publish からコピーします。
-
dotnet App.BackgroundService.dll
に委任するエントリ ポイントの定義。
ヒント
mcr.microsoft.com
の MCR は "Microsoft Container Registry" を表し、公式の Docker ハブからの Microsoft のシンジケート コンテナー カタログです。
Microsoft シンジケート コンテナー カタログの記事には、追加の詳細が含まれています。
.NET Worker Service のデプロイ戦略として Docker を対象とする場合、プロジェクト ファイルにはいくつかの考慮事項があります。
<Project Sdk="Microsoft.NET.Sdk.Worker">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>true</ImplicitUsings>
<RootNamespace>App.WorkerService</RootNamespace>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.6" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.21.0" />
</ItemGroup>
</Project>
上記のプロジェクト ファイルでは、 <DockerDefaultTargetOS>
要素はターゲットとして Linux
を指定します。 Windows コンテナーをターゲットにするには、代わりに Windows
を使用します。
Microsoft.VisualStudio.Azure.Containers.Tools.Targets
NuGet パッケージは、Docker サポートがテンプレートから選択されると、パッケージ参照として自動的に追加されます。
.NET を使用した Docker の詳細については、「 チュートリアル: .NET アプリをコンテナー化する」を参照してください。 Azure へのデプロイの詳細については、「 チュートリアル: Worker サービスを Azure にデプロイする」を参照してください。
重要
Worker テンプレートで ユーザー シークレット を利用する場合は、 Microsoft.Extensions.Configuration.UserSecrets
NuGet パッケージを明示的に参照する必要があります。
ホステッド サービスの拡張性
IHostedService インターフェイスでは、2 つのメソッドが定義されています。
これら 2 つのメソッドは ライフサイクル メソッドとして機能します。これらは、ホストの開始イベントと停止イベントの間にそれぞれ呼び出されます。
注
StartAsyncメソッドまたはStopAsync メソッドのいずれかをオーバーライドする場合は、サービスが正常に開始またはシャットダウンされるように、await
クラス メソッドを呼び出してbase
する必要があります。
重要
インターフェイスは、 AddHostedService<THostedService>(IServiceCollection) 拡張メソッドのジェネリック型パラメーター制約として機能します。つまり、実装のみが許可されます。 サブクラスで提供されている BackgroundService を自由に使用することも、独自のを完全に実装することもできます。
完了の通知
ほとんどの一般的なシナリオでは、ホストされているサービスの完了を明示的に通知する必要はありません。 ホストがサービスを開始すると、ホストが停止するまで実行するように設計されています。 ただし、一部のシナリオでは、サービスの完了時にホスト アプリケーション全体の完了を通知する必要がある場合があります。 完了を通知するには、次の Worker
クラスを検討してください。
namespace App.SignalCompletionService;
public sealed class Worker(
IHostApplicationLifetime hostApplicationLifetime,
ILogger<Worker> logger) : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
// TODO: implement single execution logic here.
logger.LogInformation(
"Worker running at: {Time}", DateTimeOffset.Now);
await Task.Delay(1_000, stoppingToken);
// When completed, the entire app host will stop.
hostApplicationLifetime.StopApplication();
}
}
上記のコードでは、 BackgroundService.ExecuteAsync(CancellationToken) メソッドはループせず、完了すると IHostApplicationLifetime.StopApplication()を呼び出します。
重要
これにより、停止する必要があることをホストに通知します。この呼び出しを行わないと、ホスト StopApplication
は無期限に実行され続けます。 有効期間の短いホステッド サービス (1 回だけ実行するシナリオ) を実行し、Worker テンプレートを使用する場合は、 StopApplication
を呼び出してホストに停止を通知する必要があります。
詳細については、以下を参照してください。
代替アプローチ
依存関係の挿入、ログ記録、構成が必要な有効期間の短いアプリの場合は、Worker テンプレートの代わりに .NET 汎用ホスト を使用します。 これにより、 Worker
クラスなしでこれらの機能を使用できます。 汎用ホストを使用する有効期間の短いアプリの簡単な例では、次のようなプロジェクト ファイルを定義できます。
<Project Sdk="Microsoft.NET.Sdk.Worker">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>true</ImplicitUsings>
<RootNamespace>ShortLived.App</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="9.0.6" />
</ItemGroup>
</Project>
クラス Program
次のようになります。
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddSingleton<JobRunner>();
using var host = builder.Build();
try
{
var runner = host.Services.GetRequiredService<JobRunner>();
await runner.RunAsync();
return 0; // success
}
catch (Exception ex)
{
var logger = host.Services.GetRequiredService<ILogger<Program>>();
logger.LogError(ex, "Unhandled exception occurred during job execution.");
return 1; // failure
}
上記のコードは、ジョブを実行するためのロジックを含むカスタム クラスである JobRunner
サービスを作成します。
RunAsync
メソッドはJobRunner
で呼び出され、正常に完了すると、アプリは0
を返します。 ハンドルされない例外が発生すると、エラーがログに記録され、 1
が返されます。
この単純なシナリオでは、 JobRunner
クラスは次のようになります。
using Microsoft.Extensions.Logging;
internal sealed class JobRunner(ILogger<JobRunner> logger)
{
public async Task RunAsync()
{
logger.LogInformation("Starting job...");
// Simulate work
await Task.Delay(1000);
// Simulate failure
// throw new InvalidOperationException("Something went wrong!");
logger.LogInformation("Job completed successfully.");
}
}
RunAsync
メソッドに実際のロジックを追加する必要は明らかですが、この例では、Worker
クラスを必要とせず、ホストの完了を明示的に通知することなく、有効期間の短いアプリに汎用ホストを使用する方法を示します。
こちらも参照ください
.NET