RedisCli.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. <?php
  2. /**
  3. * Redis 操作,支持 Master/Slave 的负载集群
  4. *
  5. */
  6. namespace app\command\Org;
  7. class RedisCli {
  8. // 是否使用 M/S 的读写集群方案
  9. private $_isUseCluster = false;
  10. // Slave 句柄标记
  11. private $_sn = 0;
  12. // 服务器连接句柄
  13. private $_linkHandle = array(
  14. 'master' => null, // 只支持一台 Master
  15. 'slave' => array(), // 可以有多台 Slave
  16. );
  17. private $rsConfig = array();
  18. private $isCLT = false;
  19. /**
  20. * 构造函数
  21. *
  22. * @param boolean $isUseCluster 是否采用 M/S 方案
  23. */
  24. public function __construct($isUseCluster = false,$isCLI = false) {
  25. $this->_isUseCluster = $isUseCluster;
  26. $this->isCLT = $isCLI;
  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), $auth = array(), $isMaster = true) {
  36. // default port
  37. if (!isset($config['port'])) {
  38. $config['port'] = 6379;
  39. }
  40. $this->rsConfig = array('config'=>$config,'auth'=>$auth);
  41. // 设置 Master 连接
  42. if ($isMaster) {
  43. $this->_linkHandle['master'] = new \Redis();
  44. if($this->isCLT)
  45. $ret = $this->_linkHandle['master']->connect($config['host'], $config['port'],5);
  46. else
  47. $ret = $this->_linkHandle['master']->pconnect($config['host'], $config['port'],5);
  48. if (count($auth) > 0 && !empty($auth[0])) {
  49. if ($this->_linkHandle['master']->auth($auth[0] . ":" . $auth[1]) === false) {
  50. die('Redis PassWord Error');
  51. }
  52. }
  53. } else {
  54. // 多个 Slave 连接
  55. $this->_linkHandle['slave'][$this->_sn] = new Redis();
  56. if($this->isCLT)
  57. $ret = $this->_linkHandle['slave'][$this->_sn]->connect($config['host'], $config['port']);
  58. else
  59. $ret = $this->_linkHandle['slave'][$this->_sn]->pconnect($config['host'], $config['port']);
  60. if (count($auth) > 0 && !empty($auth[0])) {
  61. if ($this->_linkHandle['master']->auth($auth[0] . ":" . $auth[1]) === false) {
  62. die('Redis PassWord Error');
  63. }
  64. }
  65. ++$this->_sn;
  66. }
  67. return $ret;
  68. }
  69. /**
  70. * 重新连接
  71. */
  72. private function reConnect() {
  73. $this->_linkHandle['master'] = new \Redis();
  74. $config = $this->rsConfig['config'];
  75. $auth = $this->rsConfig['auth'];
  76. if($this->isCLT)
  77. $ret = $this->_linkHandle['master']->connect($config['host'], $config['port'],5);
  78. else
  79. $ret = $this->_linkHandle['master']->pconnect($config['host'], $config['port']);
  80. if (count($auth) > 0 && !empty($auth[0])) {
  81. if ($this->_linkHandle['master']->auth($auth[0] . ":" . $auth[1]) === false) {
  82. die('Redis PassWord Error');
  83. }
  84. }
  85. }
  86. /**
  87. * 判断是否连接成功
  88. */
  89. public function isConnet() {
  90. try {
  91. $res = $this->getRedis()->ping();
  92. if ($res !== '+PONG') {
  93. $this->reConnect();
  94. }
  95. } catch (\RedisException $ex) {
  96. $this->reConnect();
  97. }
  98. }
  99. /**
  100. * 关闭连接
  101. *
  102. * @param int $flag 关闭选择 0:关闭 Master 1:关闭 Slave 2:关闭所有
  103. * @return boolean
  104. */
  105. public function close($flag = 2) {
  106. switch ($flag) {
  107. // 关闭 Master
  108. case 0:
  109. $this->getRedis()->close();
  110. break;
  111. // 关闭 Slave
  112. case 1:
  113. for ($i = 0; $i < $this->_sn; ++$i) {
  114. $this->_linkHandle['slave'][$i]->close();
  115. }
  116. break;
  117. // 关闭所有
  118. case 1:
  119. $this->getRedis()->close();
  120. for ($i = 0; $i < $this->_sn; ++$i) {
  121. $this->_linkHandle['slave'][$i]->close();
  122. }
  123. break;
  124. }
  125. return true;
  126. }
  127. /**
  128. * 得到 Redis 原始对象可以有更多的操作
  129. *
  130. * @param boolean $isMaster 返回服务器的类型 true:返回Master false:返回Slave
  131. * @param boolean $slaveOne 返回的Slave选择 true:负载均衡随机返回一个Slave选择 false:返回所有的Slave选择
  132. * @return redis object
  133. */
  134. public function getRedis($isMaster = true, $slaveOne = true) {
  135. // 只返回 Master
  136. if ($isMaster) {
  137. return $this->_linkHandle['master'];
  138. } else {
  139. return $slaveOne ? $this->_getSlaveRedis() : $this->_linkHandle['slave'];
  140. }
  141. }
  142. /**
  143. * 写缓存
  144. *
  145. * @param string $key 组存KEY
  146. * @param string $value 缓存值
  147. * @param int $expire 过期时间, 0:表示无过期时间
  148. */
  149. public function set($key, $value, $expire = 0) {
  150. // 永不超时
  151. if ($expire == 0) {
  152. $ret = $this->getRedis()->set($key, $value);
  153. } else {
  154. $ret = $this->getRedis()->setex($key, $expire, $value);
  155. }
  156. return $ret;
  157. }
  158. /**
  159. * 读缓存
  160. *
  161. * @param string $key 缓存KEY,支持一次取多个 $key = array('key1','key2')
  162. * @return string || boolean 失败返回 false, 成功返回字符串
  163. */
  164. public function get($key) {
  165. // 是否一次取多个值
  166. $func = is_array($key) ? 'mGet' : 'get';
  167. // 没有使用M/S
  168. if (!$this->_isUseCluster) {
  169. return $this->getRedis()->{$func}($key);
  170. }
  171. // 使用了 M/S
  172. return $this->_getSlaveRedis()->{$func}($key);
  173. }
  174. /**
  175. * 读取key值 支持模糊读取
  176. * @param type $key
  177. */
  178. public function keyS($key){
  179. return $this->getRedis()->keys($key);
  180. }
  181. public function exists($key){
  182. return $this->getRedis()->exists($key);
  183. }
  184. /*
  185. // magic function
  186. public function __call($name,$arguments){
  187. return call_user_func($name,$arguments);
  188. }
  189. */
  190. /**
  191. * 条件形式设置缓存,如果 key 不存时就设置,存在时设置失败
  192. *
  193. * @param string $key 缓存KEY
  194. * @param string $value 缓存值
  195. * @return boolean
  196. */
  197. public function setnx($key, $value) {
  198. return $this->getRedis()->setnx($key, $value);
  199. }
  200. /**
  201. * 返回成功
  202. * @param type $key
  203. */
  204. public function llen($key){
  205. return $this->getRedis()->llen($key);
  206. }
  207. /**
  208. * 过期时间
  209. */
  210. public function expire($key,$expire = 0){
  211. if($expire == 0) return;
  212. $this->getRedis()->expire($key,$expire);
  213. }
  214. /**
  215. * 删除缓存
  216. *
  217. * @param string || array $key 缓存KEY,支持单个健:"key1" 或多个健:array('key1','key2')
  218. * @return int 删除的健的数量
  219. */
  220. public function remove($key) {
  221. // $key => "key1" || array('key1','key2')
  222. return $this->getRedis()->delete($key);
  223. }
  224. /**
  225. * 值加加操作,类似 ++$i ,如果 key 不存在时自动设置为 0 后进行加加操作
  226. *
  227. * @param string $key 缓存KEY
  228. * @param int $default 操作时的默认值
  229. * @return int 操作后的值
  230. */
  231. public function incr($key, $default = 1) {
  232. if ($default == 1) {
  233. return $this->getRedis()->incr($key);
  234. } else {
  235. return $this->getRedis()->incrBy($key, $default);
  236. }
  237. }
  238. /**
  239. * 值减减操作,类似 --$i ,如果 key 不存在时自动设置为 0 后进行减减操作
  240. *
  241. * @param string $key 缓存KEY
  242. * @param int $default 操作时的默认值
  243. * @return int 操作后的值
  244. */
  245. public function decr($key, $default = 1) {
  246. if ($default == 1) {
  247. return $this->getRedis()->decr($key);
  248. } else {
  249. return $this->getRedis()->decrBy($key, $default);
  250. }
  251. }
  252. /**
  253. * 添空当前数据库
  254. *
  255. * @return boolean
  256. */
  257. public function clear() {
  258. return $this->getRedis()->flushDB();
  259. }
  260. /* =================== 以下私有方法 =================== */
  261. /**
  262. * 随机 HASH 得到 Redis Slave 服务器句柄
  263. *
  264. * @return redis object
  265. */
  266. private function _getSlaveRedis() {
  267. // 就一台 Slave 机直接返回
  268. if ($this->_sn <= 1) {
  269. return $this->_linkHandle['slave'][0];
  270. }
  271. // 随机 Hash 得到 Slave 的句柄
  272. $hash = $this->_hashId(mt_rand(), $this->_sn);
  273. return $this->_linkHandle['slave'][$hash];
  274. }
  275. /**
  276. * 根据ID得到 hash 后 0~m-1 之间的值
  277. *
  278. * @param string $id
  279. * @param int $m
  280. * @return int
  281. */
  282. private function _hashId($id, $m = 10) {
  283. //把字符串K转换为 0~m-1 之间的一个值作为对应记录的散列地址
  284. $k = md5($id);
  285. $l = strlen($k);
  286. $b = bin2hex($k);
  287. $h = 0;
  288. for ($i = 0; $i < $l; $i++) {
  289. //相加模式HASH
  290. $h += substr($b, $i * 2, 2);
  291. }
  292. $hash = ($h * 1) % $m;
  293. return $hash;
  294. }
  295. /**
  296. * lpush
  297. */
  298. public function lpush($key, $value) {
  299. return $this->getRedis()->lpush($key, $value);
  300. }
  301. /**
  302. * add lpop
  303. */
  304. public function lpop($key) {
  305. return $this->getRedis()->lpop($key);
  306. }
  307. /**
  308. * lrange
  309. */
  310. public function lrange($key, $start, $end) {
  311. return $this->getRedis()->lrange($key, $start, $end);
  312. }
  313. /**
  314. * set hash opeation
  315. */
  316. public function hset($name, $key, $value) {
  317. if (is_array($value)) {
  318. return $this->getRedis()->hset($name, $key, serialize($value));
  319. }
  320. return $this->getRedis()->hset($name, $key, $value);
  321. }
  322. /**
  323. * get hash opeation
  324. */
  325. public function hget($name, $key = null, $serialize = true) {
  326. if ($key) {
  327. $row = $this->getRedis()->hget($name, $key);
  328. if ($row && $serialize) {
  329. unserialize($row);
  330. }
  331. return $row;
  332. }
  333. return $this->getRedis()->hgetAll($name);
  334. }
  335. /**
  336. * delete hash opeation
  337. */
  338. public function hdel($name, $key = null) {
  339. if ($key) {
  340. return $this->getRedis()->hdel($name, $key);
  341. }
  342. return $this->getRedis()->hdel($name);
  343. }
  344. /**
  345. * Transaction start
  346. */
  347. public function multi() {
  348. return $this->getRedis()->multi();
  349. }
  350. /**
  351. * Transaction send
  352. */
  353. public function exec() {
  354. return $this->getRedis()->exec();
  355. }
  356. /**
  357. *
  358. */
  359. public function info($key){
  360. return $this->getRedis()->scan($key);
  361. }
  362. /**
  363. * 有序字段插入
  364. * @param type $key
  365. * @param type $num
  366. * @param type $value
  367. */
  368. public function zAdd($key,$num,$value){
  369. return $this->getRedis()->zadd($key,$num,$value);
  370. }
  371. /**
  372. * 获取有序字段的长度
  373. * @param type $key
  374. */
  375. public function zSize($key){
  376. return $this->getRedis()->zsize($key);
  377. }
  378. /**
  379. * 删除有序字段数据
  380. * @param type $key
  381. * @param type $value
  382. */
  383. public function zDelete($key,$value){
  384. return $this->getRedis()->zdelete($key,$value);
  385. }
  386. /**
  387. * 查询数据
  388. */
  389. public function zRange($key,$num){
  390. return $this->getRedis()->zrange($key,$num,-1,true);
  391. }
  392. }