When you have a web farm, monitoring can become a little bit more tricky. When you deploy a monitor it is only monitoring the behaviour of the server chosen by the load balancer to respond. You need to create a monitor that is engaging each server directly, bypassing the load-balancer. Obvious, yes. Hard, no.
When you have a web farm to serve your web site you want to make sure that they are all serving the same content. Maybe post-deployment, or just on a regular interval you want to check that one server hasn’t failed to pick up a configuration change or been accidentally interfered with. Or perhaps, when another monitor detects a failure you want to see if the wrong content is displaying across all web servers or just one.
One thing you can do is to create a web page that includes an iFrame of each web server (bypassing the load balancer obviously). This is great for a quick eye-balling and may give the answer you need straight away. Sometimes the difference in a web page is not visual, or too subtle to spot in a hurry…
I feed my habit by coding on the train home and I knocked this PowerShell script together to line by line compare the output of a page across a farm. It is a start and has proved handy a couple of times to spot a problem. It can be vastly improved of course. For example: by repeating the script and passing different HTTP headers (emulating a different browser type) it can see whether Web Server A is serving different content to IE8 to Web Server D. That’d be hard to spot. Anyway, here it is:
$inputDocuments ="http://192.168.0.10/default.aspx","http://192.168.0.11/default.aspx","http://192.168.0.12/default.aspx","http://192.168.0.20/default.aspx","http://192.168.0.21/default.aspx"
# Use this line if you would prefer the script actually ignored known patterns that different in each file
$patternList ="^\s*<input\s+type=""hiden""\s+value=""[^""]+""\s+/>","^[\s]*$"
functionMatchesKnownPattern ($inputLine)
{
ForEach ($patternin$patternList)
{
$pat=[regex]$pattern
if($pat.match($inputLine).Success)
{
return $TRUE
}
}
return $FALSE
}
# For each document create a hash table of line number and the actual line. Exclude all lines that match a pattern known to be different in each document.
$parsedDocuments =@()
ForEach ($inputDocument in$inputDocuments)
{
[regex]::Split((New-Objectnet.webclient).DownloadString($inputDocument),"\r\n") |ForEach-Object -begin{$count=0;$webLines=@{}}-Process{if(MatchesKnownPattern($_)){$webLines[$count]="KNOWN DIFFERENCE";$count++;}else{$webLines[$count]=$_;$count++;}}-End{$parsedDocuments +=$webLines}
}
# Look at each document and see if after all known different patterns are excluded if they have the same line count:
$lineCounts =@{}
ForEach ($parsedDocument in$parsedDocuments)
{
$lineCounts[$parsedDocument.psbase.keys.count]++
}
if($lineCounts.Count-gt 1)
{
Write-Host"Line counts are different"
$documentCount =0;
ForEach ($inputDocument in$inputDocuments)
{
Write-Host"Document ["$inputDocument "] has a count of: "$parsedDocuments[$documentCount].Count
$documentCount++
}
}
# Having agreed that they all have the same line count then go through each line
# and then compare:
# file 1 line 1 with file 2 line 1
# file 2 line 1 with file 3 line 1
# file 3 line 1 with file 4 line 1
# file 4 line 1 with file 5 line 1
# file 1 line 2 with file 2 line 2
# ....
$agreedLineCount =$parsedDocuments[0].Count
for($index=0;$index-lt $agreedLineCount;$index++)
{
for($innerIndex =0;$innerIndex -lt $parsedDocuments.Length-1;$innerIndex++)
{
if($parsedDocuments[$innerIndex][$index]-cne $parsedDocuments[$innerIndex +1][$index])
{
Write-Host"Compared Document ["$inputDocuments[$innerIndex]"] Line Number ["($index+1)"]:"$parsedDocuments[$innerIndex][$index]
Write-Host"To Document ["$inputDocuments[$innerIndex +1]"] Line Number ["($index+1)"]:"$parsedDocuments[$innerIndex +1][$index]
# If the files are horrendously different (i.e. out of step by one line) then you may just want the script exit on first error
#Exit
}
}
}
Write-Host Done