1 <?php 2 /** 3 * Redis 操作,支持 Master/Slave 的负载集群 4 * 6 */ 7 class RedisCluster{ 8 9 // 是否使用 M/S 的读写集群方案 10 private $_isUseCluster = false; 11 12 // Slave 句柄标记 13 private $_sn = 0; 14 15 // 服务器连接句柄 16 private $_linkHandle = array( 17 ‘master‘=>null,// 只支持一台 Master 18 ‘slave‘=>array(),// 可以有多台 Slave 19 ); 20 21 /** 22 * 构造函数 23 * 24 * @param boolean $isUseCluster 是否采用 M/S 方案 25 */ 26 public function __construct($isUseCluster=false){ 27 $this->_isUseCluster = $isUseCluster; 28 } 29 30 /** 31 * 连接服务器,注意:这里使用长连接,提高效率,但不会自动关闭 32 * 33 * @param array $config Redis服务器配置 34 * @param boolean $isMaster 当前添加的服务器是否为 Master 服务器 35 * @return boolean 36 */ 37 public function connect($config=array(‘host‘=>‘127.0.0.1‘,‘port‘=>6379), $isMaster=true){ 38 // default port 39 if(!isset($config[‘port‘])){ 40 $config[‘port‘] = 6379; 41 } 42 // 设置 Master 连接 43 if($isMaster){ 44 $this->_linkHandle[‘master‘] = new Redis(); 45 $ret = $this->_linkHandle[‘master‘]->pconnect($config[‘host‘],$config[‘port‘]); 46 }else{ 47 // 多个 Slave 连接 48 $this->_linkHandle[‘slave‘][$this->_sn] = new Redis(); 49 $ret = $this->_linkHandle[‘slave‘][$this->_sn]->pconnect($config[‘host‘],$config[‘port‘]); 50 ++$this->_sn; 51 } 52 return $ret; 53 } 54 55 /** 56 * 关闭连接 57 * 58 * @param int $flag 关闭选择 0:关闭 Master 1:关闭 Slave 2:关闭所有 59 * @return boolean 60 */ 61 public function close($flag=2){ 62 switch($flag){ 63 // 关闭 Master 64 case 0: 65 $this->getRedis()->close(); 66 break; 67 // 关闭 Slave 68 case 1: 69 for($i=0; $i<$this->_sn; ++$i){ 70 $this->_linkHandle[‘slave‘][$i]->close(); 71 } 72 break; 73 // 关闭所有 74 case 2: 75 $this->getRedis()->close(); 76 for($i=0; $i<$this->_sn; ++$i){ 77 $this->_linkHandle[‘slave‘][$i]->close(); 78 } 79 break; 80 } 81 return true; 82 } 83 84 /** 85 * 得到 Redis 原始对象可以有更多的操作 86 * 87 * @param boolean $isMaster 返回服务器的类型 true:返回Master false:返回Slave 88 * @param boolean $slaveOne 返回的Slave选择 true:负载均衡随机返回一个Slave选择 false:返回所有的Slave选择 89 * @return redis object 90 */ 91 public function getRedis($isMaster=true,$slaveOne=true){ 92 // 只返回 Master 93 if($isMaster){ 94 return $this->_linkHandle[‘master‘]; 95 }else{ 96 return $slaveOne ? $this->_getSlaveRedis() : $this->_linkHandle[‘slave‘]; 97 } 98 } 99 100 /** 101 * 写缓存 102 * 103 * @param string $key 组存KEY 104 * @param string $value 缓存值 105 * @param int $expire 过期时间, 0:表示无过期时间 106 */ 107 public function set($key, $value, $expire=0){ 108 // 永不超时 109 if($expire == 0){ 110 $ret = $this->getRedis()->set($key, $value); 111 }else{ 112 $ret = $this->getRedis()->setex($key, $expire, $value); 113 } 114 return $ret; 115 } 116 117 /** 118 * 读缓存 119 * 120 * @param string $key 缓存KEY,支持一次取多个 $key = array(‘key1‘,‘key2‘) 121 * @return string || boolean 失败返回 false, 成功返回字符串 122 */ 123 public function get($key){ 124 // 是否一次取多个值 125 $func = is_array($key) ? ‘mGet‘ : ‘get‘; 126 // 没有使用M/S 127 if(! $this->_isUseCluster){ 128 return $this->getRedis()->{$func}($key); 129 } 130 // 使用了 M/S 131 return $this->_getSlaveRedis()->{$func}($key); 132 } 133 134 /** 135 * 条件形式设置缓存,如果 key 不存时就设置,存在时设置失败 136 * 137 * @param string $key 缓存KEY 138 * @param string $value 缓存值 139 * @return boolean 140 */ 141 public function setnx($key, $value){ 142 return $this->getRedis()->setnx($key, $value); 143 } 144 145 /** 146 * 删除缓存 147 * 148 * @param string || array $key 缓存KEY,支持单个健:"key1" 或多个健:array(‘key1‘,‘key2‘) 149 * @return int 删除的健的数量 150 */ 151 public function remove($key){ 152 // $key => "key1" || array(‘key1‘,‘key2‘) 153 return $this->getRedis()->delete($key); 154 } 155 156 /** 157 * 值加加操作,类似 ++$i ,如果 key 不存在时自动设置为 0 后进行加加操作 158 * 159 * @param string $key 缓存KEY 160 * @param int $default 操作时的默认值 161 * @return int 操作后的值 162 */ 163 public function incr($key,$default=1){ 164 if($default == 1){ 165 return $this->getRedis()->incr($key); 166 }else{ 167 return $this->getRedis()->incrBy($key, $default); 168 } 169 } 170 171 /** 172 * 值减减操作,类似 --$i ,如果 key 不存在时自动设置为 0 后进行减减操作 173 * 174 * @param string $key 缓存KEY 175 * @param int $default 操作时的默认值 176 * @return int 操作后的值 177 */ 178 public function decr($key,$default=1){ 179 if($default == 1){ 180 return $this->getRedis()->decr($key); 181 }else{ 182 return $this->getRedis()->decrBy($key, $default); 183 } 184 } 185 186 /** 187 * 添空当前数据库 188 * 189 * @return boolean 190 */ 191 public function clear(){ 192 return $this->getRedis()->flushDB(); 193 } 194 195 /* =================== 以下私有方法 =================== */ 196 /** 197 * 随机 HASH 得到 Redis Slave 服务器句柄 198 * 199 * @return redis object 200 */ 201 private function _getSlaveRedis(){ 202 // 就一台 Slave 机直接返回 203 if($this->_sn <= 1){ 204 return $this->_linkHandle[‘slave‘][0]; 205 } 206 // 随机 Hash 得到 Slave 的句柄 207 $hash = $this->_hashId(mt_rand(), $this->_sn); 208 return $this->_linkHandle[‘slave‘][$hash]; 209 } 210 211 /** 212 * 根据ID得到 hash 后 0~m-1 之间的值 213 * 214 * @param string $id 215 * @param int $m 216 * @return int 217 */ 218 private function _hashId($id,$m=10) 219 { 220 //把字符串K转换为 0~m-1 之间的一个值作为对应记录的散列地址 221 $k = md5($id); 222 $l = strlen($k); 223 $b = bin2hex($k); 224 $h = 0; 225 for($i=0;$i<$l;$i++) 226 { 227 //相加模式HASH 228 $h += substr($b,$i*2,2); 229 } 230 $hash = ($h*1)%$m; 231 return $hash; 232 } 233 234 }// End Class 235 236 // ================= TEST DEMO ================= 237 238 // 只有一台 Redis 的应用$redis = new RedisCluster();$redis->connect(array(‘host‘=>‘127.0.0.1‘,‘port‘=>6379));$redis->set(‘id‘,35);var_dump($redis->get(‘id‘)); 239 240 // 有一台 Master 和 多台Slave 的集群应用$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){ 241 var_dump($redis->get(‘id‘)).PHP_EOL; 242 } 243 // phpRedis 扩展的更多高级操作$redis = new RedisCluster();$redis->connect(array(‘host‘=>‘127.0.0.1‘,‘port‘=>6379));$ret = $redis->getRedis()->ping();// phpRedis 原始APIvar_dump($ret);
时间: 2024-10-12 15:28:18