There is a big buzz around all things Cloud Computing – and obviously Microsoft also wants its part of the cake. We at thinktecture have been spending a lot of time researching the Windows Azure platform offering, including all relevant parts like Windows Azure Compute, Windows Azure Storage, SQL Azure, Claims-based security & identity management and the Windows Azure platform AppFabric with the Access Control Service (ACS) and the Service Bus.
For those people interested into getting more insight into the Windows Azure family, we partnered with DevelopMentor and dm is offering a course “Cloud Computing for .NET Developers: The Windows Azure platform”. Go and check it out.
Note: The approach outlined below currently only works with Internal endpoints, not Input endpoints.
Anyway, let’s hop over to the actual problem I ran into the other day. My goal was to self-host some of my WCF services with HTTP-based endpoints in a Windows Azure worker role – should be easy, eh?
Turned out that it was easy. Once you consider using the Azure API to get the actual endpoint to listen on (which obviously you need to specify/model in the service’s .csdef file), like this:
using System;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.ServiceModel;
using System.Threading;
using Microsoft.WindowsAzure.Diagnostics;
using Microsoft.WindowsAzure.ServiceRuntime;
using Services;
namespace WCFWorkerRole
{public class WorkerRole : RoleEntryPoint{private ServiceHost host;
public override void Run(){Trace.WriteLine("WCFWorkerRole entry point called", "Information");while (true){Thread.Sleep(10000);Trace.WriteLine("Working", "Information");}}public override bool OnStart(){ServicePointManager.DefaultConnectionLimit = 12;DiagnosticMonitor.Start("DiagnosticsConnectionString");
StartWCFService();RoleEnvironment.Changing += RoleEnvironmentChanging;return base.OnStart();}public override void OnStop(){StopWCFService();base.OnStop();
}private void StartWCFService(){IPEndPoint ip = RoleEnvironment.CurrentRoleInstance.InstanceEndpoints[
"WCFService"].IPEndpoint;
Uri baseAddress = new Uri(String.Format("http://{0}", ip));try
{host = new ServiceHost(typeof(HelloService), baseAddress);host.Open();}catch (Exception ex)
{Trace.WriteLine(ex.Message, "Error");
throw;
}Trace.WriteLine("WCF Hello service running...");
}private void StopWCFService(){if (host != null){try
{host.Close();}catch (Exception ex)
{Trace.WriteLine(ex.Message, "Error");
host.Abort();throw;
}}}private void RoleEnvironmentChanging(object sender, RoleEnvironmentChangingEventArgs e){if (e.Changes.Any(change => change is RoleEnvironmentConfigurationSettingChange)){e.Cancel = true;
}}}}
Make sure you start your WCF service in OnStart – Steve has a good explanation why.
When I run the worker role in the dev fabric everything looks fine:
Cool – so let’s deploy it into the cloud.
<17 minutes later… />
Argh – we get an exception when our WCF service wants to start in Windows Azure! What happened?
OK. The root cause of this problem is that Azure creates an “IP-bound Weak Wildcard” HTTP reservation using the IP address of the modeled endpoint. But by default, WCF attempts to bind to the “Strong Wildcard”, leading to a reservation problem as seen in the exception text. Our WCF binding must be compatible with the reservation. Currently, the only known way is to use the Exact hostname comparison mode.
We can do this in code or in config. Let’s – for the sake of simplicity - just use BasicHttpBinding to illustrate this:
var b = new BasicHttpBinding
{HostNameComparisonMode = HostNameComparisonMode.Exact};
Or in config:
<bindings><basicHttpBinding><binding name="basicHttp"hostNameComparisonMode="Exact" /></basicHttpBinding>…
And as I am a big fan of custom bindings this is what I had to do to get my sample from above working with my custom binary-over-http binding:
<bindings><customBinding><binding name="binaryHttp"><binaryMessageEncoding /><httpTransporthostNameComparisonMode="Exact" /></binding></customBinding></bindings>
Hope this helps someone.
Nice article. But don't understand why this wouldn't work with Input endpoints? Is the HTTP reservation different? The external load-balancer would change the request to http://ip:port and hostNameComparisionMode=Exact would still work. Right?
Posted by: Sid | 09/20/2010 at 08:59 PM
Unfortunately, not, nope :( I am sure they will fix it in upcoming releases.
Posted by: Christian Weyer | 09/26/2010 at 11:58 AM
Thanks for publishing this article. I was able resolve my issue. I am however now not able to connect to the service through my client. I am using the following address http://someservice.cloudapp.net/servicename to try and connect. Just gives and error saying
The remote server returned an error: (404) Not Found.
There was no endpoint listening at http://someservice.cloudapp.net/servicename that could accept the message. This is often caused by an incorrect address or SOAP action. See InnerException, if present, for more details.
Do I need to do anything special on the client side
Posted by: Amitesh | 10/04/2010 at 01:01 AM
Hm... did you already try to enable WCF tracing to see what is really going bad...?
Posted by: Christian Weyer | 10/04/2010 at 09:41 AM
As an extension to the sample code, I would suggest to register a handler for the Faulted event of the Service Host; have the Handler set a ManualResetEvent, and have the Run method return if the event gets set. This way, in case of a faulting host, Run will return, causing the Role to be Recycled.
Posted by: Antonello Tesoro | 12/20/2010 at 05:49 PM
has anyone found a fix for this?
I'm trying to listen on http:80 using a WCF service from a worker role in Azure (with an input endpoint). I get the 404 error as mentioned in the comments previously.
Posted by: Krishna Nadiminti | 02/14/2011 at 06:28 AM
Good question - I do not have time currently to try it, but maybe we need to register a URL namespace in a startup task.
Posted by: Christian Weyer | 02/14/2011 at 07:56 AM
I was able resolve my issue. I am however now not able to connect to the service through my client.
Posted by: yeast infection | 03/05/2011 at 03:06 AM