PHP实现一致性哈希算法

代码如下:

<?php
function myHash($str) {  
    // hash(i) = hash(i-1) * 33 + str[i]  
    $hash = 0;  
    $s    = md5($str);  
    $seed = 5;  
    $len  = 32;  
    for ($i = 0; $i < $len; $i++) {  
        // (hash << 5) + hash 相当于 hash * 33  
        //$hash = sprintf("%u", $hash * 33) + ord($s{$i});  
        //$hash = ($hash * 33 + ord($s{$i})) & 0x7FFFFFFF;  
        $hash = ($hash << $seed) + $hash + ord($s{$i});  
    }  
   
    return $hash & 0x7FFFFFFF;  
}
class ConsistentHash
{
    // server列表
    private $_server_list = array();
    // 延迟排序,因为可能会执行多次addServer
    private $_layze_sorted = FALSE;

    public function addServer($server)
    {
        $hash = myHash($server);
        $this->_layze_sorted = FALSE;
        if (!isset($this->_server_list[$hash]))
        {
            $this->_server_list[$hash] = $server;
        }
        return $this;
    }

    public function find_my_hash($key='')
    {
        // 寻找区间值 比如当前 server_list已排好序的hash对照表为
        // array( 3=>"server1",9=>"server2","30"=>"server3")
        // 则 hash 会区分为3块
        // 1  <3的           分布在server3
        // 2  >=3的并且<9的  分布在server1
        // 3  >=9的并且<30的 分布在server2
        // 4  >=30的         分布在server3
        $find_hash=myHash($key);//计算当前hash
        $length=count($this->_server_list);
        if($length==0){return false;}//如果是空的 返回false
        if($length==1){return current($this->_server_list);}//如果是只有1个 就立即返回当前这个
        if (!$this->_layze_sorted)
        {
            ksort($this->_server_list);//尚未排序 首次进行排序
            $this->_layze_sorted = TRUE;//后面就不需要排序了
        }
        reset($this->_server_list);
        $first_key=key($this->_server_list);//获取首个key
        if($find_hash<$first_key)
        {
            return end($this->_server_list);//如果小于等于第一个 立即返回最后一个
        }
        reset($this->_server_list);//重置数组
        $result=current($this->_server_list);//获取当前第一个作为假定的结果
        foreach ($this->_server_list as $key => $v)
        {
            if($find_hash>=$key)
            {
                $result=$v;//如果要找的hash大于这个起点 认为该区域就是要寻找的区块
            }else
            {
                reset($this->_server_list);
                return $result;//要找的hash已经小于了当前区块起点 说明上一个就是要找的区块结果 立即返回
            }
        }
        return $result;//最后也没找到说明 大于了最大值
    }
}
   
$consisHash = new ConsistentHash();
for ($i=0; $i < 1000; $i++)
{ 
    $consisHash->addServer("serv{$i}");
}
$start_time=microtime(true);
for ($i=0; $i < 10000; $i++)
{
    var_dump("key{$i}-->".$consisHash->find_my_hash("key{$i}"));
   
}
$end_time=microtime(true);
$result=($end_time-$start_time)*1000;
var_dump($result);

?>

效果如图:

 

posted @ 2017-12-13 19:44  李照耀  阅读(468)  评论(0编辑  收藏  举报