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前缀
];

三、主从处理的类

<?php

/**
 * 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 <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());
}


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
首先,你需要在 Spring Boot 项目中添加 Redis 的依赖,可以使用以下 Maven 依赖: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> ``` 接着,在 application.properties 或 application.yml 文件中添加 Redis 的配置: ```yaml # Redis 主节点配置 spring.redis.host=127.0.0.1 spring.redis.port=6379 # Redis 从节点配置 spring.redis.cluster.nodes=192.168.1.1:6379,192.168.1.2:6379 spring.redis.cluster.master=master ``` 以上配置是一个 Redis 主节点和两个从节点的示例,其中: - `spring.redis.host` 和 `spring.redis.port` 是主节点的地址和端口。 - `spring.redis.cluster.nodes` 是从节点的地址和端口列表,多个节点使用英文逗号分隔。 - `spring.redis.cluster.master` 是主节点的名称,可以在 Redis 配置文件中设置。 最后,你需要使用 RedisTemplate 或者 StringRedisTemplate 类来操作 Redis。在 Spring Boot 中,你可以通过注入 RedisTemplate 或者 StringRedisTemplate 类来使用 Redis。例如: ```java @Autowired private RedisTemplate<String, Object> redisTemplate; public void set(String key, Object value) { redisTemplate.opsForValue().set(key, value); } public Object get(String key) { return redisTemplate.opsForValue().get(key); } ``` 以上代码中,我们注入了 RedisTemplate 类,并使用其 `opsForValue` 方法来操作 Redis 中的数据。 注意,如果你使用的是 Redis 主从复制模式,你在写入数据时应该使用主节点,而在读取数据时可以使用主节点或从节点。如果你使用的是 Redis 集群模式,Spring Boot 会自动将读取操作负载均衡到不同的节点上。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值