一致性hash算法-php-redis版本

<?php
//抄過來的代碼,改了一點點,
//實際來說,效率偏低,命中還算均勻

 class RedisHash {

	private $_node = array();
	private $_nodeData = array();
	private $_keyNode = 0;
	private $_redis = null;
	public  $nodeCounter=[];
	
	//每個物理服務器生成虛擬節點個數 [注:節點數越多,cache分佈的均勻性越好,
	//同時set get操作時,也更耗資源,10臺物理服務器,採用200較爲合理]
	private $_virtualNodeNum = 100;
	
	private function __construct() {
	       
		$config = [
			'127.0.0.1:6370',
			'127.0.0.1:6371',
			'127.0.0.1:6372',
			'127.0.0.1:6373',
			'127.0.0.1:6374',
			'127.0.0.1:6375',
			'127.0.0.1:6376',
			'127.0.0.1:6377',
			'127.0.0.1:6378',
			'127.0.0.1:6379',
		];				       
		if (!$config) throw new Exception('Cache config NULL');
		
		foreach ($config as $key => $value) {
			for ($i = 0; $i < $this->_virtualNodeNum; $i++) {
				$this->_node[sprintf("%u", crc32($value . '_' . $i))] = $value . '_' . $i;
			}
		}
		ksort($this->_node);
	}

	private function __clone(){}
	
	
	static public function getInstance() {
		static $redisObj = null;
		if (!is_object($redisObj)) {
			$redisObj = new self();
		}
		return $redisObj;
	}
	
	
	public function getRedis($key) {
		$this->_nodeData = array_keys($this->_node);
		$this->_keyNode = sprintf("%u", crc32($key));
		$nodeKey = $this->_findServerNode();		
		//如果超出環,從頭再用二分法查找一個最近的,然後環的頭尾做判斷,取最接近的節點
		if ($this->_keyNode > end($this->_nodeData)) {
			$this->_keyNode -= end($this->_nodeData);
			$nodeKey2 = $this->_findServerNode();
			if (abs($nodeKey2 - $this->_keyNode) < abs($nodeKey - $this->_keyNode))  $nodeKey = $nodeKey2;
		}
		
		// var_dump($this->_node[$nodeKey]);
		
		list($config, $num) = explode('_', $this->_node[$nodeKey]);
		if (!$config) throw new Exception('Cache config Error');
		if (!isset($this->_redis[$config])) {
			$this->_redis[$config] = new \Redis;
			list($host, $port) = explode(':', $config);
			$this->_redis[$config]->connect($host, $port);	
		}
		
		$this->nodeCounter[$config] ++;
		
		return $this->_redis[$config];
	}
	
	
	private function _findServerNode($m = 0, $b = 0) {
	    $total = count($this->_nodeData);
	    if ($total != 0 && $b == 0) $b = $total - 1;
	    if ($m < $b){
			$avg = (int)(($m+$b) / 2);
			if ($this->_nodeData[$avg] == $this->_keyNode) {
				return $this->_nodeData[$avg];
			}elseif ($this->_keyNode < $this->_nodeData[$avg] && ($avg-1 >= 0)) {
				return $this->_findServerNode($m, $avg-1);
			}else {
				return $this->_findServerNode($avg+1, $b);
			}
	    }
		if (abs($this->_nodeData[$b] - $this->_keyNode) < abs($this->_nodeData[$m] - $this->_keyNode))  return $this->_nodeData[$b];
		else return $this->_nodeData[$m];
	}
		
	public function set($key, $value, $expire = 0) {
	    return $this->getRedis($key)->set($key, $value,$expire);
	}
	
	
	public function get($key) {
		return $this->getRedis($key)->get($key);
	}
	
	public function delete($key) {
		return $this->getRedis($key)->delete($key);
	}
	
}

$time_start = microtime(true);
$redis = RedisHash::getInstance();
for($i=0;$i<10000;$i++) {
     $b = $redis->set('m_key'.$i, $i);
}
print_r($redis->nodeCounter);
echo  microtime( true ) - $time_start ;   



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章