Programming resources...

Finding the fastest server at runtime

What it's for

This script tests a number of servers to see how fast they are running CGI scripts. When you hit "submit", it submits its online form to the fastest server.

It's particularly handy if you're dealing with unreliable servers (like my PC which I've set up as a server, which reacts pretty fast but only when it's turned on...).

You might also like to use it for backup; You can set up an account at a free, but ad-ridden, web-space provider like Virtual Avenue, and have them pick up the slack if your normal server goes down or gets busy.

Ideally I guess you want one server which generally caters to each timezone. For example, Nasu-net, my Japanese ISP, is great as long as you don't try to use it between 9 am and 5 pm Japanese time. Luckily at that time Virtual Avenue's servers are sitting around drinking coffee and waiting for some work to do.

How it works

The script requests an image from each of the servers you would like to test. I use a little yellow bullet , but any image would do. I like to have a visible image so that I can keep an eye on things if I want to, but most of the time I conceal it using CSS.

The image is doled out using a CGI Script (image.cgi). You could just call the image from the server directly, but often a busy server will put requests for CGI scripts to the back of the queue, so your server might send you the test image really fast but then be slow in actually processing your script. If your script usually takes a long time to run on the server, rather than just taking a long time for the server to get around to processing it, you might like to add something to the test script to make it correlate better with your actual script.

When each image loads, it triggers JavaScript's onLoad event, allowing my JavaScript script to make a note of the time it took to arrive.

If you want to give preference to one particular script ("Always use server X unless server Y is more than 10 seconds faster", or whatever), you can give your least favourite servers a "handicap". It just adds however many seconds you say to the time it took to download the image from that particular server.

When you hit "submit", triggering JavaScript's onSubmit event, the script checks which one took least time. (There's a place in the code where you can have it check the form is valid first, and stuff as well.) It then resets the "action" property of the form, before submitting it.

Changing the action property of a form requires JavaScript 1.2 or above. I set the form up so that if the browser hasn't read the script (which it won't if it has less than Javascript 1.2) it will submit the form in the usual way. An alternative way would be to make an invisible copy of the form for each server, and copy the details to the fastest one, then submit that.

The JavaScript

<script LANGUAGE="JavaScript1.2">
<!--
/*
Script by Ed Edgar, November, 2000, http://edochan.com
This script chooses the fastest of several possible servers on which to run a CGI script.
It does this by downloading an image (image.cgi) from each of the servers and timing the download.
When the user clicks the submit button, it sends them to whichever server sent it the picture fastest.
*/

// YOUR SERVER INFO GOES HERE
var servers = new Array ('nasuNet', 'virtualAve', 'edsPC'); //A list of servers. This is only actually used for testing.
var timeTaken = new Array (99998, 99999, 99999); //If the user hits submit before the image from a server has loaded, it will use the number you set here (in thousandths of seconds). So if they're all really slow, it'll use my favourite, Nasu-net.
var handicap = new Array (0,500,0); //If you want to give preference to one particular server, give the others a few thousand thousandths of a second "handicap". For example, Virtual Ave, my second server, has ads, and I reckon it's worth waiting an extra half-second to get a page without them.
var testImages = new Array ('http://www2.nasu-net.or.jp/~ededgar/meta/image.cgi', 'http://www.edochan.com/image.cgi', 'http://www.edochan.dyn.dhs.org/~search/image.cgi'); //The urls of the script you're using to send the test images go here.
var scripts = new Array ('http://www2.nasu-net.or.jp/~ededgar/meta/go20n.cgi', 'http://www.edochan.com/go20e.cgi', 'http://www.edochan.dyn.dhs.org/~search/go20ls.cgi'); //These are the urls of the scripts you want to submit your form to.
// THAT SHOULD BE ALL YOU NEED TO CHANGE

var startTime = getTheTime();

function noteTime(serverNumber) {
// notes the time triggered by the image's onLoad event

   timeTaken[serverNumber] = (getTheTime() - startTime);

}


function getTheTime() {

   var today = new Date();
   return (today.getTime());

}


function reportResults() {
// just for testing. 

   var loop;
   for (loop = 0; loop < timeTaken.length; loop++) {
      alert (servers[loop] + ' took ' + timeTaken[loop] + ' thousandths of a second.');
   }

}


function indexOfLowestNumber (anArray) {
//returns the index number of the lowest number in the array you've sent it.

   var lowestSoFarIndex = 0;
   var lowestSoFarValue = anArray[0];
   var loop;

   for (loop=0 ; loop < anArray.length ; loop++) {
      if (anArray[loop] < lowestSoFarValue) {
         lowestSoFarIndex = loop;
	 lowestSoFarValue = anArray[loop];
      }
   
   }
   return lowestSoFarIndex;

}


function submitForm(formName) {
// Checks which server was fastest and sends it the form.

   var fastestServer;
   var urlOfFastestScript;
   var i;
 
   //reportResults(); //Uncomment this line if you want to test the script with some messages telling you how long each image took to load.

   if (formIsValid(formName) == true) {

      doSomethingBeforeSubmittingForm (formName); 

      for (i=0; i<timeTaken.length; i++) {
          timeTaken[i] =+ handicap[i];
      }
      
      fastestServer = indexOfLowestNumber(timeTaken);
      urlOfFastestScript = scripts[fastestServer];

      //alert ("submitting to " + fastestScript); //Uncomment this line for testing.
      formName.action=urlOfFastestScript; // NB In old browsers this property is read-only.
      formName.submit();

   }
   return false;

}


function formIsValid(formInQuestion) {
// This is where I check all the right boxes have been filled in before submitting the form.
// Your own form validation code goes here. 
// If the form's invalid, make it return false.

         return true;

}

function doSomethingBeforeSubmittingForm (formInQuestion) {
// You can add your own code here if you want to do something before the form is submitted.
// I use this to display a message for the user to read while they're waiting.
// "Searching" or "submitting form" or "just popped out, won't be a minute" or whatever.

}


function writeImageTags() {
// This writes the image tags on the script. 
// You don't have to do this with JavaScript, but it lets you avoid calling up the test images for people who don't have JavaScript enabled and couldn't use the fastest-server-finding functionality anyway.  

   var loop

   for (loop = 0 ; loop < testImages.length ; loop++) {

      var url;
      var imageTag;

      url = testImages[loop] + '?time=' + startTime; //Add the time to the url to make sure the image gets called from the server, not just the browser's cache or that of the proxy server.
      imageTag = '<img src="' + url + '" onLoad="noteTime(' + loop + ');" class="hidden">';
      // alert(imageTag);
      document.writeln(imageTag);

   }

}

var browserCanReadScript = true;

//-->
</script>

You'll need to add the following line to the <form> tag to trigger the script when the form is submitted:

onSubmit="if (window.browserCanReadScript) {submitForm(this); return false;}"

Somewhere on the page you'll also need to call up the picture:

<script language="JavaScript1.2">
<!--
writeImageTags();
//-->
</script>

I have mine at the bottom of the page, and conceal it using Cascading Style Sheets, embedded in the head of the document, like this:

<style TYPE="text/css">
<!--
img.hidden { visibility: hidden }
-->
</style>

The CGI script

Any CGI script will do here as long as it sends a picture back to the browser. If you have a hit-counter on your site you could use that. Or if you have several identical-sized images on your web-page you could have them downloaded from different servers.

#!/usr/bin/perl
#The above depends on your server - on my Windows PC #!C:\Perl\bin\perl

# liberated from http://absolut.banki.hu/~asoka/www.mcp.com/818640000/0-7897/0-7897-0877-9/ch4.htm

$file = 'bullet5.gif'; # or whatever image you want to use

print "Content-Type: image/gif\n\n";
open(GIF,"<$file") || die "Can't open GIF\n"; 
while (read(GIF,$buffer,16384)) {
 print $buffer;
} 

Navigation