thinkphp的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配置

// REDIS配置
	'DATA_CACHE_TYPE' =>'Redis',
	'DATA_REDIS_HOST' =>'localhost,localhost',
	'DATA_REDIS_PORT' =>'6379,6380',
	'DATA_CACHE_TIME' =>30,
	'DATA_CACHE_PREFIX' =>'redis_',
	'DATA_PERSISTENT'	=>true
默认第一个是master,其余的是slave。

二、redis的缓存驱动


// +----------------------------------------------------------------------
namespace Think\Cache\Driver;
use Think\Cache;
defined('THINK_PATH') or exit();

/**
 * Redis缓存驱动 
 * 要求安装phpredis扩展:https://github.com/nicolasff/phpredis
 * @category   Think
 * @package  Cache
 * @subpackage  Driver
 * @author huangzengbing
 */
class Redisrw extends Cache {
	/**
	*类对象实例数组
	*共有静态变量
	*@param mixed $_instance存放实例
	*/
	private static $_instance=array();

	/**
	*每次实例的句柄
	*保护变量
	*/
	protected $handler;

	/**
	*redis的配置
	*全局 静态变量
	*静态的方法里调用静态变量和静态方法只能用self,不能出现$this
	*/
	static $option=array();

	/**
	*架构函数,必须设置为私有,防止外部new
	*实例化redis驱动的实例,寄一个socket
	*
	*/
	private function __construct($host,$port,$auth) {
		if(!$this->handler) {
			$this->handler= new \Redis;
		}
        $func = self::$option['persistent'] ? 'pconnect' : 'connect';
        if(self::$option['timeout'] === false) {
					$this->handler->$func($host,$port);
		}else {
					$this->handler->$func($host,$port,self::$option['timeout']);
		}

		// 认证
		if($auth){
			$this->handler->auth($auth);
		}
	}

	/**
	*实例函数,单例入口
	*共有,静态函数
	*/

	public static function getInstance($options=array()) {
		// 判断是否存在redis扩展
		if ( !extension_loaded('redis') ) {
            E(L('_NOT_SUPPERT_').':redis');
        }
        if(empty($options)) {
            $options = array (
                'host'          => C('DATA_REDIS_HOST') ? C('DATA_REDIS_HOST') : '127.0.0.1',
                'port'          => C('DATA_REDIS_PORT') ? C('DATA_REDIS_PORT') : 6379,
                'timeout'       => C('DATA_CACHE_TIME') ? C('DATA_CACHE_TIME') : false,
                'persistent'    => C('DATA_PERSISTENT') ? C('DATA_PERSISTENT') : false,
				'auth'			=> C('DATA_REDIS_AUTH') ? C('DATA_REDIS_AUTH') : false,
            );
        }
		$options['host'] = explode(',', $options['host']);
		$options['port'] = explode(',', $options['port']);
		$options['auth'] = explode(',', $options['auth']);
		foreach ($options['host'] as $key=>$value) {
			if (!isset($options['port'][$key])) {
				$options['port'][$key] = $options['port'][0];
			}
			if (!isset($options['auth'][$key])) {
				$options['auth'][$key] = $options['auth'][0];
			}
		}
        self::$option =  $options;
        self::$option['expire'] =  isset($options['expire']) ?  $options['expire']  :   C('DATA_EXPIRE');
        self::$option['prefix'] =  isset($options['prefix']) ?  $options['prefix']  :   C('DATA_CACHE_PREFIX');        
        self::$option['length'] =  isset($options['length']) ?  $options['length']  :   0;
        // 一次性创建redis的在不同host的实例
        foreach(self::$option['host'] as $i=>$server) {
        	$host=self::$option['host'][$i];
        	$port=self::$option['port'][$i];
        	$auth=self::$option['auth'][$i];
			if(!(self::$_instance[$i] instanceof self)) {
					self::$_instance[$i]=new self($host,intval($port),$auth);
			}		
		}
		
		// 默认返回第一个实例,即master
		return self::$_instance[0];
	}

	/**
	*判断是否master/slave,调用不同的master或者slave实例
	*
	*/
	public function is_master($master=true) {
		if($master) {
			$i=0;
		}else {
			$count=count(self::$option['host']);
			if($count==1) {
				$i=0;
			}else{
				$i=rand(1,$count - 1);
			}
		}
		//返回每一个实例的句柄
		return self::$_instance[$i]->handler;
	}

	/**
     * 读取缓存,随机从slave服务器中读缓存
     * @access public
     * @param string $name 缓存变量名
     * @return mixed
     */
    public function get($name) {
		$redis=$this->is_master(false);
        N('cache_read',1);
        $value = $redis->get(self::$option['prefix'].$name);
        $jsonData  = json_decode( $value, true );
        //检测是否为JSON数据 true 返回JSON解析数组, false返回源数据
        return ($jsonData === NULL) ? $value : $jsonData;	

    }

    /**
     * 写入缓存,写入master的redis服务器
     * @access public
     * @param string $name 缓存变量名
     * @param mixed $value  存储数据
     * @param integer $expire  有效时间(秒)
     * @return boolean
     */
    public function set($name, $value, $expire = null) {
		$redis=$this->is_master(true);
        N('cache_write',1);
        if(is_null($expire)) {
            $expire  =  self::$option['expire'];
        }
        $name   =   self::$option['prefix'].$name;
        //对数组/对象数据进行缓存处理,保证数据完整性
        $value  =  (is_object($value) || is_array($value)) ? json_encode($value) : $value;
        if(is_int($expire) && $expire > 0) {
            $result = $redis->setex($name, $expire, $value);
        }else{
            $result = $redis->set($name, $value);
        }
        if($result && self::$option['length']>0) {
            // 记录缓存队列
            $this->queue($name);
        }
        return $result;
    }

     /**
     * 删除缓存
     * @access public
     * @param string $name 缓存变量名
     * @return boolean
     */
    public function rm($name) {
		$redis=$this->is_master(true);
        return $redis->delete(self::$option['prefix'].$name);
    }

    /**
     * 清除缓存
     * @access public
     * @return boolean
     */
    public function clear() {
		$redis=$this->is_master(true);
        return $redis->flushDB();
    }

    /**
    *禁止外部克隆对象  
    *
    */
    private function __clone() {

    }

    //可以根据需要,继续添加phpredis的驱动api.

    /**
	 * 关闭长连接
	 * @access public
	 */
	public function __destruct() {
		if (self::$option['persistent'] == 'pconnect') {
			// 关闭master的长连接,不可以写,但slave任然可以读
			$redis=$this->is_master(true);
			$redis->close();
		}
	}
}



3、CONTROLLER中用法

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



你可能感兴趣的:(php,redis)