php+redis主从架构

php是用phpredis扩展实现和redis的连接的,但是phpredis只是提供了简单的命令处理,没有对redis的主从架构做处理,因此,考虑自己写一个。

处理的思路很简单,在phpredis外面再套一层,根据传入的命令判断是进行读操作还是写操作,然后用call_user_func_array方法,将命令传递给phpredis做处理


一、安装phpredis扩展

二、配置

//redis主从配置
$GLOBALS['config']['redis_master_slave'] = [
    'host'         => '127.0.0.1,127.0.0.1,127.0.0.1,127.0.01', // redis主机,默认第一个为master
    'port'         => '6379,6380,6381,6382', // redis端口
    'password'     => '', // 密码
    'select'       => '0,0,0,0', // 操作库
    'expire'       => 3600, //有效期
    'timeout'      => 0, // 连接超时时间(单位:毫秒)
    'prefix' => 'PHPREDIS_SESSION:', // key前缀
];

三、主从处理的类


/**
 * Class RedisMsredis主从复制
 */
class RedisMs {
    /**
     * 对象实例数组
     * @var array
     */
    private static $_instance = [];

    /**
     * hander
     * @var null|redis
     */
    protected $handler = null;

    /**
     * 配置
     * @var array
     */
    protected static $config  = [
        'host'         => '127.0.0.1,127.0.0.1', // redis主机
        'port'         => '6379,6379', // redis端口
        'password'     => '', // 密码
        'select'       => '0,0', // 操作库
        'timeout'      => 0, // 连接超时时间(单位:毫秒)
    ];

    /**
     * debug模式
     * @var bool
     */
    public $debug = false;

    /*
     * 单例模式
     */
    private function __construct($host, $port, $auth, $select, $timeout, $debug = false) {
        if(!$this->handler) {
            $this->handler = new redis();
        }

        $this->handler->connect($host, $port, $timeout);

        if('' != $auth) {
            $this->handler->auth($auth);
        }

        if(0 != $select) {
            $this->handler->select($select);
        }
        
        $this->debug = $debug;
    }

    public static function getInstance($config = [], $debug = false) {
        // 检测php环境
        if (!extension_loaded('redis')) {
            throw new Exception('not support:redis');
        }

        $config = array_merge(self::$config, $config);

        $hosts = explode(",", $config['host']);
        $ports = explode(",", $config['port']);
        $pwds = explode(",", $config['password']);
        $selects = explode(",", $config['select']);

        if(count($hosts) < 2 || $ports < 2) {
            throw new Exception('config error at least 2 host and 2 port needed');
        }

        foreach($hosts as $key => $host) {
            $port = !empty($ports[$key]) ? $ports[$key] : 6379;
            $pwd = !empty($pwds[$key]) ? $pwds[$key] : '';
            $select = intval($selects[$key]);
            $timeout = $config['timeout'];

            if(!self::$_instance[$key] instanceof self) {
                self::$_instance[$key] = new self($host, $port, $pwd, $select, $timeout, $debug);
            }
        }

        return self::$_instance[0];
    }

    /**
     * 获取主服务器
     * @return mixed
     */
    public function master() {
        if($this->debug) {
            echo 'i am master 
'
; } return self::$_instance[0]; } /** * 获取从服务器 */ public function slaves() { $slaves = []; for($i = 1; $i < count(self::$_instance); $i++) { $slaves[] = self::$_instance[$i]; } return $slaves; } /** * 随机生成一台从服务器 */ public function oneSlave() { $slaves = $this->slaves(); $count=count($slaves); $i= mt_rand(0,$count - 1); if($this->debug) { echo 'i am slave '.$i.'
'
; } return self::$_instance[$i]; } /** * 执行命令 */ public function runCommand($command, $params) { try{ $redis = $this->getByCommand($command); return call_user_func_array([$redis, $command], $params); } catch (Exception $e) { throw new Exception($e->getMessage(), $e->getCode()); } } /** * 根据command命令来获取服务器 */ protected function getByCommand($command) { //TODO::这里需要完善,只列出了部分读命令 $read_command = ['get', 'hGet', 'lRange']; $write_command = ['set', 'hSet']; if(in_array($command, $read_command)) { //读命令,随机返回一台读服务器 return $this->oneSlave()->handler; } elseif(in_array($command, $write_command)) { return $this->master()->handler; } else { throw new Exception('不支持该命令:'.$command); } } private function __clone() { } }
注意:getByCommand方法还需要完善,里面只列出了部分的读命令和写命令,将命令完善就可以了。


四、调用

/**
 * redis主从操作
 */
header("Content-type: text/html; charset=utf-8");

error_reporting(E_ERROR);

define('DIR', dirname(__FILE__));

//引入配置文件
require_once DIR.'/config.php';

//引入库文件
require_once DIR.'/lib/RedisMs.php';

$handler = RedisMs::getInstance($GLOBALS['config']['redis_master_slave'], true);

//测试set命令
try{
echo '测试set命令
'
; $handler->runCommand('set', ['name', 'maque']); var_dump($handler->runCommand('get', ['name'])); echo '测试hset命令
'
; $handler->runCommand('hSet', ['user_info', 'name', 'maque']); $handler->runCommand('hGet', ['user_info', 'name']); } catch (Exception $e) { var_dump($e->getMessage()); }


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