更新日: 2010 年 5 月 7 日

執筆者: 株式会社 日立システムアンドサービス 研究開発センタ
酒井達明

この記事は、「MSDN プログラミング シリーズ」として発行している技術書籍「Windows Azure アプリケーション開発シリーズ」(日経 BP 社刊) を基に Windows Azure アプリケーション開発時に特にポイントとなるテーマについて解説したものです。

目次

  1. はじめに (本連載に際して)
  2. 2 種類のロールの役割分担を理解しよう
  3. Worker ロールで TCP サービスを公開する
  4. Internal TCP の利用

1. はじめに (本連載に際して)

2010 年 2 月に Windows Azure Platform のサービス提供が開始され、Windows プラットフォームにおいても本格的にクラウド時代が到来したといえましょう。これを受けて、多くの方々が Windows Azure Platform 上で実行されるアプリケーションの開発に着手されているのではないでしょうか?

そうした背景を受け、ここでは、開発現場でも応用可能な実践的なアプリケーション開発の気づきになるよう、実際のコードをまじえて、Windows Azure アプリケーションの開発に必要な様々なテーマをご紹介させていただこうと思います。

なお、本連載では、言語そのもの (C#,Visual Basic,PHP,など) や、.NET Framework の各テクノロジ (ASP.NET,WCF,など) に関する一定レベルの知識を前提として記載していますので、読者の方はこれらの知識を習得した上で本連載をご参照ください。

Note: LINQ,ASP.NET,WCF など、最新の .NET Framework のテクノロジを学習したい方は、MSDN 10 行でズバリ ! を参考にしてください。

ページのトップへ


2. 2 種類のロールの役割分担を理解しよう

MSDN オンラインをはじめとした各種コンテンツでも紹介されていますが、Windows Azure で実行されるアプリケーションには、ASP.NET などの Web アプリケーションをホストする Web ロールと、サービスなどの主に画面を持たないバックエンドの処理をホストする Worker ロールの 2 種類があることはご存知の方も多いでしょう。

この Web ロールと Worker ロールは、正確には、以下のように分類することができます。

Web ロール: IIS でホスティングされるアプリケーションを実行する
Worker ロール: IIS を用いないアプリケーションを実行する

そう、これらの唯一の違いは、「IIS を使うか使わないか」であるという点をおぼえておいてください。

したがって、Web ロールでは IIS 7 で実行可能なタイプのアプリケーションを実行することが可能です。例えば、.NET Framework ベースでは ASP .NET Web アプリケーションはもちろんですが、実は画面を持たない WCF Web サービスもこの Web ロールで実行可能です。

一方、Worker ロールは IIS を利用しない様々なアプリケーションが実行可能です。特に、アプリケーションのステータスが Active の間、Worker ロールは常に実行状態が維持されるため、Worker ロールでは、常駐型のアプリケーションを実行することが可能です。

例えば、キュー (Queue) ストレージに格納されたリクエストを順次取り出して実行するバッチ処理を実現するようなコードを記述する際などに、このロールが利用できます。以下の C# のコードのように、Worker ロールのクラス (RoleEntryPoint クラスから継承されるクラスです) の Run オーバーライド メソッドを実装します。

C#
public override void Run()
{
    try
    {
        //ストレージアカウントの作成
        CloudStorageAccount stAccount =
            CloudStorageAccount.FromConfigurationSetting("StorageConnection");
        //Queueクライアントの作成
        CloudQueueClient qcLog = stAccount.CreateCloudQueueClient();
        //Queueクライアントに対するリトライポリシーの設定(任意)
        qcLog.RetryPolicy = RetryPolicies.Retry(2, TimeSpan.FromMilliseconds(100));
        //メッセージキューの作成
        CloudQueue qLog = qcLog.GetQueueReference("applog");
        qLog.CreateIfNotExist();
        //メッセージインスタンスの作成
        CloudQueueMessage qMsg;

        while (true)
        {
            //メッセージの有無を確認
            qMsg = qLog.GetMessage();
            if (qMsg != null)
            {
                //ログにメッセージを記録
                Trace.WriteLine(qMsg.AsString, "Application");
                //メッセージを消去
                qLog.DeleteMessage(qMsg);
            }
            else Thread.Sleep(3000);
        }
    }
    catch (StorageClientException ex)
    {
        Trace.WriteLine(ex.Message, "Error");
    }
}
 

この例は、キュー (Queue) ストレージに格納されたメッセージを順次処理するコードの例です。

Note: Worker ロールのプロジェクトの作成手順の詳細はここでは述べませんが、この手順については、10 行でズバリ !! Windows Azure の Worker ロール サービス開発 (C#) を参考にしてください。

また、常駐型のサービスだけでなく、プログラム コードを読み出してプロセスを起動 (仕事が終わったらプロセスを終了) することも可能です。また、Worker ロールと言えども、HTTP および TCP の (外部から接続可能な) エンド ポイントを持っているため、任意の WCF サービスをセルフ ホストのコードを記述して Worker ロールとして実行することも可能です。

ページのトップへ


3. Worker ロールで TCP サービスを公開する

Windows Azure の学習をはじめた方にとっては、上述の通り、Web ロールは ASP.NET など、その利用方法や構成方法が比較的に想像しやすいロールかもしれません。しかし、Worker ロールについては、どのようなことが可能か、最初はなかなか想像がつかないロールではないしょうか。そこで、ここでは、TCP による Worker ロールの通信方法 (コミュニケーション) について説明し、Worker ロールを使った実際の構成方法とプログラミングの一部を紹介してみましょう。

まず、Worker ロールで、上述した TCP を利用するには、TcpListener を利用したソケット接続を利用する方法と、WCF を利用する方法があります。ここでは、WCF を利用する例をご紹介しましょう。Worker ロールにエンドポイントを設定するには、Windows Azure にホストするサービスの構成情報に Endpoint に関する情報を設定します。この構成情報を設定するには、Visual Studio で作成されるプロジェクトの ServiceDefinition.csdef を編集してエンドポイントを設定するか、Visual Studio で Worker ロールのプロパティ ページを開き、Endpoint タブ ページでエンドポイント情報を追加します。(下図)

この TCP によるサービスを公開するためのコードは、以下のようになります。

private ServiceHost serviceHost;
public override void Run()
{
   // Bindingの指定
   NetTcpBinding binding = new NetTcpBinding(SecurityMode.None);

   // 内部エンドポイント情報 (上述で設定したエンドポイント) を取得
   RoleInstanceEndpoint externalEndPoint =
       RoleEnvironment.CurrentRoleInstance.
       InstanceEndpoints["HelloTCP"];

   // WCF のサービスホストインスタンスの生成
   this.serviceHost = new ServiceHost(typeof(HelloTCP));

   // WCF のサービスホストにエンドポイントを追加
   var ep = this.serviceHost.AddServiceEndpoint(
                   typeof(IHelloTCP),
                   binding,
                   String.Format("net.tcp://{0}/HelloTCP",
                   externalEndPoint.IPEndpoint));

   // 内部エンドポイント情報をログに出力
   Trace.WriteLine(String.Format("Internal Endpoint is {0}",
       externalEndPoint.IPEndpoint));

   // WCF サービスホストのOpen
   serviceHost.Open();

   while (true)
       Thread.Sleep(300000);
}

ここで注目していただきたいのが、エンドポイント情報に公開するポートを直接記述するのではなく、RoleEnvironment クラスの CurrentRoleInstance プロパティ内にある InstanceEndpoints を参照している点です (上記の太字を参照)。このコードを Development Fabric (開発ファブリック) (※1 下記参照) 上で実行すると、以下のようにエンドポイント情報が出力され、ここではじめて、そのマシンに割り当てられた IP アドレスやポート番号などの情報を確認することができます。

※1: 「Development Fabric (開発ファブリック)」は、ローカル コンピューター上で、デバッグなどの目的で、Windows Azure 上での実行をエミュレーションするための環境です。

このトレース ログの出力 (上図の赤の囲み) を見てわかるように、内部で開かれているポート番号が、実は、前述の構成情報で指定したポート番号とは異なっています。これは、ロード バランサー (負荷分散) を介してサービスが呼び出されるためであり、内部的には別のポート番号が割り当てられているためです。

この例からも想像できるように、Web ロールだけでなく、Worker ロールにおいても、ロード バランサーが有効であることがわかります。

ページのトップへ


4. Internal TCP の利用

Worker ロールでは、上記でみてきた外部に対するポートの開放以外に、Internal Endpoint という特殊な TCP ポートを公開することも可能です。

このポートは、Windows Azure 上のリソースなどコンピューター環境全体を管理する「ファブリック コントローラー」が動的に割り当てるポートで、Worker ロール インスタンス間のコミュニケーションなど、内部の通信に利用することが可能なエンドポイントです。この Internal TCP エンドポイントは、Worker ロールのプロパティ ページで設定することが可能です。

上図の通り、エンドポイントの [Type] 属性を [Internal] (内部) に指定すると、ポートは動的に割り当てられるようになります。(それ以外の項目 (属性) は、通常の外部公開されたポートと同じ方法で設定できます。)

それでは、動的に公開されたポートをどのように発見すればよいのでしょうか? 以下のコードは、Internal TCP ポートを呼び出すサンプル コードです。(このコードは、このポートを設定した環境と同じ環境内で Worker ロールとして動作させます。)

private void SyncRoles(string Message)
{
   //他のインスタンスへメッセージを送信するためのProxy生成
   NetTcpBinding binding = new NetTcpBinding(SecurityMode.None);

   //カレント(自身の)Roleインスタンスを所得
   var current = RoleEnvironment.CurrentRoleInstance;

   //自分のインスタンス以外のロールから[InnerCommunication]の
   //エンドポイントを持つインスタンス一覧を取得
   var endPoints = current.Role.Instances.Where(instance => instance != current)
                   .Select(instance => instance.InstanceEndpoints["InternalCommunication"]);

   //各エンドポイントを取得して、そこにメッセージを送信
   foreach (var ep in endPoints)
   {
       //クライアントチャネルを生成
       ChannelFactory<IInternalCommunicationChannel> channelFactory
           = new ChannelFactory<IInternalCommunicationChannel>(binding,
               new EndpointAddress(string.Format("net.tcp://{0}/InternalCommunication",
               ep.IPEndpoint)));
       IInternalCommunicationChannel channel = channelFactory.CreateChannel();

       //チャネルをオープンし、メッセージを送信
       channel.Open();
       channel.SyncOther(current.Id, Message);
       channel.Close();
   }
}

ここで注目していただきたいのが、コード中ほどの太字の部分です。(ここでは、LINQ to Object を使用してクエリを実行しています。) RoleEnvironment.CurrentRoleInstance.Role.Instances は "同種のロール"、すなわち、"Worker ロール" のインスタンスを列挙しており、このコレクション (Worker ロールのコレクション) に対し公開している Internal Endpoint の一覧を (Select で) クエリして、該当するエンドポイント (今回の場合、同じ環境下の Internal Endpoint) のみを抽出しています。また、抽出されたエンドポイントに対する URI (接続先のアドレス) は IPEndpoint プロパティで取得することができます。これにより、動的に割り当てられたポートを呼び出すことが可能なのです。

ここでは、通信 (コミュニケーション) を中心に実際の Worker ロールの構成方法とプログラミングの例をみていきましたが、このように、実は、Worker ロールでは、さまざまな形態の通信が可能であるため、バッチなど処理に限らず、Web ロールから使用可能な共通的な処理なども Worker ロールとして共通化して連携することが可能になります。

次回は、ここで紹介した Worker ロールと Web ロールの連携を中心に説明します。

ページのトップへ


Code Recipe Windows Azure Platform デベロッパー センター

ページのトップへ