redis的讀寫分離代碼層

最近公司的项目越做越大,数据量越来越大,逐渐地要开始支持分布式的数据库,当然包括要缓存。经过了各种的讨论和认证,决定用redis服务器作为数据缓存的服务器,除了支持丰富的数据类型,string,list,hash,set ,sort set ,还有持久化的数据的功能。这一方面确实比memcache好很多。下面是我的整个测试过程(tp3.2以上)。

注意:首先要安装phpredis和配置好redis的主从复制,并且要启动各个redis的实例。

与tp自带的redis.class.php的缓存驱动不同,实现功能

1、可以实现主从分布,多个slave

2、master主要负责写,slave负责随机读

3、单例模式,一次实例化所有的redis,包括master,slave。不需要每操作一次redis,就连接redis一次,那样严重浪费资源。

4、自由拓展,可以根据自己的需要继续添加redis的API操作。

一、tp的redis配置

  1. // REDIS配置
  2. 'DATA_CACHE_TYPE' =>'Redis',
  3. 'DATA_REDIS_HOST' =>'localhost,localhost',
  4. 'DATA_REDIS_PORT' =>'6379,6380',
  5. 'DATA_CACHE_TIME' =>30,
  6. 'DATA_CACHE_PREFIX' =>'redis_',
  7. 'DATA_PERSISTENT' =>true </span>
默认第一个是master,其余的是slave。

二、redis的缓存驱动

  1. <?php
  2. // +----------------------------------------------------------------------
  3. // | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
  4. // +----------------------------------------------------------------------
  5. // | Copyright (c) 2006-2013 http://thinkphp.cn All rights reserved.
  6. // +----------------------------------------------------------------------
  7. // | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
  8. // +----------------------------------------------------------------------
  9. // | Author: liu21st <[email protected]>
  10. // +----------------------------------------------------------------------
  11. namespace Think\ Cache\ Driver;
  12. use Think\ Cache;
  13. defined( 'THINK_PATH') or exit();
  14. /**
  15. * Redis缓存驱动
  16. * 要求安装phpredis扩展:https://github.com/nicolasff/phpredis
  17. * @category Think
  18. * @package Cache
  19. * @subpackage Driver
  20. * @author huangzengbing
  21. */
  22. class Redisrw extends Cache {
  23. /**
  24. *类对象实例数组
  25. *共有静态变量
  26. *@param mixed $_instance存放实例
  27. */
  28. private static $_instance= array();
  29. /**
  30. *每次实例的句柄
  31. *保护变量
  32. */
  33. protected $handler;
  34. /**
  35. *redis的配置
  36. *全局 静态变量
  37. *静态的方法里调用静态变量和静态方法只能用self,不能出现$this
  38. */
  39. static $option= array();
  40. /**
  41. *架构函数,必须设置为私有,防止外部new
  42. *实例化redis驱动的实例,寄一个socket
  43. *
  44. */
  45. private function __construct($host,$port,$auth) {
  46. if(! $this->handler) {
  47. $this->handler= new \Redis;
  48. }
  49. $func = self::$option[ 'persistent'] ? 'pconnect' : 'connect';
  50. if( self::$option[ 'timeout'] === false) {
  51. $this->handler->$func($host,$port);
  52. } else {
  53. $this->handler->$func($host,$port, self::$option[ 'timeout']);
  54. }
  55. // 认证
  56. if($auth){
  57. $this->handler->auth($auth);
  58. }
  59. }
  60. /**
  61. *实例函数,单例入口
  62. *共有,静态函数
  63. */
  64. public static function getInstance($options=array()) {
  65. // 判断是否存在redis扩展
  66. if ( !extension_loaded( 'redis') ) {
  67. E(L( '_NOT_SUPPERT_'). ':redis');
  68. }
  69. if( empty($options)) {
  70. $options = array (
  71. 'host' => C( 'DATA_REDIS_HOST') ? C( 'DATA_REDIS_HOST') : '127.0.0.1',
  72. 'port' => C( 'DATA_REDIS_PORT') ? C( 'DATA_REDIS_PORT') : 6379,
  73. 'timeout' => C( 'DATA_CACHE_TIME') ? C( 'DATA_CACHE_TIME') : false,
  74. 'persistent' => C( 'DATA_PERSISTENT') ? C( 'DATA_PERSISTENT') : false,
  75. 'auth' => C( 'DATA_REDIS_AUTH') ? C( 'DATA_REDIS_AUTH') : false,
  76. );
  77. }
  78. $options[ 'host'] = explode( ',', $options[ 'host']);
  79. $options[ 'port'] = explode( ',', $options[ 'port']);
  80. $options[ 'auth'] = explode( ',', $options[ 'auth']);
  81. foreach ($options[ 'host'] as $key=>$value) {
  82. if (! isset($options[ 'port'][$key])) {
  83. $options[ 'port'][$key] = $options[ 'port'][ 0];
  84. }
  85. if (! isset($options[ 'auth'][$key])) {
  86. $options[ 'auth'][$key] = $options[ 'auth'][ 0];
  87. }
  88. }
  89. self::$option = $options;
  90. self::$option[ 'expire'] = isset($options[ 'expire']) ? $options[ 'expire'] : C( 'DATA_EXPIRE');
  91. self::$option[ 'prefix'] = isset($options[ 'prefix']) ? $options[ 'prefix'] : C( 'DATA_CACHE_PREFIX');
  92. self::$option[ 'length'] = isset($options[ 'length']) ? $options[ 'length'] : 0;
  93. // 一次性创建redis的在不同host的实例
  94. foreach( self::$option[ 'host'] as $i=>$server) {
  95. $host= self::$option[ 'host'][$i];
  96. $port= self::$option[ 'port'][$i];
  97. $auth= self::$option[ 'auth'][$i];
  98. if(!( self::$_instance[$i] instanceof self)) {
  99. self::$_instance[$i]= new self($host,intval($port),$auth);
  100. }
  101. }
  102. // 默认返回第一个实例,即master
  103. return self::$_instance[ 0];
  104. }
  105. /**
  106. *判断是否master/slave,调用不同的master或者slave实例
  107. *
  108. */
  109. public function is_master($master=true) {
  110. if($master) {
  111. $i= 0;
  112. } else {
  113. $count=count( self::$option[ 'host']);
  114. if($count== 1) {
  115. $i= 0;
  116. } else{
  117. $i=rand( 1,$count - 1);
  118. }
  119. }
  120. //返回每一个实例的句柄
  121. return self::$_instance[$i]->handler;
  122. }
  123. /**
  124. * 读取缓存,随机从slave服务器中读缓存
  125. * @access public
  126. * @param string $name 缓存变量名
  127. * @return mixed
  128. */
  129. public function get($name) {
  130. $redis= $this->is_master( false);
  131. N( 'cache_read', 1);
  132. $value = $redis->get( self::$option[ 'prefix'].$name);
  133. $jsonData = json_decode( $value, true );
  134. //检测是否为JSON数据 true 返回JSON解析数组, false返回源数据
  135. return ($jsonData === NULL) ? $value : $jsonData;
  136. }
  137. /**
  138. * 写入缓存,写入master的redis服务器
  139. * @access public
  140. * @param string $name 缓存变量名
  141. * @param mixed $value 存储数据
  142. * @param integer $expire 有效时间(秒)
  143. * @return boolean
  144. */
  145. public function set($name, $value, $expire = null) {
  146. $redis= $this->is_master( true);
  147. N( 'cache_write', 1);
  148. if(is_null($expire)) {
  149. $expire = self::$option[ 'expire'];
  150. }
  151. $name = self::$option[ 'prefix'].$name;
  152. //对数组/对象数据进行缓存处理,保证数据完整性
  153. $value = (is_object($value) || is_array($value)) ? json_encode($value) : $value;
  154. if(is_int($expire) && $expire > 0) {
  155. $result = $redis->setex($name, $expire, $value);
  156. } else{
  157. $result = $redis->set($name, $value);
  158. }
  159. if($result && self::$option[ 'length']> 0) {
  160. // 记录缓存队列
  161. $this->queue($name);
  162. }
  163. return $result;
  164. }
  165. /**
  166. * 删除缓存
  167. * @access public
  168. * @param string $name 缓存变量名
  169. * @return boolean
  170. */
  171. public function rm($name) {
  172. $redis= $this->is_master( true);
  173. return $redis->delete( self::$option[ 'prefix'].$name);
  174. }
  175. /**
  176. * 清除缓存
  177. * @access public
  178. * @return boolean
  179. */
  180. public function clear() {
  181. $redis= $this->is_master( true);
  182. return $redis->flushDB();
  183. }
  184. /**
  185. *禁止外部克隆对象
  186. *
  187. */
  188. private function __clone() {
  189. }
  190. //可以根据需要,继续添加phpredis的驱动api.
  191. /**
  192. * 关闭长连接
  193. * @access public
  194. */
  195. public function __destruct() {
  196. if ( self::$option[ 'persistent'] == 'pconnect') {
  197. // 关闭master的长连接,不可以写,但slave任然可以读
  198. $redis= $this->is_master( true);
  199. $redis->close();
  200. }
  201. }
  202. }


 
 3、CONTROLLER中用法 
  
 

  1. $redis=\Think\Cache\Driver\Redisrw::getInstance();
  2. $redis->set('address','beijing');
  3. $result=$redis->get('address');
  4. dump($result);

猜你喜欢

转载自blog.csdn.net/qq_37779709/article/details/80929771