You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

397 lines
10 KiB

  1. <?php
  2. namespace App\Common;
  3. /**
  4. * Redis 操作,支持 Master/Slave 的负载集群
  5. *
  6. * @author jackluo
  7. */
  8. class RedisCluster
  9. {
  10. // 是否使用 M/S 的读写集群方案
  11. private $_isUseCluster = false;
  12. // Slave 句柄标记
  13. private $_sn = 0;
  14. // 服务器连接句柄
  15. private $_linkHandle = array(
  16. 'master'=>null,// 只支持一台 Master
  17. 'slave'=>array(),// 可以有多台 Slave
  18. );
  19. /**
  20. * 构造函数
  21. *
  22. * @param boolean $isUseCluster 是否采用 M/S 方案
  23. */
  24. public function __construct($isUseCluster=false)
  25. {
  26. $this->_isUseCluster = $isUseCluster;
  27. }
  28. /**
  29. * 连接服务器,注意:这里使用长连接,提高效率,但不会自动关闭
  30. *
  31. * @param array $config Redis服务器配置
  32. * @param boolean $isMaster 当前添加的服务器是否为 Master 服务器
  33. * @return boolean
  34. */
  35. public function connect($config=array('host'=>'127.0.0.1','port'=>6379), $isMaster=true)
  36. {
  37. // default port
  38. if(!isset($config['port']))
  39. {
  40. $config['port'] = 6379;
  41. }
  42. // 设置 Master 连接
  43. if($isMaster)
  44. {
  45. $this->_linkHandle['master'] = new Redis();
  46. $ret = $this->_linkHandle['master']->pconnect($config['host'],$config['port']);
  47. }
  48. else
  49. {
  50. // 多个 Slave 连接
  51. $this->_linkHandle['slave'][$this->_sn] = new Redis();
  52. $ret = $this->_linkHandle['slave'][$this->_sn]->pconnect($config['host'],$config['port']);
  53. ++$this->_sn;
  54. }
  55. return $ret;
  56. }
  57. /**
  58. * 关闭连接
  59. *
  60. * @param int $flag 关闭选择 0:关闭 Master 1:关闭 Slave 2:关闭所有
  61. * @return boolean
  62. */
  63. public function close($flag=2){
  64. switch($flag){
  65. // 关闭 Master
  66. case 0:
  67. $this->getRedis()->close();
  68. break;
  69. // 关闭 Slave
  70. case 1:
  71. for($i=0; $i<$this->_sn; ++$i){
  72. $this->_linkHandle['slave'][$i]->close();
  73. }
  74. break;
  75. // 关闭所有
  76. case 1:
  77. $this->getRedis()->close();
  78. for($i=0; $i<$this->_sn; ++$i){
  79. $this->_linkHandle['slave'][$i]->close();
  80. }
  81. break;
  82. }
  83. return true;
  84. }
  85. /**
  86. * 得到 Redis 原始对象可以有更多的操作
  87. *
  88. * @param boolean $isMaster 返回服务器的类型 true:返回Master false:返回Slave
  89. * @param boolean $slaveOne 返回的Slave选择 true:负载均衡随机返回一个Slave选择 false:返回所有的Slave选择
  90. * @return redis object
  91. */
  92. public function getRedis($isMaster=true,$slaveOne=true){
  93. // 只返回 Master
  94. if($isMaster){
  95. return $this->_linkHandle['master'];
  96. }else{
  97. return $slaveOne ? $this->_getSlaveRedis() : $this->_linkHandle['slave'];
  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. * @param string $key 缓存KEY,支持一次取多个 $key = array('key1','key2')
  120. * @return string || boolean 失败返回 false, 成功返回字符串
  121. */
  122. public function get($key){
  123. // 是否一次取多个值
  124. $func = is_array($key) ? 'mGet' : 'get';
  125. // 没有使用M/S
  126. if(! $this->_isUseCluster){
  127. return $this->getRedis()->{$func}($key);
  128. }
  129. // 使用了 M/S
  130. return $this->_getSlaveRedis()->{$func}($key);
  131. }
  132. /*
  133. // magic function
  134. public function __call($name,$arguments){
  135. return call_user_func($name,$arguments);
  136. }
  137. */
  138. /**
  139. * 条件形式设置缓存,如果 key 不存时就设置,存在时设置失败
  140. *
  141. * @param string $key 缓存KEY
  142. * @param string $value 缓存值
  143. * @return boolean
  144. */
  145. public function setnx($key, $value){
  146. return $this->getRedis()->setnx($key, $value);
  147. }
  148. /**
  149. * 删除缓存
  150. *
  151. * @param string || array $key 缓存KEY,支持单个健:"key1" 或多个健:array('key1','key2')
  152. * @return int 删除的健的数量
  153. */
  154. public function remove($key){
  155. // $key => "key1" || array('key1','key2')
  156. return $this->getRedis()->delete($key);
  157. }
  158. /**
  159. * 值加加操作,类似 ++$i ,如果 key 不存在时自动设置为 0 后进行加加操作
  160. *
  161. * @param string $key 缓存KEY
  162. * @param int $default 操作时的默认值
  163. * @return int 操作后的值
  164. */
  165. public function incr($key,$default=1){
  166. if($default == 1){
  167. return $this->getRedis()->incr($key);
  168. }else{
  169. return $this->getRedis()->incrBy($key, $default);
  170. }
  171. }
  172. /**
  173. * 值减减操作,类似 --$i ,如果 key 不存在时自动设置为 0 后进行减减操作
  174. *
  175. * @param string $key 缓存KEY
  176. * @param int $default 操作时的默认值
  177. * @return int 操作后的值
  178. */
  179. public function decr($key,$default=1){
  180. if($default == 1){
  181. return $this->getRedis()->decr($key);
  182. }else{
  183. return $this->getRedis()->decrBy($key, $default);
  184. }
  185. }
  186. /**
  187. * 添空当前数据库
  188. *
  189. * @return boolean
  190. */
  191. public function clear(){
  192. return $this->getRedis()->flushDB();
  193. }
  194. /* =================== 以下私有方法 =================== */
  195. /**
  196. * 随机 HASH 得到 Redis Slave 服务器句柄
  197. *
  198. * @return redis object
  199. */
  200. private function _getSlaveRedis(){
  201. // 就一台 Slave 机直接返回
  202. if($this->_sn <= 1){
  203. return $this->_linkHandle['slave'][0];
  204. }
  205. // 随机 Hash 得到 Slave 的句柄
  206. $hash = $this->_hashId(mt_rand(), $this->_sn);
  207. return $this->_linkHandle['slave'][$hash];
  208. }
  209. /**
  210. * 根据ID得到 hash 0~m-1 之间的值
  211. *
  212. * @param string $id
  213. * @param int $m
  214. * @return int
  215. */
  216. private function _hashId($id,$m=10)
  217. {
  218. //把字符串K转换为 0~m-1 之间的一个值作为对应记录的散列地址
  219. $k = md5($id);
  220. $l = strlen($k);
  221. $b = bin2hex($k);
  222. $h = 0;
  223. for($i=0;$i<$l;$i++)
  224. {
  225. //相加模式HASH
  226. $h += substr($b,$i*2,2);
  227. }
  228. $hash = ($h*1)%$m;
  229. return $hash;
  230. }
  231. /**
  232. * lpush
  233. */
  234. public function lpush($key,$value)
  235. {
  236. return $this->getRedis()->lpush($key,$value);
  237. }
  238. /**
  239. * add lpop
  240. */
  241. public function lpop($key)
  242. {
  243. return $this->getRedis()->lpop($key);
  244. }
  245. /**
  246. * lrange
  247. */
  248. public function lrange($key,$start,$end)
  249. {
  250. return $this->getRedis()->lrange($key,$start,$end);
  251. }
  252. /**
  253. * set hash opeation
  254. */
  255. public function hset($name,$key,$value)
  256. {
  257. if(is_array($value))
  258. {
  259. return $this->getRedis()->hset($name,$key,serialize($value));
  260. }
  261. return $this->getRedis()->hset($name,$key,$value);
  262. }
  263. /**
  264. * get hash opeation
  265. */
  266. public function hget($name,$key = null,$serialize=true)
  267. {
  268. if($key)
  269. {
  270. $row = $this->getRedis()->hget($name,$key);
  271. if($row && $serialize)
  272. {
  273. unserialize($row);
  274. }
  275. return $row;
  276. }
  277. return $this->getRedis()->hgetAll($name);
  278. }
  279. /**
  280. * delete hash opeation
  281. */
  282. public function hdel($name,$key = null)
  283. {
  284. if($key)
  285. {
  286. return $this->getRedis()->hdel($name,$key);
  287. }
  288. return $this->getRedis()->hdel($name);
  289. }
  290. /**
  291. * Transaction start
  292. */
  293. public function multi()
  294. {
  295. return $this->getRedis()->multi();
  296. }
  297. /**
  298. * Transaction send
  299. */
  300. public function exec()
  301. {
  302. return $this->getRedis()->exec();
  303. }
  304. }// End Class
  305. // ================= TEST DEMO =================
  306. // 只有一台 Redis 的应用
  307. /* $redis = new RedisCluster();
  308. $redis->connect(array('host'=>'127.0.0.1','port'=>6379));
  309. //*
  310. $cron_id = 10001;
  311. $CRON_KEY = 'CRON_LIST'; //
  312. $PHONE_KEY = 'PHONE_LIST:'.$cron_id;//
  313. //cron info
  314. $cron = $redis->hget($CRON_KEY,$cron_id);
  315. if(empty($cron)){
  316. $cron = array('id'=>10,'name'=>'jackluo');//mysql data
  317. $redis->hset($CRON_KEY,$cron_id,$cron); // set redis
  318. }
  319. //phone list
  320. $phone_list = $redis->lrange($PHONE_KEY,0,-1);
  321. print_r($phone_list);
  322. if(empty($phone_list)){
  323. $phone_list =explode(',','13228191831,18608041585'); //mysql data
  324. //join list
  325. if($phone_list){
  326. $redis->multi();
  327. foreach ($phone_list as $phone) {
  328. $redis->lpush($PHONE_KEY,$phone);
  329. }
  330. $redis->exec();
  331. }
  332. }
  333. print_r($phone_list); */
  334. /*$list = $redis->hget($cron_list,);
  335. var_dump($list);*/
  336. //*/
  337. //$redis->set('id',35);
  338. /*
  339. $redis->lpush('test','1111');
  340. $redis->lpush('test','2222');
  341. $redis->lpush('test','3333');
  342. $list = $redis->lrange('test',0,-1);
  343. print_r($list);
  344. $lpop = $redis->lpop('test');
  345. print_r($lpop);
  346. $lpop = $redis->lpop('test');
  347. print_r($lpop);
  348. $lpop = $redis->lpop('test');
  349. print_r($lpop);
  350. */
  351. // var_dump($redis->get('id'));