/** * Redis 操作,支持 Master/Slave 的負(fù)載集群 * * @author V哥 */ class RedisCluster{ // 是否使用 M/S 的讀寫(xiě)集群方案 private $_isUseCluster = false; // Slave 句柄標(biāo)記 private $_sn = 0; // 服務(wù)器連接句柄 private $_linkHandle = array( 'master'=>null,// 只支持一臺(tái) Master 'slave'=>array(),// 可以有多臺(tái) Slave ); /** * 構(gòu)造函數(shù) * * @param boolean $isUseCluster 是否采用 M/S 方案 */ public function __construct($isUseCluster=false){ $this->_isUseCluster = $isUseCluster; } /** * 連接服務(wù)器,注意:這里使用長(zhǎng)連接,提高效率,但不會(huì)自動(dòng)關(guān)閉 * * @param array $config Redis服務(wù)器配置 * @param boolean $isMaster 當(dāng)前添加的服務(wù)器是否為 Master 服務(wù)器 * @return boolean */ public function connect($config=array('host'=>'127.0.0.1','port'=>6379), $isMaster=true){ // default port if(!isset($config['port'])){ $config['port'] = 6379; } // 設(shè)置 Master 連接 if($isMaster){ $this->_linkHandle['master'] = new Redis(); $ret = $this->_linkHandle['master']->pconnect($config['host'],$config['port']); }else{ // 多個(gè) Slave 連接 $this->_linkHandle['slave'][$this->_sn] = new Redis(); $ret = $this->_linkHandle['slave'][$this->_sn]->pconnect($config['host'],$config['port']); ++$this->_sn; } return $ret; } /** * 關(guān)閉連接 * * @param int $flag 關(guān)閉選擇 0:關(guān)閉 Master 1:關(guān)閉 Slave 2:關(guān)閉所有 * @return boolean */ public function close($flag=2){ switch($flag){ // 關(guān)閉 Master case 0: $this->getRedis()->close(); break; // 關(guān)閉 Slave case 1: for($i=0; $i<$this->_sn; ++$i){ $this->_linkHandle['slave'][$i]->close(); } break; // 關(guān)閉所有 case 1: $this->getRedis()->close(); for($i=0; $i<$this->_sn; ++$i){ $this->_linkHandle['slave'][$i]->close(); } break; } return true; } /** * 得到 Redis 原始對(duì)象可以有更多的操作 * * @param boolean $isMaster 返回服務(wù)器的類(lèi)型 true:返回Master false:返回Slave * @param boolean $slaveOne 返回的Slave選擇 true:負(fù)載均衡隨機(jī)返回一個(gè)Slave選擇 false:返回所有的Slave選擇 * @return redis object */ public function getRedis($isMaster=true,$slaveOne=true){ // 只返回 Master if($isMaster){ return $this->_linkHandle['master']; }else{ return $slaveOne ? $this->_getSlaveRedis() : $this->_linkHandle['slave']; } } /** * 寫(xiě)緩存 * * @param string $key 組存KEY * @param string $value 緩存值 * @param int $expire 過(guò)期時(shí)間, 0:表示無(wú)過(guò)期時(shí)間 */ public function set($key, $value, $expire=0){ // 永不超時(shí) if($expire == 0){ $ret = $this->getRedis()->set($key, $value); }else{ $ret = $this->getRedis()->setex($key, $expire, $value); } return $ret; } /** * 讀緩存 * * @param string $key 緩存KEY,支持一次取多個(gè) $key = array('key1','key2') * @return string || boolean 失敗返回 false, 成功返回字符串 */ public function get($key){ // 是否一次取多個(gè)值 $func = is_array($key) ? 'mGet' : 'get'; // 沒(méi)有使用M/S if(! $this->_isUseCluster){ return $this->getRedis()->{$func}($key); } // 使用了 M/S return $this->_getSlaveRedis()->{$func}($key); } /** * 條件形式設(shè)置緩存,如果 key 不存時(shí)就設(shè)置,存在時(shí)設(shè)置失敗 * * @param string $key 緩存KEY * @param string $value 緩存值 * @return boolean */ public function setnx($key, $value){ return $this->getRedis()->setnx($key, $value); } /** * 刪除緩存 * * @param string || array $key 緩存KEY,支持單個(gè)健:"key1" 或多個(gè)健:array('key1','key2') * @return int 刪除的健的數(shù)量 */ public function remove($key){ // $key => "key1" || array('key1','key2') return $this->getRedis()->delete($key); } /** * 值加加操作,類(lèi)似 ++$i ,如果 key 不存在時(shí)自動(dòng)設(shè)置為 0 后進(jìn)行加加操作 * * @param string $key 緩存KEY * @param int $default 操作時(shí)的默認(rèn)值 * @return int 操作后的值 */ public function incr($key,$default=1){ if($default == 1){ return $this->getRedis()->incr($key); }else{ return $this->getRedis()->incrBy($key, $default); } } /** * 值減減操作,類(lèi)似 --$i ,如果 key 不存在時(shí)自動(dòng)設(shè)置為 0 后進(jìn)行減減操作 * * @param string $key 緩存KEY * @param int $default 操作時(shí)的默認(rèn)值 * @return int 操作后的值 */ public function decr($key,$default=1){ if($default == 1){ return $this->getRedis()->decr($key); }else{ return $this->getRedis()->decrBy($key, $default); } } /** * 添空當(dāng)前數(shù)據(jù)庫(kù) * * @return boolean */ public function clear(){ return $this->getRedis()->flushDB(); } /* =================== 以下私有方法 =================== */ /** * 隨機(jī) HASH 得到 Redis Slave 服務(wù)器句柄 * * @return redis object */ private function _getSlaveRedis(){ // 就一臺(tái) Slave 機(jī)直接返回 if($this->_sn <= 1){ return $this->_linkHandle['slave'][0]; } // 隨機(jī) Hash 得到 Slave 的句柄 $hash = $this->_hashId(mt_rand(), $this->_sn); return $this->_linkHandle['slave'][$hash]; } /** * 根據(jù)ID得到 hash 后 0~m-1 之間的值 * * @param string $id * @param int $m * @return int */ private function _hashId($id,$m=10) { //把字符串K轉(zhuǎn)換為 0~m-1 之間的一個(gè)值作為對(duì)應(yīng)記錄的散列地址 $k = md5($id); $l = strlen($k); $b = bin2hex($k); $h = 0; for($i=0;$i<$l;$i++) { //相加模式HASH $h += substr($b,$i*2,2); } $hash = ($h*1)%$m; return $hash; } }// End Class // ================= TEST DEMO ================= // 只有一臺(tái) Redis 的應(yīng)用 $redis = new RedisCluster(); $redis->connect(array('host'=>'127.0.0.1','port'=>6379)); $redis->set('id',35); var_dump($redis->get('id')); // 有一臺(tái) Master 和 多臺(tái)Slave 的集群應(yīng)用 $redis = new RedisCluster(true); $redis->connect(array('host'=>'127.0.0.1','port'=>6379), true);// master $redis->connect(array('host'=>'127.0.0.1','port'=>63791), false);// slave 1 $redis->connect(array('host'=>'127.0.0.1','port'=>63792), false);// slave 2 $redis->set('id',100); for($i=1; $i<=100; ++$i){ var_dump($redis->get('id')).PHP_EOL; } // phpRedis 擴(kuò)展的更多高級(jí)操作 $redis = new RedisCluster(); $redis->connect(array('host'=>'127.0.0.1','port'=>6379)); $ret = $redis->getRedis()->ping();// phpRedis 原始API var_dump($ret); |
|