How do I: Deploy a Service Bus Enabled Worker Role Windows Azure Application

by Mark Zhou 25. December 2009 06:25

Recently Microsoft has released Windows Azure Tools for Visual Studio November 2009 CTP. which includes the latest Windows Azure SDK binaries and tools for Visual Studio 2010 Beta 2. There are some new features within this tools such as Role based Windows Azure development. Now developers can create a cloud service project that contains multiple roles, there are two mainly role categories supported in this release – Web Role and Worker Role. Web Role means the application consumes the Azure service as a Web application, for example, an ASP.NET Web application, ASP.NET MVC 2.0 application or WCF Web Service application; the Worker Role means the application works as a background process. The following screenshot shows the supported Roles when you create a cloud application in Visual Studio.

image 

Another useful SDK for Windows Azure platform is AppFabric. you can download the AppFabric SDK here. The AppFabric SDK contains the Service Bus assemblies, which enables communication between client and servers regardless the network device and infrastructure. If you want to develop a service using Service Bus, you may have problems to deploy your project to the Windows Azure platform because Microsoft has not released the Visual Studio support in this AppFabric SDK. I am planning to use Service Bus to host a cloud-enabled IM application, I wrote a simple solution to test the functionality. The application works well in the local machine, but unfortunately, when I deploy the package to the Windows Azure, the Worker Role failed to initialize, sometimes the status is “stopped” after deployment. I followed the Windows Azure Training Kit to create the Work Role application, but why this failed to start?

The origin code

Here is the application structure I created:

image

1. The WCF service class and interface (ICloudIMService.cs and CloudIMService.cs):

Code Snippet
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Runtime.Serialization;
  5. using System.ServiceModel;
  6. using System.Text;
  7.  
  8. namespace CloudIM.ServiceContract
  9. {
  10.     // NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "ICloudIMService" in both code and config file together.
  11.     [ServiceContract(Name = "CloudIM.WorkerRole.CloudIMService", Namespace = "cloudim.cloudapp.net/service/v1")]
  12.     public interface ICloudIMService
  13.     {
  14.         [OperationContract]
  15.         string Echo();
  16.     }
  17. }

 

 

Code Snippet
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Runtime.Serialization;
  5. using System.ServiceModel;
  6. using System.Text;
  7. using CloudIM.ServiceContract;
  8.  
  9. namespace CloudIM.WorkerRole
  10. {
  11.     public class CloudIMService : ICloudIMService
  12.     {
  13.         public string Echo()
  14.         {
  15.             return "Hello, World";
  16.         }
  17.     }
  18. }

2. The App.config file:

Code Snippet
  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <configuration>
  3.   <appSettings>
  4.     <add key="ServiceNamespace" value="cloudim" />
  5.     <add key="ServiceIssuer" value="owner"/>
  6.     <add key="ServiceSecret" value="***********************************"/>
  7.   </appSettings>
  8.   <system.diagnostics>
  9.     <trace>
  10.       <listeners>
  11.         <add type="Microsoft.WindowsAzure.Diagnostics.DiagnosticMonitorTraceListener, Microsoft.WindowsAzure.Diagnostics, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
  12.             name="AzureDiagnostics">
  13.         </add>
  14.       </listeners>
  15.     </trace>
  16.   </system.diagnostics>
  17.   <system.serviceModel>
  18.     <services>
  19.       <service name="CloudIM.WorkRole.CloudIMService">
  20.         <endpoint binding="tcpRelayBinding" contract="CloudIM.WorkRole.ICloudIMService"></endpoint>
  21.       </service>
  22.     </services>
  23.   </system.serviceModel>
  24. </configuration>

Then in WorkerRole.cs, host the service:

Code Snippet
  1. static void Main(string[] args)
  2.         {
  3.             // ...
  4.  
  5.             // create the service host reading the configuration
  6.             ServiceHost host = new ServiceHost(typeof(CloudIMService), address);
  7.  
  8.             // create the ServiceRegistrySettings behavior for the endpoint
  9.             IEndpointBehavior serviceRegistrySettings = new ServiceRegistrySettings(DiscoveryType.Public);
  10.  
  11.             // add the Service Bus credentials to all endpoints specified in configuration
  12.             foreach (ServiceEndpoint endpoint in host.Description.Endpoints)
  13.             {
  14.                 endpoint.Behaviors.Add(sharedSecretServiceBusCredential);
  15.             }
  16.  
  17.             host.Open();
  18.  
  19.             // ...
  20.         }

Finally I deploy this package to the Windows Azure platform, but this service is not running.

Afterwards, I start troubleshooting – this could be as long as you think…

First, I doubt the “host” is not opened, but this one is quickly eliminated because no visible runtime exception shows after deployment; then, I try to create an Endpoint for this service using other protocols (http or https) but still failed to initialize after deploy. all the behaviors can work properly in my local development environment.

Finally I think it is the fault of the configuration, so I remove the <system.serviceModel> configuration from app.config, comment the host creation method, then deploy, good! it works now.

I think I have got something to solve this problem! by removing the WCF configuration section, take some change to the WorkerRole.cs, my service is finally running on the cloud. The main point is, do not use configuration to describe the WCF service contract and behavior, alternatively, programmatically create a service contract description and endpoint is the right way to make it work.

The worked version

1. The WorkerRole.cs:

Code Snippet
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Linq;
  5. using System.Net;
  6. using System.Text;
  7. using System.Threading;
  8. using Microsoft.WindowsAzure.Diagnostics;
  9. using Microsoft.WindowsAzure.ServiceRuntime;
  10. using Microsoft.ServiceBus;
  11. using System.ServiceModel;
  12. using System.Configuration;
  13. using System.ServiceModel.Description;
  14. using CloudIM.ServiceContract;
  15.  
  16. namespace CloudIM.WorkerRole
  17. {
  18.     public class WorkerRole : RoleEntryPoint
  19.     {
  20.         private ServiceHost _serviceHost = null;
  21.  
  22.         public override void Run()
  23.         {
  24.             // This is a sample worker implementation. Replace with your logic.
  25.             Trace.WriteLine("CloudIM.WorkerRole entry point called", "Information");
  26.  
  27.             this._serviceHost = GetServiceHost();
  28.  
  29.             try
  30.             {
  31.                 this._serviceHost.Open();
  32.             }
  33.             catch (AddressAlreadyInUseException)
  34.             {
  35.                 return;
  36.             }
  37.  
  38.             while (true)
  39.             {
  40.                 Thread.Sleep(10000);
  41.                 Trace.WriteLine("Working...");
  42.             }
  43.         }
  44.  
  45.         private ServiceHost GetServiceHost()
  46.         {
  47.             string serviceNamespace = ConfigurationManager.AppSettings["ServiceNamespace"];
  48.             string serviceIssuer = ConfigurationManager.AppSettings["ServiceIssuer"];
  49.             string serviceSecret = ConfigurationManager.AppSettings["ServiceSecret"];
  50.  
  51.             Uri address = ServiceBusEnvironment.CreateServiceUri("sb", serviceNamespace, "CloudIM.WorkerRole.CloudIMService");
  52.  
  53.             // create the credential object for the endpoint
  54.             TransportClientEndpointBehavior sharedSecretServiceBusCredential = new TransportClientEndpointBehavior();
  55.             sharedSecretServiceBusCredential.CredentialType = TransportClientCredentialType.SharedSecret;
  56.             sharedSecretServiceBusCredential.Credentials.SharedSecret.IssuerName = serviceIssuer;
  57.             sharedSecretServiceBusCredential.Credentials.SharedSecret.IssuerSecret = serviceSecret;
  58.  
  59.             // Create the Service Host
  60.             ServiceHost host = new ServiceHost(typeof(CloudIMService), address);
  61.             ContractDescription contractDescription = ContractDescription.GetContract(typeof(ICloudIMService), typeof(CloudIMService));
  62.             ServiceEndpoint serviceEndpoint = new ServiceEndpoint(contractDescription);
  63.             serviceEndpoint.Address = new EndpointAddress(address);
  64.             serviceEndpoint.Binding = new NetTcpRelayBinding();
  65.             serviceEndpoint.Behaviors.Add(sharedSecretServiceBusCredential);
  66.             host.Description.Endpoints.Add(serviceEndpoint);
  67.  
  68.             return host;
  69.         }
  70.  
  71.         public override bool OnStart()
  72.         {
  73.             // Set the maximum number of concurrent connections
  74.             ServicePointManager.DefaultConnectionLimit = 12;
  75.  
  76.             DiagnosticMonitor.Start("DiagnosticsConnectionString");
  77.  
  78.             // For information on handling configuration changes
  79.             // see the MSDN topic at http://go.microsoft.com/fwlink/?LinkId=166357.
  80.             RoleEnvironment.Changing += RoleEnvironmentChanging;
  81.  
  82.             return base.OnStart();
  83.         }
  84.  
  85.         public override void OnStop()
  86.         {
  87.             try { this._serviceHost.Close(); }
  88.             catch { }
  89.             base.OnStop();
  90.         }
  91.  
  92.         private void RoleEnvironmentChanging(object sender, RoleEnvironmentChangingEventArgs e)
  93.         {
  94.             // If a configuration setting is changing
  95.             if (e.Changes.Any(change => change is RoleEnvironmentConfigurationSettingChange))
  96.             {
  97.                 // Set e.Cancel to true to restart this role instance
  98.                 e.Cancel = true;
  99.             }
  100.         }
  101.     }
  102. }

2. App.config:

Code Snippet
  1. <?xml version="1.0" encoding="utf-8" ?>
  2. <configuration>
  3.   <appSettings>
  4.     <add key="ServiceNamespace" value="cloudim" />
  5.     <add key="ServiceIssuer" value="owner"/>
  6.     <add key="ServiceSecret" value="***************"/>
  7.   </appSettings>
  8.   <system.diagnostics>
  9.     <trace>
  10.       <listeners>
  11.         <add type="Microsoft.WindowsAzure.Diagnostics.DiagnosticMonitorTraceListener, Microsoft.WindowsAzure.Diagnostics, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
  12.             name="AzureDiagnostics">
  13.         </add>
  14.       </listeners>
  15.     </trace>
  16.   </system.diagnostics>
  17. </configuration>

Here we go, the service status is now ready on the Windows Azure platform. :)

image

Tags:

Add comment




  Country flag
biuquote
  • Comment
  • Preview
Loading


Translate This Page

About Mark

Mark is a developer who works for building base class libraries and tools for developers.

Mark's Awards

Microsoft Community Contributor

Month List

Who visit this site

Recent Comments

Comment RSS