Tuesday 20 March 2012

Windows Azure Instance To Instance Communication


If you set Instance count value in configuration for webrole/workerrole your application will run on two instances.The loadbalancer in azure uses some techniques to direct requests to instances.To communicate instance to instance we have to use internal endpoints.
->Create new azure project and add new workerrole project.
->Open  ServiceDefinition.csdef file  add new endpoints for wcf service and for internal communication.

<Endpoints>
      <InputEndpoint name="Rest" protocol="http" port="80" />
    <InternalEndpoint name="InternalRest" protocol="tcp" />
</Endpoints>

->Add new IService interface.
Service Interface:
    [ServiceContract]
    public interface IService
    {
        [OperationContract]
        [WebGet]
        string SayHello(string name);
    }
-> Add new IWorker interface.
   [ServiceContract]
    public interface IWorker
    {
        [OperationContract]
        void UpdateMessage(string message, string roleId);
    }


->Add new Service class and implement IService, IWorker  interfaces.

Service Implementation:
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(IncludeExceptionDetailInFaults = true, InstanceContextMode = InstanceContextMode.Single)]
    public class Service : IService, IWorker
    {
        //Static Message variable.
        public static string Message = "";

        public string SayHello(string message)
        {
            //Set the message.
            Message = message;

            //Get the role id.
            string roleId = RoleEnvironment.CurrentRoleInstance.Id;


            //Notify all instances.
            WorkerRole.NotifyAllNodes(message);

            return "Hello" + message;
        }

        //Update the message.
        public void UpdateMessage(string message, string roleId)
        {
            //Set the message.
            Message = message;

        }

    }

 ->Open WorkerRole.cs class,create serviceHost varaible
 and add CreateServiceHost method to host our Wcf REST service.

//Static channel factory for inter-role communication.
private static ChannelFactory<IWorker> factory;

 //Service host varible.
private ServiceHost serviceHost;


private void CreateServiceHost()
{
   //Url of service.
   Uri httpUri = new Uri(String.Format("http://{0}/{1}", RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["Rest"].IPEndpoint.ToString(), "Rest"));

   //Assign url to service host.
   serviceHost = new ServiceHost(typeof(Service), httpUri);

   //Adding endpoint for REST service.
   ServiceEndpoint ep = serviceHost.AddServiceEndpoint(typeof(IService), new WebHttpBinding(), "");

 //Adding behavior for REST service.
 ep.Behaviors.Add(new WebHttpBehavior() { HelpEnabled = true });


 //NetTcpBinding with no security.
 NetTcpBinding binding = new NetTcpBinding(SecurityMode.None);

//Define an internal endpoint for inter-role traffic.
RoleInstanceEndpoint internalEndPoint = RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["InternalRest"];

//Add internal endpoint.
this.serviceHost.AddServiceEndpoint(typeof(IWorker), binding, String.Format("net.tcp://{0}/InternalRest", internalEndPoint.IPEndpoint));

//Create channel factory for inter-role communication.
WorkerRole.factory = new ChannelFactory<IWorker>(binding);

//Open the service host  to start listening for messages.
serviceHost.Open();

 }
->Create NotifiyAllNodes function to notify the instances.
  internal static void NotifyAllNodes(string message)
        {
            //Get the role id.
            string roleId = RoleEnvironment.CurrentRoleInstance.Id;


                //Gets a RoleInstance object representing the role instance in which this code is currently executing.
                var current = RoleEnvironment.CurrentRoleInstance;

                //Get all instances except the current.
                var endPoints = current.Role.Instances
                                .Where(instance => instance != current)
                                .Select(instance => instance.InstanceEndpoints["InternalRest"]);
                foreach (var ep in endPoints)
                {
                    //Internal endpoint address.
                    EndpointAddress address = new EndpointAddress(String.Format("net.tcp://{0}/InternalRest", ep.IPEndpoint));

                    //Channel for communication.
                    IWorker client = WorkerRole.factory.CreateChannel(address);

                    //Update all the instances.
                    client.UpdateMessage(message, ep.RoleInstance.Id);
                }
         }

->Call CreateServiceHost function in OnStart() method.

If you follow all these steps and upload it then you got error like below.
HTTP could not register URL http://+:80/Rest/. Your process does not have access rights to this namespace (see http://go.microsoft.com/fwlink/?LinkId=70353 for details). Source:System.ServiceModel.
Solution to this problem is find out the port it needs to open and give the workerrole permission to open the appropriate port.
->Create new console project PortDetect and configure Target framework as 3.5.
->Open Program.cs file and add below code which is used to detect the port and assign permission to open the appropriate port.

class Program
 {
   static void Main(string[] args)
    {
            Process.Start("netsh", string.Format("http add urlacl url=http://+:{0}/Service user=everyone listen=yes delegate=yes",
                     RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["Rest"].IPEndpoint.Port.ToString()));
    }
  }

 ->Build the project and get the  executable into you workerrole project.

->Set the Copy to Output Directory property of  executable to Copy Always.
-> Open  ServiceDefinition.csdef file specify PortDetect.exe as a startup task.

  <Startup>
    <Task commandLine="PortDetect.exe" executionContext="elevated" taskType="simple">
    </Task>
  </Startup>

Now you can access the RestService  which is hosted(at port 80) on workerrole.And you can do instance to instance communication.

You can’t understand what instance is updating the Message variable here.You can use blob storage to get these details.Write the data(roleId) to the blob storage in the SayHello and UpdateMessage functions.

Let me know, if you have any feedback. Mail me for source code. Enjoy reading my articles…
sekhartechblog@gmail.com

No comments:

Post a Comment