Multithreaded XML Document for Read/Write Access


Summary

This article describes a process for using a ThreadPool within a windows service that monitors other services. It also shows how to allow multithreaded read/write access to an XmlDocument, that acts as persistent storage, using a Mutex. The windows services that I'm monitoring here just happen to be web applications based on a Java application server. So, what I do is check a page running within the service to see if (a) it doesn't timeout or (b) doesn't match the text that it expects to see. If that is the case, I assume the service is down, or in an error state and I restart the service.

ServiceController oSC;
oSC =
new ServiceController(sService);

I can then do a

oSC.Start();
 
The Monitor

My input XML file, called focus.xml, looks like this:

<?xml version="1.0" encoding="utf-8"?>
<
network>
<
server>
<
IP>192.168.1.120</IP>
<
service>
<
name>Site A</name>
<
webpage>http://sitea.myserver.com/www/sample.jsp</webpage>
<
verify>Please click here</verify>
<
retries>0</retries>
</
service>
...
</server>
</
network>

The key here is that each server can have multiple services that I monitor. I read this XmlDocument and in a loop, call the Http monitoring service, passing in the text of the Name, Webpage and Verify text nodes. Retries is the number I increment if there is a timeout. What I do is set the the service on a 5 minute polling interval and on the second retry, I restart the service. I pass all these parameters in as a delimited string, because a ThreadPool callback function can only take 1 parameter ( I suppose I could use a string[] as well ! ). The service uses a Timer that runs on a 5 minute interval and runs this code:

while
(reader.Read())
{
if (reader.Name.Equals("name") && reader.NodeType == XmlNodeType.Element)
{
sight += reader.ReadElementString("name") + "|";
//Debug.WriteLine(sight);
}
if (reader.Name.Equals("webpage") && reader.NodeType ==XmlNodeType.Element)
{
sight += reader.ReadElementString("webpage") + "|";
//Debug.WriteLine(sight);
}
if (reader.Name.Equals("verify") && reader.NodeType ==XmlNodeType.Element)
{
sight += reader.ReadElementString("verify");
Debug.WriteLine(sight);
ThreadPool.QueueUserWorkItem (
new WaitCallback (VerifyHttpService), sight);
sight = "";
}
}

A ThreadPool is nice for threads that are relatively homogeneous. It is a nice simple structure and is available with out instantiation as a static class.

Http Monitor

Now lets look at the Http monitor. In the VerifyHttpService method I split the string into its components.

private static void VerifyHttpService(string sSite)
{
//test the service
//
string[] retina = sSite.Split('|');
string status = Scanners.CheckWebPage(retina[1],retina[2]);
..

Then what happens is that another class, called Scanners makes the HttpWebRequest and returns a status code. If the status is timeout, then the thread has to read the RETRIES count from the XmlDocument to see if it has already timed out once before. I use an XPath query on the document.

iTest =PathEval.pathXeval(getPath() + @"\focus.xml", @"/network/server/service[name='" +
retina[0] +
"']/retries");

This query can both read and update the XmlDocument. If there is a timeout, and RETRIES is 0, then it increments it to 1. If it's one, it sets it back to 0 ( because 1 means it's going to restart the service ). But remember, there can be multiple services on the server and hence multiple threads writing to the XmlDocument! And these threads are going to be attacking the XmlDocument all in the same millesecond!

How do we handle this? With a Mutex of course! A mutex acts as a queue that pools up the requests so there is no collision! So the PathEval.pathXeval() method looks like:

...
public static int pathXeval(string xDoc, string Xpath, int newVal)
{
int retn = 0;
try
{
//Load the XML document
//here the mutex queues up the requests from the threads
mut.WaitOne();
XmlDocument doc =
new XmlDocument();
doc.Load(xDoc);
//Select the nodes
XmlNodeList resultNodes = doc.SelectNodes(Xpath);
Debug.WriteLine("matching nodes found..." + resultNodes.Count);
//Print the node values
int iCount = 0;
foreach(XmlNode aNode in resultNodes)
{
Debug.WriteLine("Node {0} value: {1}" + ++iCount, aNode.InnerText);
if (newVal!=-1)
{
aNode.InnerText= newVal.ToString();
doc.Save(xDoc);
}
retn = Convert.ToInt32(aNode.InnerText.ToString());
}
mut.ReleaseMutex();
}

So the Mutex lets me use an XmlDocument as a mini-database!

Next Steps

Next project will be for me to create a web service with a global XmlDocument to be a simple data store that can be updated and read by multiple client web service consumers!

Next Recommended Readings