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;
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