.NET の Worker サービス

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

  • 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;

IHost host = Host.CreateDefaultBuilder(args)
    .ConfigureServices(services =>
    {
        services.AddHostedService<Worker>();
    })
    .Build();

await host.RunAsync();

前の Program クラスは次のとおりです。

  • 既定の IHostBuilder を作成します。
  • ConfigureServices を呼び出して、AddHostedService でのホステッド サービスとして Worker を追加します。
  • ビルダーから IHost をビルドします。
  • host インスタンスで Run を呼び出します。アプリが実行されます。

ヒント

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

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

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

テンプレートの Program.cs ファイルは、最上位レベルのステートメントを使用して書き換えることができます。

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using App.WorkerService;

using IHost host = Host.CreateDefaultBuilder(args)
    .ConfigureServices((hostContext, services) =>
    {
        services.AddHostedService<Worker>();
    })
    .Build();

await host.RunAsync();

これは、元のテンプレートと機能的に同等です。 C# 9.0 の機能の詳細については、「C# 9.0 の新機能」を参照してください。

Worker の場合と同様に、テンプレートでは単純な実装が提供されます。

namespace App.WorkerService
{
    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);
            }
        }
    }
}

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

プロジェクト ファイル

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

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

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

NuGet パッケージ

ワーカー サービス テンプレートに基づくアプリは 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:6.0 AS base
WORKDIR /app

FROM mcr.microsoft.com/dotnet/sdk:6.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:6.0 の基本イメージのエイリアス base としての設定。
  • /app への作業ディレクトリの変更。
  • mcr.microsoft.com/dotnet/sdk:6.0 イメージからの build エイリアスの設定。
  • /src への作業ディレクトリの変更。
  • コンテンツのコピーと .NET アプリの発行。
    • このアプリは、dotnet publish コマンドを使用して発行されます。
  • mcr.microsoft.com/dotnet/runtime:6.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>net6.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>true</ImplicitUsings>
    <RootNamespace>App.WorkerService</RootNamespace>
    <DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.Hosting" Version="6.0.0" />
    <PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.11.1" />
  </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 class Worker : BackgroundService
{
    private readonly IHostApplicationLifetime _hostApplicationLifetime;
    private readonly ILogger<Worker> _logger;

    public Worker(
        IHostApplicationLifetime hostApplicationLifetime, ILogger<Worker> logger) =>
        (_hostApplicationLifetime, _logger) = (hostApplicationLifetime, logger);

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        // TODO: implement single execution logic here.
        _logger.LogInformation(
            "Worker running at: {time}", DateTimeOffset.Now);

        await Task.Delay(1000, stoppingToken);

        // When completed, the entire app host will stop.
        _hostApplicationLifetime.StopApplication();
    }
}

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

重要

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

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

関連項目