きよくらの備忘録

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

Hangfireを使ってみる (2):ClientとServerを別プロセスにしてみる

前回からの続きです。

前回は『ClientとServerの双方を同一のASP.NET Webアプリケーションに実装』しましたが、今回はこれを分離してみたいと思います。

Hangfire Clientと Hangfire Serverを別プロセスにする

今回は『ClientはASP.NET Webアプリケーション、ServerをWindowsコンソールアプリケーションで実装』を試してみます。またClientとServerを別にするだけでなく、ジョブとして実行する処理もDLLとして切り出しておくことにします。

f:id:kiyokura:20170803223227p:plain

ソリューションは前回使ったものに手を入れていきます。

 

実装手順

1. ジョブのDLLを作成

ソリューションにWindowsクラスライブラリのプロジェクト『MyJobsLib』を追加して、ジョブとして実行したい処理のクラスを実装します。ジョブはCustomJob.csとして、実行結果が分かり易いようにTempフォルダにテキストファイルを吐き出すような処理にしてみました。

f:id:kiyokura:20170803232927p:plain

// CustomJob.cs
using System.IO;

namespace MyJobsLib
{
  public class CustomJob
  {
    public void Execute(string message)
    {
      var fileName = string.Format("HangfireJob_{0}.txt", System.Guid.NewGuid());
      var filepath = Path.Combine(Path.GetTempPath(), fileName);
      using (var writer = new StreamWriter(filepath, true))
      {
        writer.WriteLine(message);
      }
    }
  }
}
2. Webアプリケーションの書き換え

Webアプリケーション側は主に以下の2点対応を行います。

  1. キューに登録するジョブを習性
  2. OWIN StartUpからHangfire Serverの登録を削除

まず、[参照の追加]等からMyJobsLibを参照したうえで、BackgroundJob.Enqueueで登録するジョブをDLL内のものに変更します。

// HomeController.cs
using Hangfire;
using System.Web.Mvc;

namespace HangfireSample01.Web.Controllers
{
  public class HomeController : Controller
  {
    public ActionResult Index()
    {
      // Hangfireでjobをキューに登録
      BackgroundJob.Enqueue(() => new MyJobsLib.CustomJob().Execute("Job Executed.")); // ★ここを変更
      return View();
    }

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

      return View();
    }

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

      return View();
    }
  }
}

次に、Statup.csでHangfire Serverの登録を行っていた箇所を削除します。

// Startup.cs
using Hangfire;
using Microsoft.Owin;
using Owin;

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

namespace HangfireSample01.Web
{
  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(); ★ここを削除
    }
  }
}
3. コンソールアプリケーションの作成

最後に、Hangfire Serverとして動作させるコンソールアプリケーションを作成します。ここはオフィシャルのドキュメントProcessing jobs in a console applicationを参考に……というかほぼそのままです。

コンソールアプリケーションプロジェクト追加

ソリューションに『HangfireSample01.ServerConsole』という名前でコンソールアプリケーションプロジェクトを追加しました。

f:id:kiyokura:20170803234729p:plain

ジョブのDLLを参照する

[参照の追加]等からMyJobsLibの参照を追加します。ちなみにこの追加を行わなくてもビルドでエラーが出ないので、うっかり忘れると実行時に例外に遭遇することになります(経験者談)。

NuGetパッケージをインストール

サーバーとして動作させるだけの場合、必要なパッケージは限られるます。オフィシャルにドキュメントに従い、Hangfire.CoreHangfire.SqlServerのみをインストールしました。パッケージマネージャーコンソールから行う場合は以下の操作になります。

PM> Install-Package Hangfire.Core

PM> Install-Package Hangfire.SqlServer

Hangfire Serverの起動処理を実装

最後にProgram.csにHangfire Serverの起動処理を書きます。UseSqlServerStorageメソッドの引数に渡す接続文字列は、Webアプリケーション側で指定したのと同じものを指定します。それ以外はオフィシャルのドキュメントのそのままです。

// Program.cs
using Hangfire;
using System;

namespace HangfireSample01.ServerConsole
{
  class Program
  {
    static void Main(string[] args)
    {
      var constr = @"Data Source=localhost;Initial Catalog=HangfireJob;Integrated Security=True";
      GlobalConfiguration.Configuration.UseSqlServerStorage(constr);

      using (var server = new BackgroundJobServer())
      {
        Console.WriteLine("Hangfire Server started. Press any key to exit...");
        Console.ReadKey();
      }
    }
  }
}

 

実行して確認する

以上で準備が完了したので早速実行してみます。

……と言いつつ、簡単に実行するために実行前にもう一つ設定しておきます。ソリューションの[スタートアッププロジェクトの設定]で、コンソールアプリケーションとWebアプリケーションの両方を実行するように設定しておきます。

f:id:kiyokura:20170803235821p:plain

 

この状態でF5でデバッグ実行を開始するとブラウザとコンソールアプリケーションの両方が起動すると思います。起動したらHangfireのダッシュボードを表示し、jobの状態を確認してみると、ちゃんとジョブが実行されていることが確認できました。

f:id:kiyokura:20170804000620p:plain

また、Tempフォルダにファイルが作成されていることも確認できました。

f:id:kiyokura:20170804000549p:plain

 

ここまでのまとめ

ClientとServerの分離を試してみました。今回はConsoleアプリケーションでやりましたが、他の形態でも基本は変わらないように思います。またオフィシャルのドキュメント Processing jobs in a Windows Service にはWindowsサービスでの実装について書かれています。

今回のソースは以下のリポジトリのstep02ブランチになります。

github.com

次回は、DIコンテナを用いてさらに実用的な構成の検証をしていきます。