Wednesday, November 4, 2009

Multicasting in c#

This has been one topic that had me going around in circles for quite some time. Not that it was difficult to find sources that helped me. But it was the sheer difference in approaches that really frustrated me.
I'm writing this hoping that it saves someone a lot of head ache when they try multicasting. I'm going to refrain from explaining what is multicasting et al. There are excellent resources available on the internet on the topic. I will focus on the problem and the resolution here.

Problem Statement
Whenever a cache entry is updated in a node in the network, it must update its peers of the cache update. This will ensure all nodes have current cache data.

Product perspective
The nodes are the different web/app servers in the load balanced/clustered environment and a central cache server that keeps track of all updates to the cache entries.

High level solution
Multicasting will be used to transmit cache updates(all the app servers will do this) through a multicast group channel (something like a radio frequency) and anyone listening in on this channel (the central cache server in this case) will be able to pickup the transmissions.
The cache server transmits a 'pulse' signal. The peers will look for this signal to ensure that the central cache service is alive and not down.

Problem with multicast code found online
As is stated in the beginning many sources available online provide many different ways of multicasting. The problem i faced with most of the code was that it did not allow two different processes from the same machine to transmit into the multicast group.

Sending
So here is the code that finally allowed two processes to transmit into the same multicast group from


private void send(string strMessage)
{
Socket server = new Socket(AddressFamily.InterNetwork,
SocketType.Dgram, ProtocolType.Udp);
IPEndPoint iep = new IPEndPoint(IPAddress.Parse("224.237.248.237"), 5000); //IP Address and portnumbers are personal choices subject to MCast rules
byte[] data = Encoding.ASCII.GetBytes(strMessage);
try
{
server.SendTo(data, iep);
}
finally
{
server.Close();
}


}

Note: this code allows you to send messages without requiring elaborate steps like joining, socket binding etc., as suggested by some resources on the net. This cleared a major bottleneck for me

Listening
Receiving messages is mostly a consistent affair and many resources on the net are spot on when it comes to the code required to do this.
However, coming to our requirement, if we have a need to support multiple processes listening in on the same multicast group, there is no easy way out of this.
First let me present the fundamental code to listen into a multicast group

private void Listen()
{

UdpClient listener = new UdpClient(5001);
IPEndPoint groupEndPoint = new IPEndPoint(IPAddress.Parse("224.237.248.237"), 5001);
string strMsgRcvd =
string.empty;
try
{
listener.JoinMulticastGroup(
IPAddress.Parse("224.237.248.237"));
while (true)
{
byte[] bytes = listener.Receive(ref
groupEndPoint);
strMsgRcvd = Encoding.ASCII.GetString(bytes, 0, bytes.Length));
//do what you must with the message received.
}

}
catch (Exception e)
{
//log the error and proceed.
}
finally
{
listener.Close();
}

}


The above code will allow only one process to bind to the socket. Whenever another process attempts to bind to the same 5001 socket, .NEt throws an exception that only one process can bind to this socket.

There is no quick way to overcome this difficulty. This is a requirement in our case since we wanted to allow more than one process in our machine to listen in to the pulse signal from the cache service. Without the pulse signal the peer processes will not know if there exists a live central cache service to receive and process the outgoing messages sent by the peer nodes.

Here is an approach by which we can overcome this difficulty. I'm not insisting this is the best way, but this is one way of working around the problem.
Using a Pulse file
The above code is written into an independed program/windows service that writes the live status of the central cache service to a file in the local file system in a pre-determined location. Let us call this file a pulse file.
Let this pulse file have the name pulse.txt
The listener will over-write a '1' into this pulse.txt whenever it receives a pulse signal. This way the pulse.txt file is constantly 'touched' and the same value '1' over-written into it.
The independent processes that wish to be aware of the pulse status of the central service,looks for the pulse.txt file in the pre-determined location and checks the last updated timestamp property of the file (or alternatively instead of 1, the timestamp itself could be written into the file)

Once the last updated timestamp is retrieved from the file, the process determines the status of the remote cache service based on its tolerance. For instance

if((DateTime.Ticks - ObjLastAccessedDateTime.Ticks)/TimeSpan.TicksPerSecond < style="color: rgb(51, 102, 255);">else
{
// central service is not alive so throw an exception
throw new Exception("Please check whether the central cache servie is running");
}

this way we have a system by which more than one process that transmits and receives on the same multicast group in a system.

Foot note: this allowed me to run multiple dedicated web apps on the same box with different resource allocations (mem/cpu etc.,) to prioritize clients requirements at the same time sharing common infrastructure services.

0 comments:

Post a Comment

 

My Blog List

Site Info

Followers