Welcome to EMC Consulting Blogs Sign in | Join | Help

James Saull's Blog

The ethical slacker

Making sure each web server in the farm is serving the same content.

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

Published 15 December 2008 20:37 by James.Saull
Filed under:
Anonymous comments are disabled
Powered by Community Server (Personal Edition), by Telligent Systems