1. <?php
  2. /**
  3.  * 对服务器进行一致性hash分布算法
  4.  */
  5. class HashRing
  6. {
  7.     private $servers = array();
  8.     private $nodeList = array();
  9.     private $nodeHashList = array();
  10.     private $nodeTotalNum = 0;
  11.     private $virtualNodeNum = 32;
  12.     private $keyHash = '';

  13.     public function __construct($servers)
  14.     {
  15.         $this->servers = $servers;
  16.         foreach ($servers as $server) {
  17.             for ($i = 0; $i < $this->virtualNodeNum; $i++) {
  18.                 $this->nodeList[sprintf("%u", crc32($server.'-'.$i))] = array($server, $i);
  19.             }
  20.         }
  21.         ksort($this->nodeList);
  22.         $this->nodeHashList = array_keys($this->nodeList);
  23.     }

  24.     private function getNodeIndex($key)
  25.     {
  26.         $this->keyHash = sprintf("%u", crc32($key));
  27.         if ($this->keyHash > end($this->nodeHashList)) {
  28.             $this->keyHash = $this->keyHash % end($this->nodeHashList);
  29.         }
  30.         if ($this->keyHash <= reset($this->nodeHashList)) {
  31.             return 0;
  32.         }
  33.         $this->nodeTotalNum = count($this->nodeHashList);
  34.         return $this->binaryChopIndex(0, $this->nodeTotalNum);
  35.     }

  36.     private function binaryChopIndex($l=0, $r=0)
  37.     {
  38.         if ($l < $r) {
  39.             $avg = intval(($l+$r) / 2);
  40.             if ($this->nodeHashList[$avg] == $this->keyHash) {
  41.                 return $avg;
  42.             } elseif ($this->keyHash < $this->nodeHashList[$avg] && ($avg > 0)) {
  43.                 return $this->binaryChopIndex($l, $avg-1);
  44.             } else {
  45.                 return $this->binaryChopIndex($avg+1, $r);
  46.             }
  47.         } else {
  48.             return $l;
  49.         }
  50.     }

  51.     public function getServersByKey($key, $num=1)
  52.     {
  53.         $index = $this->getNodeIndex($key);
  54.         $server = $this->nodeList[$this->nodeHashList[$index]];
  55.         if ($num == 1) {
  56.             return $server[0];
  57.         }
  58.         if ($num >= count($this->servers)) {
  59.             $num = count($this->servers);
  60.         }
  61.         $result = array($server[0]);
  62.         for ($i=$index+1; true; $i++) {
  63.             if ($i >= $this->nodeTotalNum) {
  64.                 $i = 0;
  65.             }
  66.             $nextServer = $this->nodeList[$this->nodeHashList[$i]];
  67.             if (!in_array($nextServer[0], $result)) {
  68.                 $result[] = $nextServer[0];
  69.             }
  70.             if (count($result) == $num) {
  71.                 break;
  72.             }
  73.         }
  74.         return $result;
  75.     }
  76. }



  77. //示例
  78. $servers = array(
  79.     '127.0.0.1:11211',
  80.     '127.0.0.1:11212',
  81.     '127.0.0.1:11213',
  82.     '127.0.0.1:11214',
  83.     '127.0.0.1:11215'
  84. );
  85. $obj = new HashRing($servers);
  86. $servers = $obj->getServersByKey('testkey', 2);
  87. print_r($servers);
  88. echo "\n";

10-09 10:19