美文网首页Redis
Redis第1️⃣9️⃣课 哨兵Sentinel(HA)

Redis第1️⃣9️⃣课 哨兵Sentinel(HA)

作者: 小超_8b2f | 来源:发表于2019-05-16 19:12 被阅读0次

    Redis 5.0之后版本的高可用集群搭建,已弃用哨兵

    Sentinel好文

    Docker化高可用redis集群

    一、用主从复制实现高可用?

    1、为主提供备份
    2、为主提供分流,减轻主压力
    3、如果主出现了问题,故障转移基本上需要手工完成,或者是脚本

    slaveof no one #slava 变成主
    [slaveof newMasterIp port] # 多个从节点手动执行
    

    4、写能力和存储能力受限(只能在一个主节点上进行写和存储)

    二、哨兵的架构说明

    Redis 哨兵架构 Redis Sentinal故障转移2 Redis Sentinal故障转移3 一个哨兵实现可以监控多个主从Redis

    三、安装配置

    实战配置目标架构
    0. 注意事项

      1)配置开启主从节点
      2)配置开启sentinel监控主节点(sentinel是特殊的Redis)
      3)实际应该多台机器
      4)详细配置节点

    1. Redis主节点配置文件:
    redis-server redis-7000.conf
    
    port 7000
    daemonize yes
    pidfile "/var/run/redis-7000.pid"
    logfile "redis-7000.log"
    dbfilename "dump-7000.rdb"
    dir "/usr/local/redis/data/"
    #省略了RDB配置
    
    2. Redis从节点1配置文件
    redis-server redis-7001.conf
    port 7001
    daemonize yes
    pidfile "/var/run/redis-7001.pid"
    logfile "redis-7001.log"
    dbfilename "dump-7001.rdb"
    dir "/usr/local/redis/data/"
    
    #旧版本用slaveof
    #slaveof 127.0.0.1 7000
    replicaof 127.0.0.1 7000
    
    
    3. Redis从节点2配置文件
    redis-server redis-7002.conf
    port 7002
    daemonize yes
    pidfile "/var/run/redis-7002.pid"
    logfile "redis-7002.log"
    dbfilename "dump-7002.rdb"
    dir "/usr/local/redis/data/"
    
    #旧版本用slaveof
    #slaveof 127.0.0.1 7000
    replicaof 127.0.0.1 7000
    
    4. 在redis-5.0.4根目录下找到sentinel.conf
    #配置哨兵需要监控的主节点ip和端口,最后的2代表:
    #如果有2个哨兵主观认为主节点down了,那么就客观认为主节点down掉了,
    #开始发起投票选举新主节点的操作。多个主节点配置多个。
    sentinel monitor mymaster 192.167.3.145 7000 2
    #配置哨兵连接主节点的认证密码。(主节点配置的requirepass)。
    sentinel auth-pass mymaster ibethfy
    #配置多少毫秒后没收到主节点的反馈,则主观认为主节点down了。
    sentinel down-after-milliseconds mymaster 5000
    #failover过期时间。当failover开始后,在此时间内仍然没有触发任何failover操作,
    #当前sentinel将会认为此次failover失败。  
    sentinel failover-timeout mymaster 30000
    
    #切换到了新的节点后,slaves对新master进行复制
    sentinel parallel-syncs mymaster 1
    
    5. 精简配置文件(去除注释和空行)
    cat sentinel.conf | grep -v "#" | grep -v "^$" > redis-sentinel-26379.conf
    cat redis-sentinel-26379.conf
    ----------------------------------------------------------------
    port 26379
    daemonize yes
    pidfile /var/run/redis-sentinel-26379.pid
    logfile "redis-sentinel-26379.log"
    dir /usr/local/redis/data/
    sentinel monitor mymaster 127.0.0.1 7000 2
    sentinel down-after-milliseconds mymaster 30000
    sentinel parallel-syncs mymaster 1
    sentinel failover-timeout mymaster 180000
    sentinel deny-scripts-reconfig yes
    
    6. 根据redis-sentinel-26379.conf生成另外二个sentinel配置文件
    #将文件中的文本中的”26379“文本字样全都替换成”26380“
    sed "s/26379/26380/g" redis-sentinel-26379.conf > redis-sentinel-26380.conf
    sed "s/26379/26381/g" redis-sentinel-26379.conf > redis-sentinel-26381.conf
    

    注意:sentinel配置文件中只配置了主节点没有配置从节点,从节点信息是通过主节点的 info Replication获取到的:
    redis-cli -h localhost -p 7000 info Replication

    四、启动哨兵


    1. 启动所有的哨兵
    redis-sentinel redis-sentinel-26379.conf
    redis-sentinel redis-sentinel-26380.conf
    redis-sentinel redis-sentinel-26381.conf
    
    2. 查看26379哨兵信息
    guchunchao$ redis-cli -p 26379
    127.0.0.1:26379> keys *  ###很多Redis的命令不支持
    (error) ERR unknown command `keys`, with args beginning with: `*`, 
    127.0.0.1:26379> ping    ### 支持
    PONG
    127.0.0.1:26379> info    ###支持
    # Server
    redis_version:5.0.4
    redis_git_sha1:00000000
    redis_git_dirty:0
    redis_build_id:aaa9fd100be76deb
    redis_mode:sentinel
    os:Darwin 17.7.0 x86_64
    arch_bits:64
    multiplexing_api:kqueue
    atomicvar_api:atomic-builtin
    gcc_version:4.2.1
    process_id:2422
    run_id:507272280f7e541f9d99a1b4bbea005a178b76f8
    tcp_port:26379
    uptime_in_seconds:70
    uptime_in_days:0
    hz:13
    configured_hz:10
    lru_clock:14672887
    executable:/usr/local/redis/etc/sentinel/redis-sentinel
    config_file:/usr/local/redis/etc/sentinel/mySentinel.conf
    
    # Clients
    connected_clients:1
    client_recent_max_input_buffer:2
    client_recent_max_output_buffer:0
    blocked_clients:0
    
    # CPU
    used_cpu_sys:0.208366
    used_cpu_user:0.111376
    used_cpu_sys_children:0.000000
    used_cpu_user_children:0.000000
    
    # Stats
    total_connections_received:1
    total_commands_processed:1
    instantaneous_ops_per_sec:0
    total_net_input_bytes:66
    total_net_output_bytes:129
    instantaneous_input_kbps:0.00
    instantaneous_output_kbps:0.00
    rejected_connections:0
    sync_full:0
    sync_partial_ok:0
    sync_partial_err:0
    expired_keys:0
    expired_stale_perc:0.00
    expired_time_cap_reached_count:0
    evicted_keys:0
    keyspace_hits:0
    keyspace_misses:0
    pubsub_channels:0
    pubsub_patterns:0
    latest_fork_usec:0
    migrate_cached_sockets:0
    slave_expires_tracked_keys:0
    active_defrag_hits:0
    active_defrag_misses:0
    active_defrag_key_hits:0
    active_defrag_key_misses:0
    
    # Sentinel
    sentinel_masters:1
    sentinel_tilt:0
    sentinel_running_scripts:0
    sentinel_scripts_queue_length:0
    sentinel_simulate_failure_flags:0
    master0:name=mymaster,status=ok,address=127.0.0.1:7000,slaves=2,sentinels=1
    127.0.0.1:26379> 
    
    3. 再次查看26379哨兵配置文件,发现配置文件有变化
    guchunchaodeMacBook-Air:sentinel guchunchao$ cat mySentinel.conf 
    port 26379
    daemonize yes
    pidfile "/var/run/redis-sentinel.pid"
    logfile "redis-sentinel-26379.log"
    dir "/usr/local/redis/data"
    sentinel myid b4cc05b4073f4865c28b8a815d2e36b68d4a05b9
    sentinel deny-scripts-reconfig yes
    sentinel monitor mymaster 127.0.0.1 7000 2
    sentinel config-epoch mymaster 0
    sentinel leader-epoch mymaster 0
    # Generated by CONFIG REWRITE
    protected-mode no
    sentinel known-replica mymaster 127.0.0.1 7002
    sentinel known-replica mymaster 127.0.0.1 7001
    sentinel current-epoch 0
    
    4. 按哨兵的端口号分别查询各个哨兵的信息
    xiaochao$ redis-cli -p 26379 info Sentinel
    # Sentinel
    sentinel_masters:1
    sentinel_tilt:0
    sentinel_running_scripts:0
    sentinel_scripts_queue_length:0
    sentinel_simulate_failure_flags:0
    master0:name=mymaster,status=ok,address=127.0.0.1:7000,slaves=2,sentinels=3
    
    xiaochao$ redis-cli -p 26380 info Sentinel
    # Sentinel
    sentinel_masters:1
    sentinel_tilt:0
    sentinel_running_scripts:0
    sentinel_scripts_queue_length:0
    sentinel_simulate_failure_flags:0
    master0:name=mymaster,status=ok,address=127.0.0.1:7000,slaves=2,sentinels=3
    
    xiaochao$ redis-cli -p 26381 info Sentinel
    # Sentinel
    sentinel_masters:1
    sentinel_tilt:0
    sentinel_running_scripts:0
    sentinel_scripts_queue_length:0
    sentinel_simulate_failure_flags:0
    master0:name=mymaster,status=ok,address=127.0.0.1:7000,slaves=2,sentinels=3
    

    五、Java客户端连接

    1.流程图
    客户端实现基本原理step-1 客户端实现基本原理step-2 客户端实现基本原理step-3 客户端实现基本原理step-4

    故障的发现和转移都是由sentinel来做的,客户端订阅sentinel的频道,频道里有谁是master的变化,如果master变了,频道就会publish一个消息给客户端。

    2. 客户端接入流程

    1) Sentinel地址集合
    2) masterName
    3) 不是代理模式(并不是通过sentinel代理间接地连接master,是直连。sentinel只是在管理master)

    3. Jedis实现
    public class TestSentinel {
    
        static Logger  logger = LoggerFactory.getLogger(TestSentinel.class);
    
        public static void main(String[] args) {
            String masterName = "mymaster";
            Set<String> sentinels = new HashSet<>();
            sentinels.add("127.0.0.1:26379");
            sentinels.add("127.0.0.1:26380");
            sentinels.add("127.0.0.1:26381");
            
            JedisSentinelPool sentinelPool = new JedisSentinelPool(masterName, sentinels);
            Jedis jedis = null;
                while(true) {
                    try {
                        jedis = sentinelPool.getResource(); //从池中获取Redis连接
                        int index = new Random().nextInt(100000);
                        String key = "k-" + index;
                        String value = "v" + index;
                        jedis.set(key, value);
                        logger.info("{} value is {}",key,jedis.get(key));
                        TimeUnit.SECONDS.sleep(2);
                    } catch(Exception e) {
                        //当Redis的master被kill掉,这里会一直报错,时间为:
                        //sentinel down-after-milliseconds mymaster 5000 指定的时间
                        logger.error(e.getMessage(),e); 
                    } finally {
                        if(jedis != null)
                            jedis.close(); //并不是真正的关闭,而是放回池子里
                    }
                }
            
        }
    
    }
    
    

    虽然名称是JedisSentinelPool,但并不是有个sentinelPool,每次都从SentinelPool中取。这个名称仅仅是为了区分JedisPool而已,JedisSentinelPool本质上还是客户端取的master。

    端口号没配对,报如下错误
    All sentinels down, cannot determine where is mymaster master is running...

    4. 模拟master挂掉,查看Jedis客户端的状态
    1. ps aux | grep redis
    2. kill -9 master_pid
    3. 程序报异常 大约down-after-milliseconds指定的时间
    4. 然后程序可以重新正常运行
    5. 查看配置文件:
      1)新的master配置文件中没有了:replicaof 127.0.0.1 7000
      2)所有sentinel配置文件中 master的IP地址和端口号被修改
      3)挂掉的主节点依然会
      redis-cli -p newMasterIP info sentinel
      发现slave节点依然是2,说明挂掉的主节点依然被sentinel监控着,一旦重启及被重新拉入集群中作为一个slave

    六、实现原理

    1. 三个定时任务
     1) 每10秒每个sentinel对master和slave执行info操作。目的:

       (1)发现slave
       (2)确认主从关系

     2) 每2秒每个sentinel通过master节点的channel交换信息(pub / sub)

       (1)通过_sentinel_:hello频道交互
       (2)交互对节点的看法和自身信息

     3) 每一秒每个sentinel会对其他sentinel和Redis执行ping
    2. 主观下线和客观下线
    # quorum    英[ˈkwɔːrəm]  法定人数
    #写的是master,其实也是对slave的一个要求
    sentinel monitor <master-name> <ip> <redis-port> <quorum>
    sentinel monitor mymaster 127.0.0.1 7000 2
    
    sentinel down-after-milliseconds <master-name> <milliseconds>
    sentinel down-after-milliseconds mymaster 30000
    #sentinel每一秒对master和slave 进行ping,如果超过30秒还没收到回复,就主观判断其下线
    

    主观下线:每个sentinel节点对Redis节点失败的“偏见”

    由于网络分区等其他原因,可能当前的sentinel和Redis节点是不通的,或者网络是有问题的,但是你不能根据这个就断定Redis节点down掉了,这是一种“偏见”。怎么样做到公平呢?即通过客观下线:quorum个sentinel节点对Redis节点失败达成共识。通过第二个定时任务来达成共识的。

    客观下线:所有sentinel节点对Redis节点失败“达成共识”(超过quorum个统一)

    sentinel之间发送sentinel is-master-down-by-addr 询问其他sentinel关于Redis节点是否down掉,是否需要做客观下线。对于slave主观下线就行,没必要做客观下线,因为它不需要做故障转移。当同意的sentinel个数达到了quorum指定的法定人数时,即可做客观下线。

    3. leader选举
    • 原因:只有一个sentinel节点完成故障转移
    • 选举:通过 sentinel is-master-down-by-addr命令希望成为leader
    1. 每个主观下线的sentinel节点向其他sentinel节点发送命令,要求将它设置为leader。
    2. 收到命令的sentinel节点如果没有同意过其他sentinel节点发送的命令,那么将同意该请求,否则拒绝。
    3. 如果该sentinel节点发现自己的票数已经超过sentinel集合半数且超过quorum,那么它将成为leader。
    4. 如果此过程中有多个sentinel节点成为了leader,那么将等待一段时间重新选举。
    
    4. 故障转移
    1. 从slave节点中选出一个“合适的”节点作为master节点
    2. 对上面的节点执行slaveof no one 命令让其成为master节点
    3. 向剩余slave节点发送命令,让他们成为新master节点的slave节点,
       复制规则和parallel-syncs参数有关
    ----------------------------------------
    例如:1主3从,主挂了,选出一个slave做master,还有2个slave,如果parallel-synchrs=2,
    则2个从同时做复制操作。其实在master端是做了优化的,只做一次rdb的生成。
    但是会同时去完成2个slave的rdb文件发送,有网络开销【要结合实际当中的master的内存大小来设置】)。
    ----------------------------------------
    1) 资源合理使用和保护:parallel-synchrs=1,那么就是顺序执行,逐个slave进行copy。
    2)如果读写分离,你想快速恢:parallel-synchrs的值就设置大一点。
    
    4. 更新原来的master节点配置为slave,并通过定时任务对其保持“关注”,
       当其恢复后命令它去复制新的master节点。
    
    5. 选择“合适的”slave节点作为新的master节点
    1. 选择slave-priority 最高的节点,如果存在则返回,不存在则继续。(一般不配置;若某台机器的配置较高,可以将那台机器上的slave节点的优先级设高些。)
      1. 选择复制偏移量最大的slave节点(复制最完整),如果存在则返回,不存在则继续。
      1. 选择runId最小的slave节点,即:启动最早的节点。

    六、常见开发运维问题

    1. 节点运维(偏运维)
    • 机器下线:例如过保等情况
    • 机器性能不足:例如CPU、内存、硬盘、网络等
    • 节点自身故障:例如服务不稳定等
    1)节点下线

    (1)主节点:手动故障转移,忽略了主观下线,客观下线,领导者选举

    sentinel failover <mastername>
    

    (2)从节点:临时下线还是永久下线?例如是否做一些清理工作。但是要考虑读写分离。

    2)节点上线

    (1)主节点:sentinel failover mastername进行上线
    (2)从节点:slaveof 即可,sentinel节点可以感知
    (3)sentinel节点:参考其它sentinel节点启动即可

    2. 高可用读写分离(偏开发)

    读写分离的问题:从节点下线的时候,无法完成客户端感知新节点。

    七、本章回顾总结


    • Redis Sentinel是Redis的高可用实现方案,故障发现、故障自动转移、配置中心、客户端通知。
    • Sentinel是从Redis2.8版本才开始正式生产可用,之前版本生产不可用
    • 尽可能在不同物理机上部署Redis节点
    • Redis Sentinel中Sentinel节点数最好>=3且最好为奇数
    • Redis Sentinel中Sentinel节点与普通节点没什么区别。
    • 客户端初始化时连接到的是Sentinel节点集合,不再是具体的Redis节点,但Sentinel只是配置中心,不是代理。第一次启用的时候拿master节点,而不是每一次都要询问它。
    • Redis Sentinel通过三个定时任务实现了Sentinel节点对于主节点、从节点、其余sentinel节点的监控。
    • Redis Sentinel在对节点做失败判定时分为主观下线和客观下线。
    • 看懂Redis Sentinel故障转移日志对于Redis Sentinel以及问题排查非常有帮助。
    • Redis Sentinel 实现读写分离高可用可以依赖Sentinel节点的消息通知,获取Redis 数据节点的状态变化。(即:自己写代码拓展RedisSentinelPool中的)

    三个消息:
    +switch-master:切换为主节点(从节点晋升主节点)
    +convert-to-slave:切换为从节点
    +sdown:主观下线

    相关文章

      网友评论

        本文标题:Redis第1️⃣9️⃣课 哨兵Sentinel(HA)

        本文链接:https://www.haomeiwen.com/subject/wgeiaqtx.html