http://qs321.pair.com?node_id=651211

onegative has asked for the wisdom of the Perl Monks concerning the following question:

Good day Monks, I am trying to add some load balancing functionality to a script by having a list of 4 hosts that are called either randomly or in a no repeat fashion so that every time the script is called it does not use the same host from the previous call. The script is not a running daemon thus I can't keep a record of the last known host called...

Is there a way to use random() or something else to select a different host without having an external lookup table to know which host was called previously...its not necessary that the same host not be called and therefore a random host would work but I am not sure how to use this function with a complete (FQDN) string...

Any suggestions would be greatly appreciated...

Thanks in advance,
Danny

Replies are listed 'Best First'.
Re: Load Balancing
by Limbic~Region (Chancellor) on Nov 16, 2007 at 15:09 UTC
    onegative,
    With such a small number of hosts, you are very likely to hit the same host repeatedly by grabbing a random host.

    Why do you feel that you can't keep track of which host was previously called? Have you seen Storable and DBM::Deep?

    Another solution, which can be just as problematic as using rand may be to use Time::HiRes. Divide a second into 4 quarters and choose which host to use based on which quarter you are in at runtime.

    Cheers - L~R

      Thanks ya'll, I forgot to mention that my hosts needed to be in a list...also the reason I wanted to not require an external call is because the script calling my hosts doesn't belong to me thus I was attempting to make the code simple and not require additional Modules to be installed/called...

      After some thought/research I have decided upon the following code as my solution...

      #!/usr/bin/perl sub get_hosts { @hosts=("host1.domain.com","host1.domain.com","host1.domain.com","h +ost1.domain.com"); @app = (); while (@hosts) { $rv = int(rand(@hosts)); push @app,$hosts[$rv]; $hosts[$rv] = $hosts[$#hosts]; pop @hosts; } $thosts = "$app[0],$app[1],$app[2],$app[3]"; return $thosts; } print &get_hosts();
      Thanks again everyone for your suggestions...

      Danny
Re: Load Balancing
by dwm042 (Priest) on Nov 16, 2007 at 15:29 UTC
    It has to be remembered that random is not the same as 'no repeat'. Truly random sequences will have long strings of identical repeats. To compensate, you'll need some logic to avoid the previous guess. As an example:

    #!/usr/bin/perl use warnings; use strict; my @server_list = ( 'server1', 'server2', 'server3', 'server4' ); my @picked = ( 0,0,0,0 ); my $picks = shift; foreach ( 1 .. $picks ) { my $i = int(rand( scalar @server_list )); print "Guess = $i\n"; while ( $picked[$i] ) { $i = int(rand ( scalar @server_list )); print "Guess = $i\n"; }; for my $j ( 0 .. $#server_list ) { if ( $j == $i ) { $picked[$j] = 1; } else { $picked[$j] = 0; } } print "Chosen => ", $i, "\n"; }
    And a sample set of results are:

    C:\Code>perl no_repeats.pl 5 Guess = 1 Chosen => 1 Guess = 3 Chosen => 3 Guess = 1 Chosen => 1 Guess = 1 Guess = 1 Guess = 0 Chosen => 0 Guess = 0 Guess = 0 Guess = 3 Chosen => 3
    In the case of a script which runs from scratch each time, it will need some kind of external access to not repeat itself. That could be a file that keeps a number or name of the last called server, or it could be a randomizing daemon that is persistent and is called by the nonpersistent script. It could be a fifo that is set up initially (if it does not exist) and is read by the script before executing a new random number. Once a random number is used, write it into the fifo.

    Update: Added comments on persistence mechanisms.
      EXCELLENT THOUGHT dwm042...I can see what you mean...I will definitely use this concept in the future...

      Thanks,
      Danny
Re: Load Balancing
by scorpio17 (Canon) on Nov 16, 2007 at 14:38 UTC

    Here's a simple way to pick a random string:

    use strict; my @hosts = ( "host1", "host2", "host3", "host4", ); my $pick = $hosts[rand @hosts]; print "host: $pick\n";
Re: Load Balancing
by andyford (Curate) on Nov 16, 2007 at 17:56 UTC

    An easy way to implement what you might need is to use round robin load balancing via DNS. Look Ma, No Code Whatsoever!

    You could get better answers if you specified some background on why you need the requirements that you stated because different load balancing schemes are better for various goals.

Re: Load Balancing
by oha (Friar) on Nov 16, 2007 at 16:28 UTC
    The following sub make a closure with the given array. Every time the closure is called it return a random element different from the previous. It also have no loops so it's fast. Note: it will never start with the first element.
    sub RnR { push my @list, @_; return sub { my $r = 1 + int rand(@list-1); # swap [0] with [$r] my $t = $list[$r]; $list[$r] = $list[0]; $list[0] = $t; return $t; } } # usage my $rnr = RnR(qw(a b c d e f g)); print "next :", $rnr->(), "\n" for (1..20);
    If instead you can't keep the script persistent to each call and you want no repetition, then there is no solution.
    BTW Iff you have a progressive number which increment by one for every call (this can be done on client side, or using the time iff the requests are periodic) you can split the data in two sets (or more). You can then return a random one from the first set if the progressive is even, and a random one from the second set if it's odd.

    Oha

Re: Load Balancing
by andreas1234567 (Vicar) on Nov 16, 2007 at 20:13 UTC
    onegative,

    Remember that you ensure the task in question takes so long to process that it actually justifies to being sent over the network (and back again) + the overhead and complexity it represents.

    --
    Andreas