<?php
/**
 * Redis Sentinel 完整实现 - 自动故障转移
 */

class RedisSentinelClient {
    private $sentinels;
    private $masterName;
    private $redis;
    private $timeout = 2;

    public function __construct($sentinels, $masterName = 'mymaster') {
        $this->sentinels = $sentinels; // [['host' => '127.0.0.1', 'port' => 26379], ...]
        $this->masterName = $masterName;
        $this->connect();
    }

    /**
     * 连接到当前主节点
     */
    private function connect() {
        $master = $this->getMaster();
        if (!$master) {
            throw new Exception('无法获取主节点信息');
        }

        $this->redis = new Redis();
        $this->redis->connect($master['ip'], $master['port'], $this->timeout);
    }

    /**
     * 从 Sentinel 获取主节点信息
     */
    private function getMaster() {
        foreach ($this->sentinels as $sentinel) {
            try {
                $s = new RedisSentinel($sentinel['host'], $sentinel['port'], $this->timeout);
                $master = $s->getMasterAddrByName($this->masterName);

                if ($master && count($master) >= 2) {
                    return ['ip' => $master[0], 'port' => $master[1]];
                }
            } catch (Exception $e) {
                continue; // 尝试下一个 Sentinel
            }
        }
        return null;
    }

    /**
     * 执行 Redis 命令(带自动重连)
     */
    public function execute($method, $args = []) {
        $maxRetries = 3;
        $attempt = 0;

        while ($attempt < $maxRetries) {
            try {
                return call_user_func_array([$this->redis, $method], $args);
            } catch (RedisException $e) {
                $attempt++;

                if ($attempt >= $maxRetries) {
                    throw $e;
                }

                // 重新连接到新的主节点
                sleep(1);
                $this->connect();
            }
        }
    }

    /**
     * 魔术方法:代理所有 Redis 方法
     */
    public function __call($method, $args) {
        return $this->execute($method, $args);
    }

    /**
     * 获取集群状态
     */
    public function getClusterInfo() {
        foreach ($this->sentinels as $sentinel) {
            try {
                $s = new RedisSentinel($sentinel['host'], $sentinel['port']);

                return [
                    'master' => $s->master($this->masterName),
                    'slaves' => $s->slaves($this->masterName),
                    'sentinels' => $s->sentinels($this->masterName)
                ];
            } catch (Exception $e) {
                continue;
            }
        }
        return null;
    }
}

// ============ 使用示例 ============

// 配置 Sentinel 节点
$sentinels = [
    ['host' => '127.0.0.1', 'port' => 26379],
    ['host' => '127.0.0.1', 'port' => 26380],
    ['host' => '127.0.0.1', 'port' => 26381]
];

// 创建客户端
$redis = new RedisSentinelClient($sentinels, 'mymaster');

// 使用 Redis(自动故障转移)
try {
    $redis->set('key', 'value');
    echo $redis->get('key') . "\n";

    // 获取集群信息
    $info = $redis->getClusterInfo();
    print_r($info);

} catch (Exception $e) {
    echo "错误: " . $e->getMessage() . "\n";
}

sentinel.conf - 最短配置

port 26379
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 10000

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐