Reputation: 23
I have a simple php code that redirect to random uri from array. However, after a while i've noticed its not evenly load balanced. Can someone suggest tweaking so it will be evenly redirecting between the links ?
<?php
$urls = array('https://a.com',
'http://b.com',
'http://c.com',
'http://d.com',
'https://e.com',
'https://f.com'
);
shuffle($urls);
header('Location: ' . $urls[0]);
exit();
?>
Thanks
Upvotes: 0
Views: 2875
Reputation: 34093
The problem: shuffle()
internally uses the same random number generator as rand()
, which is biased, predictable, and has a small set of possible sequences (about 2^32, if I'm not mistaken).
There are two ways to solve this problem:
Persist some information across requests to guarantee the distribution is even (which is what Guillaume's answer did).
Use an unbiased random number generator, such as random_int()
(PHP 7, though a (currently experimental) polyfill for 5.x is in the works), which will in the aggregate produce an even distribution of outcomes.
For small sample sizes, solution #1 is probably the one you want. Over millions of requests, both should solve the problem.
If you're feeling adventurous, you could combine the two.
/**
* An implementation of shuffle() that uses a CSPRNG to select indices
*
* @param reference $array (pass a reference to an array)
*/
function unbiased_shuffle(&$array)
{
$size = count($array);
for ($i = $size - 1; $i > 0; --$i) {
$r = random_int(0, $i);
if ($r !== $i) {
$temp = $array[$r];
$array[$r] = $array[$i];
$array[$i] = $temp;
}
}
// Reset indices:
$array = array_values($array);
}
Upvotes: 0
Reputation: 2985
Change your header
line to choose a random index as well:
header('Location: ' . $urls[rand(0,count($urls) - 1)]);
Upvotes: -1
Reputation: 126
Evenly and randomly are contradictory. But you can use a stack instead of an array to limit the randomness. Drawback is that you then need storage between script calls. For example :
<?php
$urls=readStack();
if(empty($urls)) {
$urls = shuffle(array('https://a.com',
'http://b.com',
'http://c.com',
'http://d.com',
'https://e.com',
'https://f.com'
));
$url=array_pop($urls);
storeStack($urls);
header('Location: ' . $url);
Where storeStack() and readStack() functions are intended to store and retrieve your stack status from persistent storage. This could be done via file system storage, database access, or a memcache mechanism,... whatever is at your disposal and fast enough for your app. Here is a basic example with file system and serialize :
<?php
define( 'STOREFILENAME', '/tmp/urlstack');
function storeStack($value){
file_put_contents(STOREFILENAME, serialize( $value ));
}
function readStack($varname){
return unserialize(file_get_contents(STOREFILENAME));
}
Upvotes: 2