WorkerRole で WCF サービスを BasicHttpBinding を使ってホストしようとしたけど断念

WCF サービスを BasicHttpBinding を使って WorkerRole でホストするサンプルを作成したけど、Windows Azure にデプロイしたら WorkerRole インスタンスの開始に失敗してしまう。例外が発生したらキャッチしてログを出力するように修正して再度デプロイしたら

HTTP could not register URL http://+:7070/. Your process does not have access rights to this namespace

っていうエラーがログに出力された。WCF サービスを開始しようとしてエラーになったっぽい。

WorkerRole が公開するポートを変えたり、バインディングの設定を変えたりと、いろいろ試しつつネットで調べてたら、同じ現象に遭遇した人の投稿を見つけた。だいぶ前のだけど。

Hello, the problem with HTTP is by default, if you're not an administrator (like the user account in Windows Azure), you cannot register HTTP listners if you're using http.sys. You must have an administrator grant you the privilege. TCP doesn't have this problem. So in a Worker Role, you must use TCP endpoints.

WCF http bindings use http.sys under the hook. So you're infected.

WCF の Http 系バインディングはその下で http.sys を使っているけど、WorkerRole では http.sys を使う権限が無い、ってことかな。英語に自信無し。権限が無いんじゃどうしようもないかな。TCP では発生しないから、WCF サービスをホストするなら TCP 使え、ってモデレータの人は書いてるね。おとなしく TCP を使うことにしよう。

今回作成したサンプルを掲載しておく。クライアントは省略。

WorkerRoleWcfSample.Contracts プロジェクト

IGreetingService.cs
using System.ServiceModel;

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

WorkerRoleWcfSample.WorkerRole プロジェクト

WorkerRole.cs
using System;
using System.Diagnostics;
using System.Net;
using System.ServiceModel;
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;

            // 構成の変更を処理する方法については、
            // MSDN トピック (http://go.microsoft.com/fwlink/?LinkId=166357) を参照してください。
            try
            {
                var config = DiagnosticMonitor.GetDefaultInitialConfiguration();

                // ログの設定
                config.Logs.BufferQuotaInMB = 10;
                config.Logs.ScheduledTransferPeriod = TimeSpan.FromMinutes(1);

                // Windows ログの設定
                config.WindowsEventLog.BufferQuotaInMB = 10;
                config.WindowsEventLog.ScheduledTransferPeriod = TimeSpan.FromMinutes(1);

                // 診断ログの設定
                config.DiagnosticInfrastructureLogs.BufferQuotaInMB = 10;
                config.DiagnosticInfrastructureLogs.ScheduledTransferPeriod = TimeSpan.FromMinutes(1);

                // 診断開始
                DiagnosticMonitor.Start(
                    "Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString",
                    config);
            }
            catch (Exception ex)
            {
                Trace.WriteLine("Exception during WorkerRole.OnStart: " + ex.Message);
            }

            RoleEnvironment.Changed += (sender, e) =>
            {
                StopWCFService();
                StartWCFService();
            };
            StartWCFService();

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

        public override bool OnStart()
        {
            return base.OnStart();
        }

        private ServiceHost _host;

        private void StartWCFService()
        {
            const string ENDPOINT_NAME = "Greeting";
            try
            {
                var endpoint = RoleEnvironment.CurrentRoleInstance.InstanceEndpoints[ENDPOINT_NAME].IPEndpoint;
                var address = string.Format("{0}://{1}:{2}",
                    Uri.UriSchemeHttp,
                    endpoint.Address,
                    endpoint.Port);

                _host = new ServiceHost(typeof(GreetingService), new Uri(address));
                _host.Open();
                Trace.TraceInformation("{0} start listen {1}", typeof(GreetingService).Name, address);
            }
            catch (Exception ex)
            {
                Trace.TraceError(ex.Message);
                //throw;
            }
        }

        private void StopWCFService()
        {
            try
            {
                if (_host != null)
                {
                    _host.Close();
                    Trace.TraceInformation("Close {0}", typeof(GreetingService).Name);
                }
            }
            catch (Exception ex)
            {
                Trace.TraceError(ex.Message);
                throw;
            }
        }
    }

    [ServiceBehavior(AddressFilterMode = AddressFilterMode.Any)]
    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>

WorkerRoleWcfSample プロジェクト

ServiceDefinition.csdef
<?xml version="1.0" encoding="utf-8"?>
<ServiceDefinition name="WorkerRoleWcfSample" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition">
  <WorkerRole name="WorkerRoleWcfSample.WorkerRole" vmsize="Small">
    <Imports>
      <Import moduleName="Diagnostics" />
    </Imports>
    <Endpoints>
      <InputEndpoint name="Greeting" protocol="http" port="7070" />
    </Endpoints>
    <LocalResources>
      <LocalStorage name="DiagnosticStore" cleanOnRoleRecycle="false" sizeInMB="8192" />
    </LocalResources>
    <ConfigurationSettings>
    </ConfigurationSettings>
  </WorkerRole>
</ServiceDefinition>
ServiceConfiguration.Cloud.cscfg
<?xml version="1.0" encoding="utf-8"?>
<ServiceConfiguration serviceName="WorkerRoleWcfSample" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceConfiguration" osFamily="1" osVersion="*">
  <Role name="WorkerRoleWcfSample.WorkerRole">
    <Instances count="1" />
    <ConfigurationSettings>
      <Setting name="Microsoft.WindowsAzure.Plugins.Diagnostics.ConnectionString" value="秘密" />
    </ConfigurationSettings>
  </Role>
</ServiceConfiguration>

追記

ServiceDefinition.csdef を次のように修正したら BasicHttpBinding でホストできた。

<?xml version="1.0" encoding="utf-8"?>
<ServiceDefinition name="WorkerRoleWcfSample" xmlns="http://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition">
  <WorkerRole name="WorkerRoleWcfSample.WorkerRole" vmsize="Small">

      <!--追加-->
      <Runtime executionContext="elevated"/>

      ...