noxi雑記

.NET、Angularまわりの小ネタブログ

MicroBatchFrameworkを試す 3 - バッチ終了のハンドル

前回の記事では MicroBatchFramework で設定ファイルを読み込む方法を書いてみました。この記事は MicroBatchFramework を使用した際の、バッチアプリ終了をハンドルして何か処理を挟み込む方法です。


試用環境

バッチアプリ終了をハンドルする

MicroBatchFrameworkを使用すると HostBuilder を拡張してバッチアプリを作ることができます。この HostBuilderASP.NET Core をホストしている WebHost を Web アプリ以外でも汎用的に使えるようにしたもので、実行に関する設定方法で両者に大きな違いはありません(私が無いと思っているだけですが)。
.NET Core には IApplicationLifetime インタフェースを通じてアプリの実行開始や終了をハンドルできる機能があります。また MicroBatchFramework 自体にもインターセプターを通じて、バッチの開始や終了をハンドルする機能が実装されています。

IApplicationLifetime を使用する

先にも記した通り、 IApplicationLifetime .NET Core の HostBuilder でホストされるアプリケーションの開始や終了をハンドルすることができるインターフェースです。アプリケーションの終了は ApplicationStopping に対してリスナーを登録します。

BatchApp のルート階層に ShutdownHandler クラスを追加し、以下のように実装します。

src\BatchApp\ShutdownHandler.cs

using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;

namespace BatchApp
{
    public class ShutdownHandler : IHostedService
    {
        public ShutdownHandler(IApplicationLifetime applicationLifetime)
        {
            applicationLifetime.ApplicationStopping.Register(OnApplicationStopping);
        }

        private void OnApplicationStopping()
        {
            // アプリ終了時の処理
        }

        public Task StartAsync(CancellationToken cancellationToken)
        {
            return Task.CompletedTask;
        }

        public Task StopAsync(CancellationToken cancellationToken)
        {
            return Task.CompletedTask;
        }
    }
}

IHostedService .NET Core の汎用ホスト上で実行されるバックグラウンドサービスを実装するためのインターフェースです。これを実装することで、他のコンポーネントからの依存が無くてもアプリケーションの起動と同時にこのサービスが開始されます。
アプリ終了をハンドルするサービスを実装したら、次にこれをシングルトンコンポーネントとして登録します。 Program.cs を開き、 ConfigureServices に登録するためのコードを追加します。

src\BatchApp\Program.cs

using System;
using System.Threading.Tasks;
using MicroBatchFramework;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;

namespace BatchApp
{
    class Program
    {
        static async Task Main(string[] args)
        {
            await BatchHost.CreateDefaultBuilder()
                .ConfigureServices((hostContext, services) =>
                {
                    services.Configure<SampleConfig>(hostContext.Configuration);
                    services.AddSingleton<IHostedService, ShutdownHandler>();  // 追加
                })
                .RunBatchEngineAsync<HelloWorldBatch>(args);
        }
    }

    ...
}

インターセプターを使用する

MicroBatchFramework にはバッチアプリの開始と終了をハンドルするインターセプター機能が実装されています。 IBatchInterceptor を実装して RunBatchEngineAsync の引数に指定することで動作します。 IoC コンテナに登録するコンポーネントではないことに注意が必要です。IBatchInterceptor には4つのメソッドが用意されており、「バッチホストの開始」「バッチ処理の開始」「バッチ処理の終了」「バッチホストの終了」時に呼び出されます。

実装を書いていたら面倒臭くなってきたので、試した結果、こんなライフサイクルでした。そりゃそうだよね、という感じ以外の何者でも無いですが。

IHostedService:Start
IBatchInterceptor:BatchEngineBegin
IBatchInterceptor:BatchRunBegin
Batch:Constructor
Batch:Run
IBatchInterceptor:BatchRunComplete
IApplicationLifetime:ApplicationStopping
IApplicationLifetime:ApplicationStarted
IBatchInterceptor:BatchEngineEnd
IHostedService:Stop
IApplicationLifetime:ApplicationStopped

おわりに

この記事では MicroBatchFramework でバッチアプリの終了をハンドルする方法についてでした。私は MicroBatchFramework を使用して Azure AD から認証を取りつつ REST API を叩くだけのバッチを作ったのですが、実行ログを ApplicationInsights に対して送信する際に、バッチアプリの終了が取れずにログが未送信のまま終了してしまうのを防ぐために使用しています。試した時点ではまだドキュメントが不完全でインターセプターに関する情報が無かったため IApplicationLifetime を使用してハンドルしました。