きよくらの備忘録

「三日坊主と呼ばせない!日記」改め。主にソフトウェア開発関連の話題。

Hangfireを使ってみる (1):とりあえず触ってみる

最近 Hangfire について調査・検証しているのでメモ。

Hangfireとは

Hangfireは.NET向けのバックグランド・タスクのライブラリです。

www.hangfire.io

ストーレージにタスクをキューイングして、非同期に実行させることができます。即時実行だけでなく、cronっぽい感じで定期実行したりもできる模様。 OWIN Middleware としてASP.NETで利用できるほか、コンソール/WPF/Windows Form/Windows サービス等など……でも使えます。

 

今回から予定している一連のエントリは『ASP.NETのWebアプリケーションから重たい処理(例:集計帳票作成処理)を非同期実行させる』という観点での調査検証に基づいています。ですので、ある程度方より(?)がある点はご了承ください。

Hangfireを構成するコンポーネント

Hangfireはざっくりと以下のようなコンポーネントで構成されています。

コンポーネント 役割 典型的な実装
Hangfire Client Jobをキューへ登録 ASP.NET等, ユーザーが操作するアプリ
Job Storage Jobのキュー,その他管理情報を格納 SQL Server / Redis 等
Hangfire Server キュー上のJobの実行 ASP.NET(バックグラウンド) / Windowsサービス等

 

Hangfire ClientとHangfire Serverは同一のプロセス上で実装可能です*1。 そのほか、ASP.NET用にビルトインされていてるダッシュボード機能があり、jobの状況のモニタリングや再実行などの操作を行うことができます。

とりあえず試してみる

チュートリアル的に一番シンプルで試しやすい構成として、『ClientとServerの双方を同一のASP.NET Webアプリケーションに実装』するパターンを試してみます。

f:id:kiyokura:20170803113448p:plain:w300

https://github.com/kiyokura/HangfireSample01/tree/step01

基本構成

とりあえずこんな感じでやってみました。

  • Visual Studio 2015
  • ASP.NET MVC 5
  • SQL Server 2016

実装手順

1. SQL ServerでHangfire用のDBを作成

Jobストレージとして利用するデータベースが必要なので適当に作成しておきます。データベース内にはHangfireスキーマといくつか専用のテーブルが作成されます*2

2. ASP.NET MVCのプロジェクトを作成する

Visual Studio 2015で適当にASP.NET MVCのプロジェクトを作成しました。 f:id:kiyokura:20170803115757p:plain:w300

3. NuGetでHangfireのパッケージを取得

NuGetでHangfireを取り込みます。コマンドラインからだとInstall-Package HangfireでOK。

f:id:kiyokura:20170803120224p:plain

4. OWINスタートアップを記述

Hangire の最低限必要な設定をOWINスタートアップで行うように記述します。Webアプリケーションプロジェクトの直下にOWIN Startupクラス「Startup.cs」を追加して、以下のように書きました。

using Hangfire;
using Microsoft.Owin;
using Owin;

[assembly: OwinStartup(typeof(HangfireSample01.Startup))]

namespace HangfireSample01
{
  public class Startup
  {
    public void Configuration(IAppBuilder app)
    {
      // 接続文字列は環境に応じて変えること
      var constr = @"Data Source=localhost;Initial Catalog=HangfireJob;Integrated Security=True";
      GlobalConfiguration.Configuration.UseSqlServerStorage(constr);

      app.UseHangfireDashboard();
      app.UseHangfireServer();
    }
  }
}
5. Jobの登録処理を記述

最後に実際にjobをキューに登録する処理を書きます。既存のHomeControllerのIndexアクションメソッドに、とりあえずコンソールに文字を出力するだけの単純な処理をjobとして登録するように書いてみました。

using Hangfire;
using System;
using System.Web.Mvc;

namespace HangfireSample01.Controllers
{
  public class HomeController : Controller
  {
    public ActionResult Index()
    {
      // Hangfireでjobをキューに登録
      BackgroundJob.Enqueue(() => Console.WriteLine("Simple Job"));
      return View();
    }

    public ActionResult About()
    {
      ViewBag.Message = "Your application description page.";
      return View();
    }

    public ActionResult Contact()
    {
      ViewBag.Message = "Your contact page.";

      return View();
    }
  }
}

実行して確認する

準備が終わったので実行してみます。

1.WebアプリケーションをF5で実行

F5でデバッグ実行を開始すると見慣れた(?)ASP.NET MVCのテンプレートのホーム画面が表示されます。このとき、Home/Indexアクションメソッドが実行されているので裏ではキューにjobが登録され、そして実行されているはずです。

次の手順でそのあたりを確認してみます。

2.Hangfireダッシュボードを表示

Hangfire組み込みのダッシュボードを利用してjobの状況を確認してみます。hangfireダッシュボードは、アプリケーションルート+/hangfireというURLでアクセスできます。今回のようにIIS Expressでローカル実行している場合は例えば以下のようになります

  • http://localhost:61828/hangfire (※ポート番号は環境によって変わります)

アクセスできるとこんな感じのダッシュボードが表示されます。 f:id:kiyokura:20170803122544p:plain

3.jobの実行状況を確認

ダッシュボードで実際のjobがどのようになっているかを確認します。 メニューから[jobs]-[Succeeded]とたどっていくと、成功したジョブ一覧に先ほど登録したjobが並んでいるのが見えました。

f:id:kiyokura:20170803122848p:plain

さらにjob名をクリックすると詳細情報が見れます。 f:id:kiyokura:20170803123351p:plain

4.データベース(Job Storage)を確認

最後に、Job Storageとなっているデータベースを簡単に確認してみました。

SSMSで該当のデータベースを開いてみると、こんな感じでテーブルが作成され、例えばHangfire.Jobテーブルにはこんな形でJobが格納されているのが分かります。 f:id:kiyokura:20170803123619p:plain

ここまでのまとめ

Hangfireのクイックスタート的な感じでとりあえず手元で基本的な動作をさせてみました。というか、オフィシャルのドキュメントのQuick Startにあるそのままです。

なお、ここまでのサンプルコードは以下にあります

github.com

このHangfire ServerもASP.NETのインプロセスで動かすモデルの場合、ASP.NETの標準仕組の HostingEnvironment.QueueBackgroundWorkItem と似ているようにも見えます。しかしキューが永続化されているためjobのトレースやリトライ等QueueBackgroundWorkItemでは実現困難なことが簡単に実現できる点や将来的なスケールアウトの容易さ等、差別化のポイントはいくつもあるように思います。 (一方QueueBackgroundWorkItemはインプロセス前提なのでHttpContext等ASP.NETの実行インスタンスに依存する情報をHangfire よりもよりシンプルに扱うことができる等のメリット?はあかなーと思ったりもします)

次はClientとServerを別プロセスでやってみたいと思います。

*1:もちろん別のプロセスに分離できます。スケールなど考えるとそちらのほうが良いケースは少なくないでしょう

*2:各オブジェクトは初回実行時に勝手に作成されるのでこのタイミングで手動で作成する必要はありません