WorkerRole で WCF サービスを NetTcpBinding を使ってホストする手順

はじめに

Windows Azure の WorkerRole で、WCF サービスを NetTcpBinding を使ってホストできたので、その手順をメモしておく。

ネット上で見つかるサンプルはセキュリティを無効にしているものばかりだけど、それだと実戦で使えないので、SSL over TCP を使うようにした。

証明書を用意

  1. SSL over TCP に必要な、サーバー証明書を用意する。
  2. 作成した証明書は Windows Azure にアップロードする。

Cloud プロジェクトを作成

  1. WorkerRoleWcfSample という名前で作成。
  2. WorkerRole プロジェクトを追加。
    • 名前は WorkerRoleWcfSample.WorkerRole に変更。

サービスコントラクトを作成

  1. ソリューションに WorkerRoleWcfSample.Contracts プロジェクトを追加。
  2. サービスコントラクトを定義する。
IGreetingService.cs
using System.ServiceModel;

namespace WorkerRoleWcfSample.Contracts
{
    [ServiceContract]
    public interface IGreetingService
    {
        [OperationContract]
        string Greet(string name);
    }
}

サービスクラスとWorkerRole の作成

WorkerRoleWcfSample.WorkerRole プロジェクトで、サービスクラスと、WCF をホストする WorkerRole を作成する。

WorkerRole.cs
using System;
using System.Diagnostics;
using System.Net;
using System.Security.Cryptography.X509Certificates;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.Threading;
using Microsoft.WindowsAzure.Diagnostics;
using Microsoft.WindowsAzure.ServiceRuntime;
using WorkerRoleWcfSample.Contracts;

namespace WorkerRoleWcfSample.WorkerRole
{
    public class WorkerRole : RoleEntryPoint
    {
        public override void Run()
        {
            // 同時接続の最大数を設定
            ServicePointManager.DefaultConnectionLimit = 100;

            try
            {
                // ログの設定
                var config = DiagnosticMonitor.GetDefaultInitialConfiguration();
                config.Logs.ScheduledTransferPeriod = TimeSpan.FromMinutes(1);
                DiagnosticMonitor.Start(
                    "Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString",
                    config);

                // バインディングを作成
                var binding = new NetTcpBinding();
                binding.Security.Mode = SecurityMode.Transport;
                binding.Security.Transport.ClientCredentialType = TcpClientCredentialType.None;

                // ベースアドレスを作成
                var tcpAddress = string.Format(
                    "net.tcp://{0}",
                    RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["Greeting"].IPEndpoint);
                var httpAddress = string.Format(
                    "http://{0}",
                    RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["Mex"].IPEndpoint);

                var host = new ServiceHost(
                    typeof(GreetingService),
                    new Uri(tcpAddress),
                    new Uri(httpAddress));

                // サービスのセキュリティで使う証明書を設定
                // Thumbprint は Windows Azure にアップロードした証明書のやつ
                host.Credentials.ServiceCertificate.SetCertificate(
                    StoreLocation.CurrentUser,
                    StoreName.My,
                    X509FindType.FindByThumbprint,
                    "C0B68C37B4CF8DCA0F56C16A3891BBF1A0906009"
                );

                // サービスのエンドポイントを登録
                host.AddServiceEndpoint(
                    typeof(IGreetingService),
                    binding,
                    "");

                // サービスのメタデータを公開
                host.Description.Behaviors.Add(new ServiceMetadataBehavior()
                {
                    HttpGetEnabled = true
                });
                host.AddServiceEndpoint(
                    typeof(IMetadataExchange),
                    MetadataExchangeBindings.CreateMexHttpBinding(),
                    "mex");
                
                host.Open();
            }
            catch (Exception ex)
            {
                Trace.TraceError("Exception during WorkerRole.OnStart: {0}", ex.Message);
            }

            while (true)
            {
                Thread.Sleep(10000);
                Trace.TraceInformation("Working");
            }
        }
    }

    public class GreetingService : IGreetingService
    {
        public string Greet(string name)
        {
            var result = string.Format("Hello, {0}!", name);
            Trace.TraceInformation(result);
            return result;
        }
    }
}
App.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.diagnostics>
        <trace>
            <listeners>
                <add type="Microsoft.WindowsAzure.Diagnostics.DiagnosticMonitorTraceListener, Microsoft.WindowsAzure.Diagnostics, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
                    name="AzureDiagnostics">
                    <filter type="" />
                </add>
            </listeners>
        </trace>
    </system.diagnostics>
</configuration>

クライアントの作成

  1. WorkerRoleWcfSample.Client プロジェクトを作成する。
    • プロジェクトの種類はコンソールアプリケーション。
Program.cs
using System;
using System.Configuration;
using System.ServiceModel;
using WorkerRoleWcfSample.Contracts;
using System.Security.Cryptography.X509Certificates;

namespace WorkerRoleWcfSample.Client
{
    class Program
    {
        static void Main(string[] args)
        {
            // アプリケーション構成ファイルからエンドポイントアドレスを取得
            var address = ConfigurationManager.AppSettings["address"];

            var binding = new NetTcpBinding();

            // サーバー証明書を使ったトランスポートセキュリティを有効にする
            binding.Security.Mode = SecurityMode.Transport;
            binding.Security.Transport.ClientCredentialType = TcpClientCredentialType.None;

            var client = ChannelFactory<IGreetingService>.CreateChannel(
                binding,
                new EndpointAddress(
                    new Uri(address),
                    EndpointIdentity.CreateDnsIdentity("AzureSample")
                )
            );

            var result = client.Greet("Kagawa");

            Console.WriteLine(result);
            ((IClientChannel)client).Close();

            Console.ReadLine();
        }
    }
}
  • EndpointAddress 作成時に DNS 名が必要になる。
    • DNS 名はサーバー証明書の名前を指定したらうまくいった。
App.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <appSettings>
        <add key="address" value="net.tcp://azuresample.cloudapp.net:7070/"/>
    </appSettings>
    <system.net>
        <defaultProxy enabled="true" useDefaultCredentials="true"/>
    </system.net>
</configuration>
WorkerRole のプロパティを変更
  1. 証明書タブを選択。
  2. Windows Azure にアップロードした証明書の情報を追加する。
  3. サービス構成を『 Cloud 』に切り替える。
  4. 設定タブを選択。
  5. 接続文字列『Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString』の値を Azure Storage のキーに変更する。

あとは Windows Azure にデプロイするだけ

Visual Studio から Windows Azure にデプロイする。クライアントからサービスにアクセスできれば成功。