业务场景
有一堆组,每组成员不定时进出组,且组内成员会有状态,要求实现请求过来时,轮询分配给特定状态的成员。于是想到了经典的Round-Robin算法,根据业务写了个demo,如下:
<?php
/**
* Class RoundRobin
* 不公平循环分配实现算法
* 原理:
* 通过取模实现轮循调度
* 数据结构:
*
* 权限人员集合 [1,2,3,4,5]
* 初始轮循指针位置:4
*
* 测试操作:
* 1.人员状态变更
* 2.组内人员变更 (不公平)
*/
class RoundRobin{
protected $members;
protected $initMod;
public function __construct(array $members){
$this->members = $members;
$this->initMod = count($members) - 1;
echo '当前组内成员: '.json_encode($this->members).PHP_EOL;
}
//人员上/下线
private function checkStatus(){
return mt_rand(1,4);
}
//todo 加减组内成员 如果能实现动态轮寻就完美了 静态有漏洞 会掠过部分客服
public function changeGroupMembers(){
$total = count($this->members);
$id = mt_rand(0,$total-1);
echo '准备删除成员: '.$id+1.PHP_EOL;
array_splice($this->members,$id,1);
//echo '准备添加成员: '.$id.PHP_EOL;
//array_push($this->members,$total+1);
}
//获取当前组成员列表
public function getMembers(){
return $this->members;
}
//分配在线客服
public function choose(){
$total = count($this->members);
$mod = $c = $total; //组成员id集合
$j = $this->initMod;
//echo '当前模数: '.$j.PHP_EOL;
while($c > 0 ){
//获取成员id
$j =($j+1) % $mod;
echo '选中目标: '.$this->members[$j].PHP_EOL;
//获取成员在线状态
$status = $this->checkStatus();
//echo '在线状态: '.$status.PHP_EOL;
if( $status != 1){
$this->initMod = $j; //移动到下一个成员
//echo '模数自增之后: '.$this->initMod.PHP_EOL;
$c--;
if($c == 0){ return -1;} //未找到
continue;
}else{
$this->initMod = $j; //移动到下一个
return $j;
}
}
}
}
//测试参数
$a = new RoundRobin([1,2,3,4,5]);
echo '----第一轮调度-----'.PHP_EOL;
for($i = 1;$i<10;$i++){
$a->choose();
}
echo '----结束-----'.PHP_EOL;
echo '----修改组内客服-----'.PHP_EOL;
$a->changeGroupMembers();
echo '----第二轮循调度-----'.PHP_EOL;
for($i = 1;$i<10;$i++){
$a->choose();
}
执行结果:
MacBook-2:Desktop lemon$ php RoundRobin.php
当前组内成员: [1,2,3,4,5]
----第一轮调度-----
选中目标: 1
选中目标: 2
选中目标: 3
选中目标: 4
选中目标: 5
选中目标: 1
选中目标: 2
选中目标: 3
选中目标: 4
选中目标: 5
选中目标: 1
选中目标: 2
选中目标: 3
选中目标: 4
选中目标: 5
选中目标: 1
选中目标: 2
选中目标: 3
选中目标: 4
选中目标: 5
选中目标: 1
选中目标: 2
选中目标: 3
选中目标: 4
选中目标: 5
----结束-----
----修改组内客服-----
准备删除成员: 4
----第二轮循调度-----
选中目标: 2 //******第二轮调度跳过了1*******
选中目标: 3
选中目标: 5
选中目标: 1
选中目标: 2
选中目标: 3
选中目标: 5
选中目标: 1
选中目标: 2
选中目标: 3
选中目标: 5
选中目标: 1
选中目标: 2
选中目标: 3
选中目标: 5
选中目标: 1
选中目标: 2
选中目标: 3
选中目标: 5
选中目标: 1
选中目标: 2
选中目标: 3
选中目标: 5
选中目标: 1
结果显示,在模数变更之后,循环的指针没有跟着变更,导致的新一轮的调度中,有的成员被略过,还有可能被重复调度,具体得看剔除的位置。所以单单靠取模来轮询调度,有一定的缺陷,这种算法适合静态目标的轮询,但是如果要实现动态计算指针位置,也不是一个简单的算法,有违初衷。感兴趣的朋友可以研究下,我还没想到如何动态平衡指针,(ノへ ̄、)捂脸。。。但是有另一种算法也可以实现动态平衡,那就是——引用计数,请看下篇文章——动态轮循调度算法实现