代码如下:
<?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); ?>
效果如图: