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前缀 ];
三、主从处理的类
<?php /** * Class RedisMs:redis主从复制 */ 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 <br />'; } 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.'<br />'; } 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方法还需要完善,里面只列出了部分的读命令和写命令,将命令完善就可以了。
四、调用
<?php /** * 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命令<br />'; $handler->runCommand('set', ['name', 'maque']); var_dump($handler->runCommand('get', ['name'])); echo '测试hset命令<br />'; $handler->runCommand('hSet', ['user_info', 'name', 'maque']); $handler->runCommand('hGet', ['user_info', 'name']); } catch (Exception $e) { var_dump($e->getMessage()); }