Redis高可用性之Failover过渡方案

从Redis官方路线图来看,大概会在Redis3.0左右正式支持Cluster。不过即便是乐观的估计,至少也得等几个月的时间,为了让我的应用在这段时间内能保持高可用性,我以主从服务器为基础实现了一个Failover过渡方案。

 

从理论上解释,一旦主服务器下线,可以在从服务器里挑选出新的主服务器,同时重新设置主从关系,并且当下线服务器重新上线后能自动加入到主从关系中去,内容如下:

<?php

class RedisFailover
{
    public $config = array();
    public $map    = array();

    const CONFIG_FILE = 'config.php';
    const MAP_FILE    = 'map.php';

    public function __construct()
    {
        $config = include self::CONFIG_FILE;

        foreach ((array)$config as $name => $nodes) {
            foreach ($nodes as $node) {
                $node = new RedisNode($node['host'], $node['port']);

                if ($node->isValid()) {
                    $this->config[$name][] = $node;
                }
            }

            if (empty($this->config[$name])) {
                throw new Exception('Invalid config.');
            }

            $this->map[$name] = $this->config[$name][0];
        }

        if (file_exists(self::MAP_FILE)) {
            $map = include self::MAP_FILE;

            foreach ((array)$map as $name => $node) {
                $node = new RedisNode($node['host'], $node['port']);

                $this->map[$name] = $node;
            }
        }
    }

    public function run()
    {
        $set_nodes_master = function($nodes, $master) {
            foreach ($nodes as $node) {
                $node->setMaster($master->host, $master->port);
            }
        };

        foreach ($this->config as $name => $nodes) {
            $is_master_valid = false;

            foreach ($nodes as $node) {
                if ($node == $this->map[$name]) {
                    $is_master_valid = true;

                    break;
                }
            }

            if ($is_master_valid) {
                $set_nodes_master($nodes, $this->map[$name]);

                continue;
            }

            foreach ($nodes as $node) {
                $master = $node->getMaster();

                if (empty($master)) {
                    continue;
                }

                if ($master['master_host'] != $this->map[$name]->host) {
                    continue;
                }

                if ($master['master_port'] != $this->map[$name]->port) {
                    continue;
                }

                if ($master['master_sync_in_progress']) {
                    continue;
                }

                $node->clearMaster();

                $set_nodes_master($nodes, $node);

                $this->map[$name] = $node;

                break;
            }
        }

        $map = array();

        foreach ($this->map as $name => $node) {
            $map[$name] = array(
                'host' => $node->host, 'port' => $node->port
            );
        }

        $content = '<?php return ' . var_export($map, true) . '; ?>';

        file_put_contents(self::MAP_FILE, $content);
    }
}

class RedisNode
{
    public $host;
    public $port;

    const CLI = '/usr/local/bin/redis-cli';

    public function __construct($host, $port)
    {
        $this->host = $host;
        $this->port = $port;
    }

    public function setMaster($host, $port)
    {
        if ($this->host != $host || $this->port != $port) {
            return $this->execute("SLAVEOF {$host} {$port}") == 'OK';
        }

        return false;
    }

    public function getMaster()
    {
        $result = array();

        $this->execute('INFO', $rows);

        foreach ($rows as $row) {
            if (preg_match('/^master_/', $row)) {
                list($key, $value) = explode(':', $row);

                $result[$key] = $value;
            }
        }

        return $result;
    }

    public function clearMaster()
    {
        return $this->execute('SLAVEOF NO ONE') == 'OK';
    }

    public function isValid()
    {
        return $this->execute('PING') == 'PONG';
    }

    public function execute($command, &$output = null)
    {
        return exec(
            self::CLI . " -h {$this->host} -p {$this->port} {$command}", $output
        );
    }
}

?>

其中提到了两个文件,先说一下config.php:

<?php

return array(
    'redis_foo' => array(
        array('host' => '192.168.0.1', 'port' => '6379'),
        array('host' => '192.168.0.2', 'port' => '6379'),
        array('host' => '192.168.0.3', 'port' => '6379'),
    ),
);

?>

说明:每个别名对应一组服务器,在这组服务器中,有一个是主服务器,其余都是从服务器,主从关系不要在配置文件里硬编码,而应该通过SLAVEOF命令动态设定。

再说一下map.php文件,内容如下:

<?php

return array (
    'redis_foo' => array (
        'host' => '192.168.0.1', 'port' => '6379'
    ),
);

?>

说明:别名对应的是当前有效的服务器。需要注意的是这个文件是自动生成的!程序在使用Redis的时候,都配置成别名的形式,具体的host,port通过此文件映射获得。

明白了以上代码之后,运行就很简单了:

<?php

$failover = new RedisFailover();
$failover->run();

?>

说明:实际部署时,最严格的方式是以守护进程的方式来执行,不过如果要求不是很苛刻的话,CRON就够了。测试时可以手动杀掉主服务器进程,再通过INFO查看效果。

再补充一些命令行用法的相关说明,本文都是使用redis-cli来发送命令的,通常这也是最佳选择,不过如果因为某些原因不能使用redis-cli的话,也可以使用nc(netcat)命令按照Redis协议实现一个简单的客户端工具,比如说PING命令可以这样实现:

shell> (echo -en "PING\r\n"; sleep 1) | nc localhost 6379

说明:之所以需要sleep一下是因为Redis的请求响应机制是Pipelining方式的。

既然说到这里了,就再唠十块钱儿的,通常,我们可以使用telnet命令和服务交互,但是telnet有一点非常不爽的是命令行不支持上下键历史,还好可以借助rlwrap来达成这个目的,视操作系统,可以很容易的用APT或YUM来安装,运行也很简单:

shell> rlwrap telnet localhost 6379

说明:通过使用rlwrap,不仅支持上下键历史,而且连Ctrl+r搜索也一并支持了,强!

在Redis Cluster释出前,希望这个脚本能帮到你,其实其他的服务也可以使用类似的方案,比如MySQL,不过复杂性会加大很多,好在已经有类似MHA之类的方案了。

【编辑推荐】

  1. 缓存大量小文件?Redis是首选!
  2. Redis能干啥?细看11种Web应用场景
  3. 主流NoSQL数据库之Redis全面评测

 

文章来源网络,作者:运维,如若转载,请注明出处:https://shuyeidc.com/wp/303556.html<

(0)
运维的头像运维
上一篇2025-05-25 07:45
下一篇 2025-05-25 07:47

相关推荐

  • INIZ是什么,INIZ价格多少钱

    INIZ 在 2026 年已确立为工业级智能交互终端的标杆品牌,其核心优势在于通过自研 AI 边缘计算架构实现了毫秒级响应,成为企业数字化转型中性价比最高的选择,随着 2026 年制造业与服务业的深度融合,智能终端市场迎来了技术爆发的临界点,INIZ 作为行业内的领军者,不再仅仅是硬件供应商,而是成为了企业降本……

    2026-05-02
    0
  • ShockHostingVPS测评多少钱?3.74美元/月VPS主机性能如何

    ShockHostingVPS 在 2026 年以 3.74 美元/月的极致性价比,配合 NVMe 全闪存架构与 99.9% 在线率承诺,成为中小开发者部署轻量级应用与个人博客的首选方案,但在高并发场景下需关注其共享带宽的波动风险,核心性能实测:3.74 美元/月档位的真实表现在 2026 年云计算市场普遍涨价……

    2026-05-02
    0
  • 日本 YardVPSVPS 测评,建站实测体验,日本 VPS 测评多少钱,日本 VPS 推荐

    日本 YardVPS 在 2026 年已稳定成为中小跨境电商与独立站的首选方案,其 CN2 GIA 线路在日美欧三向延迟均控制在 30ms 以内,性价比远超同配置竞品,在 2026 年全球网络架构重构的背景下,日本作为亚太区核心枢纽,其 VPS 性能直接决定了海外业务的落地效率,针对日本 VPS 推荐这一高频需……

    2026-05-02
    0
  • 新加坡、英国INIZVPS测评,实测体验与数据对比,INIZVPS新加坡英国哪个好,INIZVPS测评

    针对需要兼顾东南亚业务拓展与欧美合规访问的企业,新加坡节点在低延迟与本地化生态上完胜,而英国节点则在多区域覆盖与法律环境上更具优势,两者无绝对优劣,需依据具体业务场景二选一,2026 年跨境网络基建:新加坡与英国 INIZVPS 实战选型指南在 2026 年全球数据中心格局重塑的背景下,企业出海面临网络稳定性与……

    2026-05-02
    0
  • hosteons独立服务器测评不限流量实测数据与性能表现怎么样?

    Hosteons 独立服务器在 2026 年实测中展现出极高的性价比,其不限流量策略配合高性能硬件,是处理高并发与大数据传输场景下的理想选择,尤其适合预算敏感型中小企业及海外业务拓展需求,核心性能实测与硬件架构解析在 2026 年云计算基础设施全面向 ARM 与高主频 Intel 混合架构转型的背景下,Host……

    2026-05-02
    0

发表回复

您的邮箱地址不会被公开。必填项已用 * 标注