きよくらの備忘録

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

Azure の仮想マシンの起動と終了をメールで通知する

前に少し調べたときにはよくわからなかくて他の方法で逃げたんですが、昨日Azure Monitorのドキュメントとかを何の気なしに眺めてるとなんか簡単にできそうなので試してみました。

実現したいこと&実現方法

実現したいこと:

- 仮想マシンの起動が成功したらメールで通知する
- 仮想マシンの停止が成功したらメールで通知する

実現方法:

- 仮想マシンのブレードから「アクティビティ ログ アラート」を追加する

 

手順

1. 仮想マシンを選択して[アラートルール]で[アクティビティログアラートの追加]を選択

いくつか入り口がありそうですがとりあえずここから。

f:id:kiyokura:20171106230936p:plain

2. 各プロパティーを設定

ブレードのフォームを埋めていきます。

仮想マシンのアクティビティログのイベントをひっかけるにはソースはこんな感じで良さそう。

項目 選択値
イベント カテゴリ 管理
リソースの種類 Vertual Machines
リソースグループ <監視したい仮想マシンのリソースグループ>
リソース <監視したい仮想マシン>
操作名 仮想マシンの起動
レベル すべて
状態 成功

アクションではメール通知を[Edit details]からメールアドレスを設定しておきます。

f:id:kiyokura:20171106231453p:plain

登録を完了させると、通期先のメールアドレスに登録完了の旨のメールが届きます。

f:id:kiyokura:20171106231555p:plain

以上で設定は完了です。

その後仮想マシンを起動するとメールで通知が飛んできました。

そのほかの操作について通知を飛ばしたい場合

なお、仮想マシン終了時など、そのほかの操作をトリガに通知したい場合は操作名で該当する操作を選択すればOKです。

f:id:kiyokura:20171106235016p:plain

 

まとめ

なんだか拍子抜けするほど簡単でした。素晴らしい。

今回は通知方法にメールを使いましたが、WebHookで任意のURLにJSON形式のデータをPOSTすることもできます。とりあえずAzure Functionsに投げたらそのまま素直に連携できましたので、後日またそれについて書いてみたいと思います。

Hangfireを使ってみる (6):Jobの種類について:3. Continuations

前回の続きです

今回は3つ目のタイプのジョブについて。

3.Continuations

連続という玉突きというか、指定したJobが終わった後に実行されるように登録するタイプのジョブです。

この前提の話として、前回と前々回で紹介したFire-and-forgetとDelayedを登録するBackgroundJob.EnqueueBackgroundJob.Scheduleメソッドは、戻り値として登録したジョブのidを返します。Continuationsなジョブの登録は、BackgroundJob.ContinueWithメソッドにそのidを指定して登録することで行います。

例えばこんな感じです。

var jobid = BackgroundJob.Enqueue<MyJobsLib.ICostomJob>(x => x.Execute("Job Executed."));
BackgroundJob.ContinueWith<MyJobsLib.ICostomJob>(jobid, x => x.Execute("2nd job Executed."));

ダッシュボード

上記コードを実行後にダッシュボードをみると、このように『awaiting』にリストアップされていました。

f:id:kiyokura:20170806172032p:plain

Jobテーブル

Jobテーブルはこんな感じです。

f:id:kiyokura:20170808001837p:plain

JobQueueテーブル

JobQueueはこうです。一つ目のジョブのみが登録されています。

f:id:kiyokura:20170808001719p:plain

Setテーブル

Setテーブルを見ると、ここでawaitingで登録されていることが分かります(Valueがジョブid)。

f:id:kiyokura:20170808001347p:plain

JobParameterテーブル

Continuations Jobsの場合のポイントはこのJobParameterテーブルです。JobId=1のレコードの中のName=Continuationsのレコードがあり、後続のジョブのJobIdが記録されていることから、このパラメーターでジョブ同士の関連性を管理しているのでしょう。

f:id:kiyokura:20170808001018p:plain

実行されるときの挙動

Job StorageがSQL Serverの場合、Hangfire Serverによるキューのポーリングを起点に順次実行されます。

このとき、現在キューにあるジョブが読み込まれたタイミングで、ストレージ上に存在する連続するジョブはすべて一度に読み込まれたのち、そのまま連続して処理されるようです。

例えば、ContinueWithで関連付けられたJob1Job2が存在する場合、Job1が実行された後、次回のポーリングを待つことなくそのまま連続して実行されました*1。 ただし、ジョブのステートの遷移を見ると一応は一瞬キューイングされてるようには記録されていますので、内部的には一応キューを経由してるはしているのかも知れません(このあたりソースを読めばはっきりするのでしょうが)。

まとめと次回

Continuations ジョブを利用すると、ちょっとしたバッチ処理のようなことも可能だと思います。ただしBackgroundJob.Requeueメソッドで再実行を行う際には二つ同時にRequeueしても順番の保証はされないようですので*2、その点は注意が必要と思いました

次は 『Recurring』、つまり定期実行タイプのジョブ実行について書きます。

*1:キューのポーリング間隔を十分に長くして検証した結果、そういう挙動が見られました

*2:ダッシュボードで連続するjobを同時に選択してRequeueすると、どちらも同時にQueueに登録された

Hangfireを使ってみる (5):Jobの種類について:2. Delayed

前回の続きです。

今回は二つ目のタイプのジョブについて。

2.Delayed

指定した時間の経過後に遅延実行されるタイプ。 BackgroundJob.Schedule()またはBackgroundJob.Schedule<T>()で登録する際にTimeSpan型で指定した時間を経過した後に実行されます。

ドキュメントとしてはこちらを参照 : Calling methods with delay

コード例

遅延時間はTimeSpan型で指定します。例えば10分後だとこんな指定です。

BackgroundJob.Schedule<MyJobsLib.ICostomJob>(x => x.Execute("Job Executed."), TimeSpan.FromMinutes(10));

ダッシュボード表示

ダッシュボードを見るとこんな感じになってます。

f:id:kiyokura:20170806144051p:plain

Jobテーブル

jobテーブル上のステータスはScheduledになってます。

f:id:kiyokura:20170806144148p:plain

Setテーブル

そしてSetテーブルになにやらエントリが作られています。

f:id:kiyokura:20170806144717p:plain

Score列に1501998606という値が入っています。この値についてドキュメントでの言及を見つけることはできなかったのですが、ソースを見る限りは以下のようです*1

  1. SetテーブルのScore列(float型)には実行時のUNIXエポックからのUTCの秒数が格納されいる
  2. Hangfire Serverがスケジュールをポーリング
  3. 現在日時とScoreから算出される日時を比較し経過していたら実行される

当初は上記 3 のタイミングで、一度キューに入れられた後改めでキューがポーリングされたタイミングで実際に実行される……と思ったのですが、動きを見る分にはどうもそういうわけではなく、スケジュールのポーリングのタイミングで実行まで行われてるように見えました*2

スケジュールのポーリングの設定

既にスケジュールはHangfire Serverからポーリングされて実行されることについては書きましたが、ついでにこのポーリング間隔の設定にも触れておきます。

Delayedの場合のスケジュールは、Hangfire ServerのオプションであるBackgroundJobServerOptionsSchedulePollingIntervalで決められています。これも規定値は15秒で、秒以上の単位でカスタマイズできるようです。この設定についても先に紹介したドキュメントで触れられています。

次回

次は 『Continuations』タイプのジョブ実行について書きます。

*1:ざっと見ただけなので違ってるかもしれません

*2:これについてはソースまで読んでないので定かではないですが、キューに入れて次のキューのポーリングを待つよりも効率的ですし多分そういうことなんだろうと思ってます

Hangfireを使ってみる (4):Jobの種類について:1. Fire-and-forget

Hangfire にはジョブの種類(と実行タイミング)がいくつかあるので、それぞれの動きについて調べてみたのでメモ。

ジョブの種類

Hangfireで利用できるジョブの種類は主に以下があるようです。

No 種別 説明
1 Fire-and-forget 即時実行。すぐに一回実行。
2 Delayed 遅延実行。一定時間経過後に一回実行。
3 Continuations 連続実行。指定のジョブ完了後に一回実行。
4 Recurring 定期実行。指定したスケジュールに沿って定期的に実行。

そのほかに、有償のPro版の場合バッチ実行もできるそうですが、今回はそちらは試す予定がないので割愛。

1-4のタイプについて、それぞれ少し試しながら見てみます。

1. Fire-and-forget

即時実行されるタイプ。 BackgroundJob.Enqueue()またはBackgroundJob.Enqueue<T>()で登録されるとすぐにキューイングされ、サーバーがキューを舐めたタイミング時で一回だけ実行されます。

ドキュメントとしてはこちらを参照 : Calling methods in background

登録後、Hangfire Serverにより実行されるまでの間、ダッシュボードで見るとこの状態です。 f:id:kiyokura:20170806130955p:plain

データベース上では以下のようになっています

Jobテーブル

jobテーブル上のステータスでは、現在キューに積まれていることを示す Enqueued となっています。

f:id:kiyokura:20170806131107p:plain

JobQueueテーブル

実際のキューであるJobQueueテーブルにエントリが作成され、処理待ちの状態になっています。

f:id:kiyokura:20170806131337p:plain

実行について

この後、Hangfire Serverがキューを舐めて処理が開始されます。 Job StorageがSQL Serverの場合、Hangfireサーバーは定期的にキューをポーリングして実行します。このポーリング間隔はSqlServerStorageOptionsQueuePollInterval で指定します。デフォルトでは15秒となってます。

規定値から変更したい場合はHangfire Server側のJob Storageの設定時に、以下のドキュメントのとおりに設定します。

Using SQL Server - Configuring the Polling Interval

次回

次は『Delayed』について調べたことを書きます。

Hangfireを使ってみる (3):HangfireでDIコンテナを使ってみる

前回の続きです。

前回は『ClientはASP.NET Webアプリケーション、ServerをWindowsコンソールアプリケーションで実装』という形の実装に変更しました。

構成としては今回私がやりたい要件*1を満たせそうですが、一点、実現できていないことがありました。 それは、DLLとした切り出したジョブに対してServer(ジョブの実行プロセス)側から設定などの情報を差し込むことです。

例えばジョブの中でDBに接続したいのですが、その際の接続文字列もしくはDbContextなどは実行プロセス側で用意したものを利用したいわけです。

結果を先に言うと、DIコンテナを使うことで実現が可能でした。HangfireはDIコンテナを用いてIoCを実現するインフラがあり、また.NETのメジャーなDIコンテナを簡単に使うための拡張も提供されています。今回はDIコンテナとしてAutofacを使ってみました。

今回はその内容のメモになります。

 

今回やってみること

今回は、DIコンテナを使って『Hangfire Serverのプロセスからジョブ実行時にDB接続文字列を渡す』というのを実現してみます。

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

1. ジョブをインタフェースにと実装に切り分ける

まずはJOBをインタフェースと実装に分離します。さらに『Serverプロセスで実装する何らかのオブジェクトを受け取る』想定の実験(?)のために、設定値を格納するオブジェクト実装するためのインタフェース『IJobConfiguration』を作成し、『CustomJob.cs』はコンストラクタでその実装を受け取って利用するように書き換えます。

// IJobConfiguration.cs 【新規】
namespace MyJobsLib
{
  public interface IJobConfiguration
  {
    string ConnectionString { get; set; }
  }
}
// ICostomJob.cs 【新規】
namespace MyJobsLib
{
  public interface ICostomJob
  {
    void Execute(string message);
  }
}
// CustomJob.cs 【変更(主に★の箇所)】
using System.IO;

namespace MyJobsLib
{
  public class CustomJob : ICostomJob // ★ICostomJobの実装にする
  {
    // ★コンストラクタでIJobConfigurationの実装を受け取り保持することにする
    private IJobConfiguration JobConfiguration = null;

    public CustomJob(IJobConfiguration jobConfiguration)
    {
      JobConfiguration = jobConfiguration;
    }
    
    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);

        // ★実行プロセス側から受け取った情報の接続文字列を使う……想定
        writer.WriteLine(JobConfiguration.ConnectionString);
      }
    }
  }
}

2. 【Hangfire Client側】 キュー登録処理を変更

BackgroundJob.Enqueueメソッドでキューにジョブを登録する際の記述を以下のように変更します。型引数にジョブのインタフェースを指定する記述になります。引数の書き方も変わります。

// HomeController.cs 【変更(主に★の箇所)】
using Hangfire;
using System.Web.Mvc;

namespace HangfireSample01.Web.Controllers
{
  public class HomeController : Controller
  {
    public ActionResult Index()
    {
      // Hangfireでjobをキューに登録
      BackgroundJob.Enqueue<MyJobsLib.ICostomJob>(x => x.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();
    }
  }
}

3. 【Hangfire Server側】 DIコンテナを利用するように変更

最後にServer側です。ここは以下の対応を行います。

  1. IJobConfigurationを実装したクラスを実装
  2. Autofacに対応した拡張をNuGetでインストール
  3. コンテナの登録
IJobConfigurationを実装したクラスを実装する

IJobConfigurationを実装した、Serverのプロセス側からジョブに注入するオブジェクトのクラスを実装します。今回はそのままJobConfigurationとし、コンストラクタで設定値を積み込むようなものを想定したものにしてみました。

// JobConfiguration.cs 【新規】
namespace HangfireSample01.ServerConsole
{
  public class JobConfiguration : MyJobsLib.IJobConfiguration
  {
    public string ConnectionString { get; set; }

    public JobConfiguration()
    {
      ConnectionString = "接続文字列だよ";
    }
  }
}
Autofacに対応した拡張をNuGetでインストールする

NuGetパッケージ Hangfire.Autofac をインストールします。Autofac自体も自動的にインストールされます。パッケージマネージャーコンソールで行う場合は以下です。

PM > Install-Package HangFire.Autofac

コンテナの登録

最後にコンテナに型を登録し、コンテナの情報をHangfireのActivatorに設定する記述を行います。

// Program.cs 【変更(主に★の箇所)】
using Autofac;
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);

      // ★コンテナに型を登録、Activatorに設定
      var builder = new ContainerBuilder();
      builder.RegisterType<JobConfiguration>().As<MyJobsLib.IJobConfiguration>();
      builder.RegisterType<MyJobsLib.CustomJob>().As<MyJobsLib.ICostomJob>();
      GlobalConfiguration.Configuration.UseAutofacActivator(builder.Build());

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

以上でコーディングは完了です。

 

実行して確認する

さっそく実行して確認してみると、ダッシュボードで見えるジョブ名が「ICostomJob.Execute」とインタフェースでの記述に変わっています。またジョブによって作成されるファイルにも、Server側のプロセスから注入された値が反映されていることが分かります。

f:id:kiyokura:20170804012526p:plain

まとめ

HangfireはDIコンテナの利用が想定されていて、メジャーなDIコンテナの実装に対応した拡張も提供されています。(参照:Extensions/IoC Containers

実際に、特に面倒もなくDIコンテナを利用できることがお分かりいただけたのではないでしょうか。

サンプルソースは例によって以下のブランチstep03です。

github.com

これでいったんこのメモのシリーズは終了する予定ですが、この後もHangfireを使っていて調べたこと・ハマったこと等を随時メモしていくと思います。

*1:お仕事で取り組んでる事なので詳細は割愛