ASP.NET Core で Hangfire を用いた永続化バックグラウンドジョブの実装

Hangfire は、.NET アプリケーション向けの分散型ジョブスケジューラであり、データベース(MySQL や SQL Server など)を用いてジョブ状態を永続化できます。本稿では、ASP.NET Core 7/8 環境下で MySQL をストレージとして Hangfire を構成・運用する方法を、実践的な設定とコード例を交えて解説します。

必要な NuGet パッケージ

  • Hangfire.AspNetCore
  • Hangfire.MySqlStorage

サービス登録とミドルウェア設定

以下は、Program.cs における最小限かつ堅牢な設定例です。接続文字列は構成ファイル(例:appsettings.json)から読み込み、MySQL ストレージの各種オプションを明示的に指定しています。

using Hangfire;
using Hangfire.MySql;
using Microsoft.Extensions.DependencyInjection;

var builder = WebApplication.CreateBuilder(args);

// Hangfire のストレージ設定(MySQL 使用)
var hangfireConnectionString = builder.Configuration.GetConnectionString("HangfireDb") 
    ?? throw new InvalidOperationException("HangfireDb connection string is missing.");

builder.Services.AddHangfire(config => config
    .UseStorage(new MySqlStorage(hangfireConnectionString, new MySqlStorageOptions
    {
        TransactionIsolationLevel = System.Transactions.IsolationLevel.ReadCommitted,
        QueuePollInterval = TimeSpan.FromSeconds(10),
        JobExpirationCheckInterval = TimeSpan.FromHours(2),
        CountersAggregateInterval = TimeSpan.FromMinutes(3),
        PrepareSchemaIfNecessary = true,
        DashboardJobListLimit = 10000,
        TransactionTimeout = TimeSpan.FromMinutes(2),
        TablesPrefix = "hf_"
    })));

// Hangfire サーバー(ジョブ実行エンジン)を登録
builder.Services.AddHangfireServer(options =>
{
    options.WorkerCount = Environment.ProcessorCount * 5;
    options.Queues = new[] { "default", "critical" };
});

// 定期ジョブ登録用のホステッドサービスを追加
builder.Services.AddHostedService<ScheduledJobRegistrar>();

// その他のサービス(Controllers, Swagger など)
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Hangfire ダッシュボードを有効化(デフォルトパス: /hangfire)
app.UseHangfireDashboard("/hangfire", new DashboardOptions
{
    Authorization = new[] { new CustomDashboardAuthorizationFilter() }
});

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();

app.Run();

定期ジョブの自動登録(ホステッドサービス)

アプリケーション起動時に Cron 式に基づきジョブを登録するため、カスタム BackgroundService を実装します。以下のクラスでは、複数のタイムゾーンや間隔を持つジョブを一括登録し、エラー発生時もプロセスが停止しないよう例外処理を組み込んでいます。

using Hangfire;
using Hangfire.Server;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace Infrastructure.Jobs;

public class ScheduledJobRegistrar : BackgroundService
{
    private readonly IRecurringJobManager _jobManager;
    private readonly ILogger<ScheduledJobRegistrar> _log;

    public ScheduledJobRegistrar(IRecurringJobManager jobManager, ILogger<ScheduledJobRegistrar> log)
    {
        _jobManager = jobManager;
        _log = log;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        try
        {
            // 毎15秒(UTC)
            _jobManager.AddOrUpdate(
                "heartbeat-utc",
                () => Console.WriteLine($"[UTC] Heartbeat at {DateTime.UtcNow:o}"),
                "*/15 * * * * ?",
                new RecurringJobOptions { TimeZone = TimeZoneInfo.Utc });

            // 毎分(ローカルタイムゾーン)
            _jobManager.AddOrUpdate(
                "daily-report",
                () => Console.WriteLine($"[Local] Report generated at {DateTime.Now:o}"),
                Cron.Minutely,
                new RecurringJobOptions { TimeZone = TimeZoneInfo.Local });

            // 毎日午前2時(日本標準時)
            var jst = TimeZoneInfo.FindSystemTimeZoneById("Tokyo Standard Time");
            _jobManager.AddOrUpdate(
                "cleanup-jst",
                () => Console.WriteLine("Running daily cleanup in JST..."),
                "0 0 2 * * ?",
                new RecurringJobOptions { TimeZone = jst });

            // 毎時15分(米国ハワイ時間)
            var hst = TimeZoneInfo.FindSystemTimeZoneById("Hawaiian Standard Time");
            _jobManager.AddOrUpdate(
                "hawaii-alert",
                () => Console.WriteLine("Hawaiian time alert fired."),
                "0 15 * * * ?",
                new RecurringJobOptions { TimeZone = hst });

            _log.LogInformation("All recurring jobs registered successfully.");
        }
        catch (Exception ex)
        {
            _log.LogError(ex, "Failed to register recurring jobs.");
        }

        // 一度だけ実行 → 終了
        await Task.CompletedTask;
    }
}

ダッシュボードとデータベース構造

アプリケーション起動後、https://localhost:5001/hangfire(または任意のホスト/ポート)にアクセスすると、Hangfire の管理ダッシュボードが表示されます。MySQL には、hf_ 接頭辞付きの以下のテーブル群が自動生成されます:

  • hf_job(ジョブ定義)
  • hf_state(ジョブ状態履歴)
  • hf_jobparameter(パラメータ)
  • hf_jobqueue(キュー)
  • hf_hash, hf_list, hf_set, hf_counter(内部カウンター・集合)

これらのテーブルにより、ジョブの再試行、監査、失敗分析が可能になります。

タグ: hangfire aspnetcore MySQL cron scheduled-tasks

6月25日 21:25 投稿