WebロールとWorkerロールの連携(疎結合編)

2013年4月16日 | By TatsuakiSakai | Filed in: Micosoft Azure.
  1. はじめに

    なんとかこの記事も続いています。

    回を重ねるごとに「お約束の準備」に関する記述を割愛することができるようになり、より本題に集中できるようになってきました。

    今回は、「WebロールとWorkerロールを疎結合連携させてみる」というテーマでご紹介したいと思います。

    WebロールとWorkerロールを疎結合で連携するにはいくつかの手段があります。

    最初の方法は、WebロールからのリクエストをWorkerロールへQueueを用いて送信し、Workerロールが順次リクエストを処理していく方法です。

    そしてもう1つ方法は、TableやSQLデータベースに処理すべきデータを蓄積しておき、WorkerロールがWebロールの状態とは独立して処理を継続する方法があります。

    前者は緩やかに相互が連携し、後者はバッチジョブを実行するようなイメージで捉えていただいてよいかと思います。

    今回は、前者の緩やかな連携をQueueストレージで実現する方法をご紹介します。

    ほかにもサービスバスのメッセージを利用する方法がありますが、この方法については過去の記事でご紹介しておりますので、そちらをご参照ください。

    それでは、実際にアプリケーションを作成していきましょう。

    すでに、WebロールおよびWorkerロールの作り方は第2回第3回でご紹介しましたし、今回利用するQueueストレージも、ストレージ利用の準備自体は前回ご紹介したばかりです。

    今回からこの記事を読まれた方は、第2回以降から順番にアプリケーションを作成していくと、これからお話しする前提知識を理解することが可能ですので、併せてご覧ください。

     

  2. 各ロールの準備

    それでは、ソリューションを作成しておきましょう。

    任意の名称でWebロールとWorkerロールを1プロジェクトずつ含むソリューションを作成します。

    そして、各プロジェクトにNuGetからStorage Client 2.0をインストールしておきます。

    次に、クラウドサービス・プロジェクトの「ロール」下にあるWebロールおよびWorkerロールのプロパティを編集し、任意の名前でストレージの接続文字列を作成します。

    この場合、実際のWindows Azureストレージを利用しても良いですし、ストレージエミュレーターを利用しても構いません。

    ただし、WebロールおよびWorkerロールの双方が同じストレージを参照するように接続文字列を設定してください。

    これで準備は完了です。

     

  3. Workerロールのインプリメント

    それではまず、Workerロールからインプリメントしていきましょう。

    今回は、Webロールから送信されたメッセージをWorkerロールで受信し、何らかの処理を実行するというサンプルです。

    Queueの送受信にフォーカスしてコードをご紹介したいため、受信したメッセージとトレースログに出力するという大変シンプルな構成にします。

    実際には、トレース出力の代わりに何らかの業務ロジックを実行することが一般的です。

     

    それでは実際にインプリメントしていきましょう。

    まず、usingディレクティブに以下の宣言を追加しておきます。

     

     

    using Microsoft.WindowsAzure.Storage;
    using Microsoft.WindowsAzure.Storage.Auth;
    using Microsoft.WindowsAzure.Storage.Core;
    using Microsoft.WindowsAzure.Storage.Queue;
    using Microsoft.WindowsAzure.Storage.RetryPolicies;
    using Microsoft.WindowsAzure.Storage.Shared;


     

    そして、以下の2つのメソッドを追加します。

     

    private  CloudQueue CreateQueueEnvironment(string queuename)

    {

    //ストレージアカウントの作成
    CloudStorageAccount myAccount =

    CloudStorageAccount.Parse(
    RoleEnvironment.GetConfigurationSettingValue(“ConnectionString1”));

    //QueueClientの作成

    CloudQueueClient myQueueClient = myAccount.CreateCloudQueueClient();

    //リトライポリシーの設定

    myQueueClient.RetryPolicy

    = new LinearRetry(TimeSpan.FromMilliseconds(100), 2);

    //指定された名称のQueueの参照を取得

    CloudQueue newQueue = myQueueClient.GetQueueReference(queuename);

    //Queueが存在しない場合は新規に作成

    newQueue.CreateIfNotExists();

    //作成されたQueueインスタンスを返す

    return newQueue;

    }

     


    private void TryRecieveMessage(CloudQueue myQueue)

    {

    //Queueからメッセージを受信

    CloudQueueMessage msgRecieve

    = myQueue.GetMessage(TimeSpan.FromMilliseconds(100));

    //メッセージが取得できた場合

    if (msgRecieve != null)

    {

    //メッセージの内容をトレースログに出力

    Trace.WriteLine(String.Format(“[{0} {1}] メッセージ受信:{2}”

    , DateTime.UtcNow.ToShortDateString()

    ,DateTime.UtcNow.ToLongTimeString(),

    msgRecieve.AsString), “Information”);

    //メッセージの処理が完了した場合、再可視化防止のためメッセージを削除

    myQueue.DeleteMessage(msgRecieve);

    }

    }

     

    1つはストレージアカウントの作成とQueueクライアント、Queueが存在しない場合に新規にQueueを作成するメソッドです。

    アカウントの作成とQueueClientの作成、そしてリトライポリシーの設定は、前回Blobのアクセス方法をご紹介した時の内容とほぼ同じです。

    BlobをQueueに置き換えるだけでQueueClientの作成まではほぼ同じコードの構成となります。

    最後に、Queueの参照を取得し、存在しない場合には新規作成をする部分はコンテナに対する操作とほぼ同じであることがわかります。

    このように、BlobとQueueはどちらかの利用法を覚えておけばもう一方の利用法も容易に理解できるようにAPIが構成されています。

    もう1つのメソッドは、メッセージの受信を試みて、メッセージが受信できた場合にTraceに内容を出力するメソッドです。

    このメソッドのTraceの呼び出し部分を適宜置き換えることで、業務ロジックを呼び出すように書き換えることができます。

    メッセージの受信はGetMessageを呼び出します。

    引数は必須ではありませんが、受信のタイムアウト時間を指定しています。

    メッセージが受信できればCloudQueueMessageのインスタンスが返されます。

    GetMessageで取得されたメッセージは一定時間Queueの中で非表示の状態になります。

    一定時間内に処理が完了しない場合、メッセージの非表示状態が解消され、再びQueueからメッセージを取り出せるようになります。

    これは、メッセージ取出し後に処理に失敗した場合やサービス自体の障害で処理続行が不可能になった場合でも、メッセージの消失を防ぐためです。

    そのため、処理が完了したらDeleteMessageでメッセージを消去しておく必要があるのです。

    最後に、Runメソッドからこれら2つのメソッドを呼び出す記述を追加してWorkerロールのインプリメントは完了です。

    Runメソッドの内容は以下の通りです。

     

    public override void Run()

    {

    //Queue作成メソッドの呼び出し

    CloudQueue myQueue = CreateQueueEnvironment(“loosecouple”);

    while (true)

    {

    //メッセージ受信を試みる

    TryRecieveMessage(myQueue);

    //5秒間待機

    Thread.Sleep(5000);

    }

    }

     

     

  4. Webロールのインプリメント

    続いて、Webロールのインプリメントをしていきましょう。

    まずは、Webのデザインを以下のようにしておきます。

    ボタンを1つ貼り付けて、ボタンに対するイベントをハンドルするようにしておきます。

    デザイン側はこれで完了です。

    次にコードをインプリメントしていきます。

    まず、usingディレクティブに以下の宣言を追加します。

     

    using Microsoft.WindowsAzure;

    using Microsoft.WindowsAzure.Diagnostics;

    using Microsoft.WindowsAzure.ServiceRuntime;

    using Microsoft.WindowsAzure.Storage;

    using Microsoft.WindowsAzure.Storage.Auth;

    using Microsoft.WindowsAzure.Storage.Core;

    using Microsoft.WindowsAzure.Storage.Queue;

    using Microsoft.WindowsAzure.Storage.RetryPolicies;

    using Microsoft.WindowsAzure.Storage.Shared;

     

    Workerロールと異なり、Webロールとして実行されるアプリケーションは、通常のASP .NETアプリケーションですので、Windows Azureで必要なAPIに対する参照が含まれていません。

    そのため、Microsoft.WindowsAzureや、ServiceRuntimeなどの宣言も追加が必要です。

    次に、Workerロール同様、ストレージアカウントからQueueClientを生成するメソッドを追加します。

    このコードはWorkerロールで利用したものと全く同じものです。

     

    private CloudQueue CreateQueueEnvironment(string queuename)
    {

      //ストレージアカウントの作成

    CloudStorageAccount myAccount =

    CloudStorageAccount.Parse(

    RoleEnvironment.GetConfigurationSettingValue(“ConnectionString1”));

    //QueueClientの作成

    CloudQueueClient myQueueClient = myAccount.CreateCloudQueueClient();

    //リトライポリシーの設定

    myQueueClient.RetryPolicy

    = new  LinearRetry(TimeSpan.FromMilliseconds(100), 2);

    //指定された名称のQueueの参照を取得

    CloudQueue newQueue = myQueueClient.GetQueueReference(queuename);

    //Queueが存在しない場合は新規に作成

    newQueue.CreateIfNotExists();

     //作成されたQueueインスタンスを返す

    return newQueue;

    }

     

    続いて、メッセージを送信するメソッドを追加します。

     

     private void SendMessage()

    {

      //Queueの作成

    CloudQueue myQueue = CreateQueueEnvironment(“loosecouple”);

    //新しいメッセージの生成

    CloudQueueMessage postMessage = new CloudQueueMessage(

    String.Format(“Message from [{0}] sent at {1} {2}”,

    RoleEnvironment.CurrentRoleInstance.Id

    , DateTime.UtcNow.ToShortDateString()

    , DateTime.UtcNow.ToLongTimeString()));

    //Queueにメッセージを追加

    myQueue.AddMessage(postMessage);

    }

     

     

    Queueにポストするメッセージは、CloudQueueMessageオブジェクトとして作成します。コンストラクタの引数で文字列を渡すことでメッセージ本文を同時に設定しています。

    デフォルトコンストラクタでメッセージを作成した場合、SetMessageContentメソッドを実行して後からメッセージ本文を設定することが可能です。

    メッセージの送信はCloudQueueオブジェクトのAddMessageを実行し、送信したいメッセージを引数に与えます。

    これでメッセージがQueueに追加されます。

    最後に、イベントハンドラをインプリメントしてWebロール側のインプリメントは完了です。

     

     

    protected void Button1_Click(object sender, EventArgs e)

    {

    SendMessage();

    }

     

  5. プロジェクトの実行

    それでは、コンピュートエミュレーター上でプロジェクトを実行してみましょう。

    Webロールが起動されたら、ページ内のボタンを続けて2~3回クリックします。

    同時に、コンピュートエミュレーターのUIを起動して、Workerロールのログを見てみましょう。

     

     

     

    スクリーンショットでは少々見づらいと思いますので、ログをテキストで抜き出してみましょう。

     

    Information: [2013/04/16 6:29:13] メッセージ受信:Message from [deployment18(332).LooseCouple.PollingWeb_IN_0] sent at 2013/04/16 6:29:09

    Information: [2013/04/16 6:29:18] メッセージ受信:Message from [deployment18(332).LooseCouple.PollingWeb_IN_0] sent at 2013/04/16 6:29:11

     

    メッセージはちょうど5秒間隔で受信されています。一方、メッセージの送信間隔は2秒間隔であったことがわかります。

    このことからも、WebロールとWorkerロールが独立して実行され、各々のタイミングで処理を実行していることがわかります。

    第1回の記事でもご紹介しましたが、このようなアーキテクチャにしておくことで、WebロールおよびWorkerロールの双方で負荷に応じて処理能力を調整することが可能です。

     


Comments are closed here.