In DNS-based load balancing, a website visitor will request a URL (like www.jds.net.au/tech-tips/). Their web browser will do a DNS lookup of the hostname (www.jds.net.au), and the DNS server will return the IP address of one of several web servers; possibly in a round-robin fashion to distribute the load across the servers.

The visitor will usually cache the results of the DNS lookup for a period of time (30 minutes in Internet Explorer), so their requests will all be sent to the same web server (this is a good example of the fact that “load balancing” is not the same thing as “high availability”).

But what if you want to ensure that all your virtual users in a load test are spread evenly across the web servers, or what if you want to have a BPM script check each web server in turn?

This is where you have to get a little sneaky…

If you make a web_url request directly to the IP address it may work (and this would be a very simple Tech Tip), but it won’t work if the web server has been configured to serve content for the web site given by the “Host” HTTP header.

This is what the request might look like in the VuGen replay log…

1
2
3
4
5
6
7
   GET / HTTP/1.1\r\n
   User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT)\r\n
   Accept-Encoding: gzip, deflate\r\n
   Accept: */*\r\n
   Connection: Keep-Alive\r\n
   Host: www.jds.net.au\r\n
   \r\n

The web server’s IP address is contained in the IP protocol layer of the request, while the HTTP protocol layer has the Host header that tells the web server which domain name to serve content from (because virtual hosts on the web server allow multiple domain names to be served from a single IP address).

VuGen will automatically set the Host header of its request to be the same as the “URL=” argument to your web_url function call. But if the web server is relying on the Host header being a domain name instead of an IP address, you are likely to get a 404 error message.

Fortunately it is possible to change this default behaviour of the VuGen HTTP replay engine by using the web functions for changing HTTP headers.

Here is an example where the front page of a popular website is loaded once for each web server:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
Action()
{
  int i;
 
  // 4 web server IP addresses to check
  lr_save_string("61.9.165.68", "IPAddress_1");
  lr_save_string("61.9.165.69", "IPAddress_2");
  lr_save_string("61.9.165.70", "IPAddress_3");
  lr_save_string("61.9.165.71", "IPAddress_4");
  lr_save_string("4", "IPAddress_count"); // setting this parameter lets me use the lr_paramarr_len function
 
  // Original request
  // t=2266ms: 169-byte request headers for "http://www.afl.com.au/" (RelFrameId=1, Internal ID=1)
  //   GET / HTTP/1.1\r\n
  //   User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT)\r\n
  //   Accept-Encoding: gzip, deflate\r\n
  //   Accept: */*\r\n
  //   Connection: Keep-Alive\r\n
  //   Host: www.afl.com.au\r\n
  //   \r\n
 
  // Modified request
  // t=407ms: 169-byte request headers for "http://61.9.165.68:80/host.asp" (RelFrameId=1, Internal ID=1)
  //   GET / HTTP/1.1\r\n
  //   User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT)\r\n
  //   Accept-Encoding: gzip, deflate\r\n
  //   Accept: */*\r\n
  //   Connection: Keep-Alive\r\n
  //   Host: www.afl.com.au\r\n
  //   \r\n
 
  // Stop vugen from automatically generating a Host header (which will be the IP address),
  // and force the Host header to match the domain name instead.
  web_remove_auto_header("Host", "ImplicitGen=No", LAST);
  web_add_auto_header("Host", "www.afl.com.au"); // Note that I am not using web_add_header because I want the header to apply for more than one of the subsequent requests.
 
  // Check website front page for all servers in list
  for (i=1; i<=lr_paramarr_len("IPAddress"); i++) {
 
    lr_save_string(lr_paramarr_idx("IPAddress", i), "TempIPAddress");
 
    lr_start_transaction("Load Front Page");
 
    web_reg_find("Text=AFL - The official site of the Australian Football League - AFL.com.au", "SaveCount=NumMatches", LAST);
 
    web_url(lr_eval_string("www.afl.com.au on {TempIPAddress}"), // Step name has been parameterised so that log will say "web_url("www.afl.com.au on 61.9.165.68") was successful".
      "URL=http://{TempIPAddress}/", 
      "Resource=0", 
      "RecContentType=text/html", 
      "Snapshot=t1.inf", 
      "Mode=HTTP", // Note that if using HTML mode, any off-site resources will use the same Host header value, which will be iterpreted as a bad request by the web server.
      LAST);
 
    if (atoi(lr_eval_string("{NumMatches}")) < 1) {
      // Ensure that "fail open transactions on lr_error_messages" is set
      lr_error_message("Problem for web server with IP address: %s", lr_eval_string("{IPAddress}"));
    }
 
    lr_end_transaction("Load Front Page", LR_AUTO);
  }
 
  // Now turn the implicit header for "Host" back on for any future requests in the script.
  web_revert_auto_header("Host");
 
  return 0;
}

5 comments on “DNS-based load balancing for virtual users

  1. Today I used this when I had a web application that was giving an error message for the first page that was loaded. This was happening approximately 50% of the time.

    I bypassed the load balancer to hit each of the web servers directly, and found that half of the web servers had been misconfigured.

    Using this technique helped speed up the responses of the infrastructure support team, who now had a list of broken servers to look at, instead of having to check all the servers.

  2. very useful info… here’s some additional considerations….

    – if this is being done for the purposes of bypassing a Load Balancer (LB), run at least once in vugen (with full logging “advanced trace” enabled) to ensure the IP address of the LB is not used by the script.

    – absolute references within pages to components will not be replaced with the IP address specified in the modified code and thus should be checked for on playback & re-recorded explictly in URL mode if they exist.

    – also, other hosts that may be referenced in the script may not be replaced with the desired IP address, and thus could send you back to the LB (or other servers) – these are worth checking for too.

    cheers
    Nick

  3. >> “The visitor will usually cache the results of the DNS lookup for a period of time (30 minutes in Internet Explorer), so their requests will all be sent to the same web server”

    I think this is what is sometimes referred to as “sticky” load-balancing?

  4. Tomer had an interesting alternative solution to this problem, which avoids the severe limitation that this solution has for pages that have off-site resources.

    His solution was to modify the hosts file entry for the domain name on each iteration and to turn off DNS caching under Runtime Settings > Internet Protocol > Preferences > Advanced Options, so that VuGen would pick up the new value.

Leave a Reply