.NET のワーカー サービス

実行時間の長いサービスを作成するには、次のようなさまざまな理由があります。

  • 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 サービス: Worker サービス テンプレートのことです。

ワーカー サービス テンプレート

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 をビルドします。
  • host インスタンスで Run を呼び出します。アプリが実行されます。

テンプレートの既定値

Worker テンプレートでは、サーバーのガベージ コレクション (GC) は既定では有効になりません。これは、その必要性を判断する役割を果たす多数の要因があるためです。 実行時間の長いサービスを必要とするすべてのシナリオで、この既定の状態のパフォーマンスへの影響を考慮する必要があります。 サーバーの GC を有効にするには、プロジェクト ファイルに ServerGarbageCollection ノードを追加します。

<PropertyGroup>
    <ServerGarbageCollection>true</ServerGarbageCollection>
</PropertyGroup>

トレードオフと考慮事項

Enabled 無効
効率的なメモリ管理: メモリ リークを防ぎ、リソース使用を最適化するために、未使用のメモリを自動的に再利用します。 リアルタイム パフォーマンスの向上: 待機時間の影響を受けやすいアプリケーションでガベージ コレクションによって発生する可能性のある一時停止または中断を回避します。
長期的な安定性: 延長期間にわたってメモリを管理することで、長時間実行されるサービスで安定したパフォーマンスを維持します。 リソース効率: リソースが制約された環境で CPU とメモリのリソースを節約できます。
メンテナンスの削減: 手動によるメモリ管理の必要性を最小限に抑え、メンテナンスを簡単にします。 手動メモリ制御: 特殊なアプリケーションでメモリをきめ細かく制御できます。
予測可能な動作: 一貫性があり、予測可能なアプリケーション動作に貢献します。 有効期間の短いプロセスに最適: 有効期間の短いプロセス、あるいは一時的なプロセスに対して、ガベージ コレクションのオーバーヘッドを最小限に抑えます。

パフォーマンスに関する考慮事項の詳細については、「サーバーの GC」を参照してください。 サーバーの GC を構成する方法の詳細については、サーバーの GC の構成例を参照してください。

Worker クラス

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 クラスは、IHostedService を実装する BackgroundService のサブクラスです。 BackgroundServiceabstract class であり、サブクラスで BackgroundService.ExecuteAsync(CancellationToken) を実装する必要があります。 テンプレートの実装では、ExecuteAsync が 1 秒に 1 回ループして、プロセスがキャンセルを通知されるまで、現在の日付と時刻をログに記録します。

プロジェクト ファイル

ワーカー テンプレートは、次のプロジェクト ファイル Sdk に依存しています。

<Project Sdk="Microsoft.NET.Sdk.Worker">

詳細については、「.NET プロジェクト SDK」を参照してください。

NuGet パッケージ

ワーカー テンプレートに基づくアプリは Microsoft.NET.Sdk.Worker SDK を使用し、Microsoft.Extensions.Hosting パッケージへの明示的なパッケージ参照を含んでいます。

コンテナーとクラウドの適応性

最新の .NET ワークロードでは、コンテナーが実用的な選択肢です。 Visual Studio でワーカー テンプレートから長時間実行されるサービスを作成する場合は、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 AS base
WORKDIR /app

FROM mcr.microsoft.com/dotnet/sdk:8.0 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 への作業ディレクトリの変更。
  • mcr.microsoft.com/dotnet/sdk:8.0 イメージからの build エイリアスの設定。
  • /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 Hub の Microsoft のシンジケート化されたコンテナー カタログです。 詳細については、Microsoft によるコンテナー カタログのシンジケート化に関する記事を参照してください。

.NET Worker サービスのデプロイ戦略として 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="8.0.0" />
    <PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.5" />
  </ItemGroup>
</Project>

前のプロジェクト ファイルの <DockerDefaultTargetOS> 要素では、ターゲットとして Linux が指定されています。 Windows コンテナーをターゲットにするには、代わりに Windows を使用します。 テンプレートから Docker サポートが選択されると、パッケージ参照として Microsoft.VisualStudio.Azure.Containers.Tools.Targets NuGet パッケージが自動的に追加されます。

.NET での Docker の詳細については、.NET アプリのコンテナー化のチュートリアルに関する記事を参照してください。 Azure へのデプロイの詳細については、Azure への Worker サービスのデプロイのチュートリアルに関する記事を参照してください。

重要

ワーカー テンプレートを使用して [ユーザー シークレット] を活用する場合は、Microsoft.Extensions.Configuration.UserSecrets NuGet パッケージを明示的に参照する必要があります。

ホステッド サービスの拡張性

IHostedService インターフェイスでは、2 つのメソッドが定義されています。

これら 2 つのメソッドは、"ライフサイクル" メソッドとして機能します。これらは、それぞれ、ホストの開始イベントと停止イベントの間に呼び出されます。

注意

StartAsync または StopAsync メソッドがオーバーライドされた場合、サービスが正常にシャットダウンされるように、base 基本クラス メソッドを呼び出す (および await する) 必要があります。

重要

このインターフェイスは、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();
    }
}

上記のコードでは、ExecuteAsync メソッドはループせず、完了すると IHostApplicationLifetime.StopApplication() が呼び出されます。

重要

これにより、停止する必要があることをホストに通知します。この StopApplication の呼び出しがないと、ホストの実行は無期限に継続されます。

詳細については、次を参照してください。

関連項目