前言

Github:https://github.com/HealerJean

博客:http://blog.healerjean.com

Redis主从复制下,一旦主节点由于故障不能提供服务需要人工将从节点晋升成主节点,因为代码中可能还是以主节点开发的,需要修改代码修改主节点。同时需要将其他节点的主节点要复制过来,这样肯定是不行,因为需要人工的干预

基本概念

对于Redis许多概念都有不同的名词解释,所以解释Redis Sentinel,Redis高可用实现方案

1、主从复制的问题

下面这两个属于redis分布式问题
1、主节点写能力受到单机的限制
2、主节点的存储能力受到单机的限制

2、Redis Sentinel高可用

它是一个分布式架构,其中包多个Sentinel节点和Redis数据节点,每个Sentinel节点会对数据节点和其余Sentinel节点进行监控,当它发现节点不可达的时候,会节点做下线标识,如果被标识的是主节点,它还会和其他Sentinel商量,当大部分人为主节点不可用的时候,会选举出一个Sentinel节点用来完成主节点的自动故障转移的工作,同时将这个变化通知给应用方,整个过程是自动的。不需要人工来介入

安装和部署

准备工作:

这里部署3个Sentinel(26379、26380、26381)、1个master(6379)、2个slave(6380、6381)

1、配置主节点

解释:daemonize
daemonize:redis采用的是单进程多线程的模式。

当redis.conf中选项daemonize设置成yes时,代表开启守护进程模式。在该模式下,redis会在后台运行,并将进程pid号写入至redis.conf选项pidfile设置的文件中,此时redis将一直运行,除非手动kill该进程。

但当daemonize选项设置成no时,当前界面将进入redis的命令行界面,exit强制退出或者关闭连接工具(putty,xshell等)都会导致redis进程退出。

1、配置

daemonize yes 
logfile "6379.log"  //也就是说redis-server启动之后的日志放到了dir目录下的6379.log
dbfilename "dump-6379.rdb"
dir "/usr/local/redis/dir"

2、启动

MacBook-Pro:src healerjean$ redis-server ../redis.conf

观察/usr/local/redis/dir 目录下面文件6379.log

5928:M 19 Apr 21:01:18.903 * Increased maximum number of open files to 10032 (it was originally set to 256).
                _._                                                  
           _.-``__ ''-._                                             
      _.-``    `.  `_.  ''-._           Redis 3.2.9 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._                                   
 (    '      ,       .-`  | `,    )     Running in standalone mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 6379
 |    `-._   `._    /     _.-'    |     PID: 5928
  `-._    `-._  `-./  _.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |           http://redis.io        
  `-._    `-._`-.__.-'_.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |                                  
  `-._    `-._`-.__.-'_.-'    _.-'                                   
      `-._    `-.__.-'    _.-'                                       
          `-._        _.-'                                           
              `-.__.-'                                               

5928:M 19 Apr 21:01:18.907 # Server started, Redis version 3.2.9
5928:M 19 Apr 21:01:18.907 * The server is now ready to accept connections on port 6379

3、测试下主节点是否可用

healerjean$ redis-cli -h 127.0.0.1 -p 6379 ping
PONG

2、启动两个从节点

6580 和 6381 两个从节点一模一样

2.1、配置从节点

redis.conf

daemonize yes
logfile "6380.log"
dbfilename "dump-6380.rdb"
dir "/usr/local/redis/dir"
slaveof 127.0.0.1 6379

daemonize yes
logfile "6381.log"
dbfilename "dump-6381.rdb"
dir "/usr/local/redis/dir"
slaveof 127.0.0.1 6379

2.2、测试从节点是否启动

MacBook-Pro:src healerjean$ redis-cli -h 127.0.0.1 -p 6380 ping
PONG
MacBook-Pro:src healerjean$ redis-cli -h 127.0.0.1 -p 6381 ping
PONG

2.3、确定主从关系

2.3.1、从视角角度

可以看到从节点8081的主节点为6379

127.0.0.1:6381> info replication
# Replication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up
master_last_io_seconds_ago:5
master_sync_in_progress:0
slave_repl_offset:435
slave_priority:100
slave_read_only:1
connected_slaves:0
master_repl_offset:0
repl_backlog_active:0
repl_backlog_size:1048576
repl_backlog_first_byte_offset:0
repl_backlog_histlen:0
127.0.0.1:6381> 

2.3.2、主视角角度

可以明显的看到下面两个从节点

127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6380,state=online,offset=547,lag=1
slave1:ip=127.0.0.1,port=6381,state=online,offset=547,lag=0
master_repl_offset:547
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:546
127.0.0.1:6379> 


3、部署Sentinel节点

3.1、配置

三个节点的配置完全一致,只不过是端口不同

daemonize yes
logfile "26379.log"
dbfilename "dump-26379.rdb"
dir "/usr/local/redis/dir"
# 2、代表主节点失败至少需要两个sentinel同意
# mymaster为主节点别名 剩下的以后说明
sentinel monitor mymaster 127.0.0.1 6379 2 
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000 

3.2、启动Sentinel节点

healerjean$ redis-sentinel ../redis.conf

log文件中

360:X 19 Apr 21:40:23.377 * Increased maximum number of open files to 10032 (it was originally set to 256).
                _._                                                  
           _.-``__ ''-._                                             
      _.-``    `.  `_.  ''-._           Redis 3.2.9 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._                                   
 (    '      ,       .-`  | `,    )     Running in sentinel mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 26381
 |    `-._   `._    /     _.-'    |     PID: 6360
  `-._    `-._  `-./  _.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |           http://redis.io        
  `-._    `-._`-.__.-'_.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |                                  
  `-._    `-._`-.__.-'_.-'    _.-'                                   
      `-._    `-.__.-'    _.-'                                       
          `-._        _.-'                                           
              `-.__.-'                                               

6360:X 19 Apr 21:40:23.382 # Sentinel ID is b38f8218f0d136dc6f2b18391a88859aafee6a11
6360:X 19 Apr 21:40:23.382 # +monitor master mymaster 127.0.0.1 6379 quorum 2
6360:X 19 Apr 21:40:23.383 * +slave slave 127.0.0.1:6380 127.0.0.1 6380 @ mymaster 127.0.0.1 6379
6360:X 19 Apr 21:40:23.385 * +slave slave 127.0.0.1:6381 127.0.0.1 6381 @ mymaster 127.0.0.1 6379

3.3、查看启动的Sentinel节点信息

可以观察到主节点有一个并且知道了端口号 从节点有2个 Sentinel有3个

info Sentinel

127.0.0.1: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:6379,slaves=2,sentinels=3
127.0.0.1:26379> 

3、建议

1、生产环境中建议Redis Sentinel的所有节点应该分布在不同的物理机器上

2、配置和优化

1、上面Sentinel中的redis.conf配置如下

daemonize yes
logfile "26379.log"
dbfilename "dump-26379.rdb"
dir "/usr/local/redis/dir"
# 2、代表主节点失败至少需要两个sentinel同意
# mymaster为主节点别名 剩下的以后说明
sentinel monitor mymaster 127.0.0.1 6379 2 
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000 

2、其实当我们利用上面的redi.conf启动的时候,就会根据自身监控到的进行修改redis.conf

# 2、代表主节点失败至少需要两个sentinel同意
# mymaster为主节点别名 剩下的以后说明
sentinel myid 3a29be18951c20683dc54c85a79fd8d3eec79ea1
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel config-epoch mymaster 0
sentinel leader-epoch mymaster 0

# Generated by CONFIG REWRITE
sentinel known-slave mymaster 127.0.0.1 6380
sentinel known-slave mymaster 127.0.0.1 6381
sentinel known-sentinel mymaster 127.0.0.1 26381 b38f8218f0d136dc6f2b18391a88859aafee6a11
sentinel known-sentinel mymaster 127.0.0.1 26380 6f3b48dd910ae93566b52f7adfac2a5ef46e52bb
sentinel current-epoch 0

3、Redis安装目录下有个sentinel.conf是默认的Sentinel节点的配置文件,下面开始以它作为例子进行讲解,其实再启动redis.conf的时候,就会将它也进行了修改,只不过没有写入监听信息Generated by CONFIG REWRITE

daemonize yes
logfile "26379.log"
dbfilename "dump-26379.rdb"
dir "/usr/local/redis/dir"
# 2、代表主节点失败至少需要两个sentinel同意
sentinel monitor mymaster 127.0.0.1 6379 2 
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000 

1、sentinel monitor

sentinel monitor mymaster 127.0.0.1 6379 2 

1、一帮情况下我们将它设置为Sentinel的一半+1,这样就保证绝大多数同意下线标识。
2、还记得上面我们说的领导者吧。至少有max(quorun,num(sentinel)/2)+1)个节点参与选举才可以选出领导者,例如 有5个Sentinle节点,quorun 为4,则为至少需要 max(4,3) = 4 才能进行领导者选举 3、虽然这里没有看到基恩空其余从节点和Sentinel信息,因为他会通过主节点获取有关从及其他节点的信息

2、sentinel down-after-milliseconds mymaster 30000

这个是对节点不可达判断的依据, 每个Sentinel节点定期发送ping命令来判断Redis节点和其他Sentinel节点是否可达,如果超过了这个时间就认为是不可达的,条件太严格有误判风险,宽松则更不好

sentinel down-after-milliseconds mymaster 30000

3、sentinel parallel-syncs mymaster 1

当Sentinel节点集合对主节点故障判断一致的时候,Sentinel领导者节点会做出故障转移操作。选出新的主节点,原来的从节点会向新的主节点发起复制操作,这里的参数则是向新的主节点发起复制操作的从节点个数,配置如果比较大则从节点同时向主节点发起复制操作,但是不好,因为比较拥挤。等于1的时候,则从节点会轮询发起复制

sentinel parallel-syncs mymaster 1

4、sentinel failover-timeout mymaster 180000

故障转移超时时间,,如果第一次失败了,则下次其实超时时间为他的2倍

sentinel failover-timeout mymaster 180000 

5、sentinel auth-pass

如果监控的主节点配置了密码,则需要配置主节点的额密码,防止对主节点无法监控

sentinel auth-pass   123456 

6、sentinel notification-script mymaster /var/redis/notify.sh

当sentinel有任何警告级别的事件发生时(比如说redis实例的主观失效和客观失效等等),将会去调用这个脚本,这时这个脚本应该通过邮件,SMS等方式去通知系统管理员关于系统不正常运行的信息。调用该脚本时,将传给脚本两个参数,一个是事件的类型,一个是事件的描述。如果sentinel.conf配置文件中配置了这个脚本路径,那么必须保证这个脚本存在于这个路径,并且是可执行的,否则sentinel无法正常启动成功。

 sentinel notification-script mymaster /var/redis/notify.sh

7、sentinel client-reconfig-script mymaster /var/redis/reconfig.sh

当一个master由于failover故障转移结束的时候,这个脚本将会被调用,通知相关的客户端关于master地址已经发生改变的信息。以下参数将会在调用脚本时传给脚本:

     是用来和旧的master和新的master(即旧的slave)通信的。这个脚本应该是通用的,能被多次调用,不是针对性的。    配置示例:

sentinel client-reconfig-script mymaster /var/redis/reconfig.sh

8、也可以监控多个主节点

3、部署技巧

   1、Sentinel节点不应该部署在一台物理机上,尽管可能通过虚拟机的方式,造成ip不同但是万一电脑出了故障,所有的Sentinel都会受影响
   2、部署至少3个且奇数个数的Sentinel节点,提高准确性
   3、合理选择监控多个主节点。比如是监控多个主节点或者是监控一个主节点,如果是监控多个zhu

4、API

Sentinel节点是一个特殊的Redis节点,有自己专属的API, 

1、sentinel masters

展示所有被监控的主节点的状态已经相关统计信息

有可能监控多个主节点,则展示指定的master则后面加上 mymaster 名字

127.0.0.1:26379> sentinel masters
1)  1) "name"
    2) "mymaster"
    3) "ip"
    4) "127.0.0.1"
    5) "port"
    6) "6379"
    7) "runid"
    8) "5b2cb1975b0e2cdbe531e6169dcf5da93fab3524"
    9) "flags"
   10) "master"
   11) "link-pending-commands"
   12) "0"
   13) "link-refcount"
   14) "1"
   15) "last-ping-sent"
   16) "0"
   17) "last-ok-ping-reply"
   18) "435"
   19) "last-ping-reply"
   20) "435"
   21) "down-after-milliseconds"
   22) "30000"
   23) "info-refresh"
   24) "4852"
   25) "role-reported"
   26) "master"
   27) "role-reported-time"
   28) "350587255"
   29) "config-epoch"
   30) "0"
   31) "num-slaves"
   32) "2"
   33) "num-other-sentinels"
   34) "2"
   35) "quorum"
   36) "2"
   37) "failover-timeout"
   38) "180000"
   39) "parallel-syncs"
   40) "1"
127.0.0.1:26379> 


2、sentinel slaves mymaster

展示置顶master主节点相关的从节点信息

127.0.0.1:26379> sentinel slaves mymaster
1)  1) "name"
    2) "127.0.0.1:6380"
    3) "ip"
    4) "127.0.0.1"
    5) "port"
    6) "6380"
    7) "runid"
    8) "72702995a478ab9986d5eb6f5e4c05002b97fd92"
    9) "flags"
   10) "slave"
   11) "link-pending-commands"
   12) "0"
   13) "link-refcount"
   14) "1"
   15) "last-ping-sent"
   16) "0"
   17) "last-ok-ping-reply"
   18) "250"
   19) "last-ping-reply"
   20) "250"
   21) "down-after-milliseconds"
   22) "30000"
   23) "info-refresh"
   24) "9088"
   25) "role-reported"
   26) "slave"
   27) "role-reported-time"
   28) "350691871"
   29) "master-link-down-time"
   30) "0"
   31) "master-link-status"
   32) "ok"
   33) "master-host"
   34) "127.0.0.1"
   35) "master-port"
   36) "6379"
   37) "slave-priority"
   38) "100"
   39) "slave-repl-offset"
   40) "2760782"
2)  1) "name"
    2) "127.0.0.1:6381"
    3) "ip"
    4) "127.0.0.1"
    5) "port"
    6) "6381"
    7) "runid"
    8) "d478ed3a435c6995db2dd1b8824bc61c25b30636"
    9) "flags"
   10) "slave"
   11) "link-pending-commands"
   12) "0"
   13) "link-refcount"
   14) "1"
   15) "last-ping-sent"
   16) "0"
   17) "last-ok-ping-reply"
   18) "250"
   19) "last-ping-reply"
   20) "250"
   21) "down-after-milliseconds"
   22) "30000"
   23) "info-refresh"
   24) "9088"
   25) "role-reported"
   26) "slave"
   27) "role-reported-time"
   28) "350691870"
   29) "master-link-down-time"
   30) "0"
   31) "master-link-status"
   32) "ok"
   33) "master-host"
   34) "127.0.0.1"
   35) "master-port"
   36) "6379"
   37) "slave-priority"
   38) "100"
   39) "slave-repl-offset"
   40) "2760782"
127.0.0.1:26379> 

  

3、展示置顶master的Sentinel集合

127.0.0.1:26379> sentinel sentinels mymaster
1)  1) "name"
    2) "b38f8218f0d136dc6f2b18391a88859aafee6a11"
    3) "ip"
    4) "127.0.0.1"
    5) "port"
    6) "26381"
    7) "runid"
    8) "b38f8218f0d136dc6f2b18391a88859aafee6a11"
    9) "flags"
   10) "sentinel"
   11) "link-pending-commands"
   12) "0"
   13) "link-refcount"
   14) "1"
   15) "last-ping-sent"
   16) "0"
   17) "last-ok-ping-reply"
   18) "320"
   19) "last-ping-reply"
   20) "320"
   21) "down-after-milliseconds"
   22) "30000"
   23) "last-hello-message"
   24) "1030"
   25) "voted-leader"
   26) "?"
   27) "voted-leader-epoch"
   28) "0"
2)  1) "name"
    2) "6f3b48dd910ae93566b52f7adfac2a5ef46e52bb"
    3) "ip"
    4) "127.0.0.1"
    5) "port"
    6) "26380"
    7) "runid"
    8) "6f3b48dd910ae93566b52f7adfac2a5ef46e52bb"
    9) "flags"
   10) "sentinel"
   11) "link-pending-commands"
   12) "0"
   13) "link-refcount"
   14) "1"
   15) "last-ping-sent"
   16) "0"
   17) "last-ok-ping-reply"
   18) "320"
   19) "last-ping-reply"
   20) "320"
   21) "down-after-milliseconds"
   22) "30000"
   23) "last-hello-message"
   24) "77"
   25) "voted-leader"
   26) "?"
   27) "voted-leader-epoch"
   28) "0"
127.0.0.1:26379> 

4、sentinel reset mymaster

对当前sentinel节点关于主节点mymaster的配置进行重置,包含清除主节点状态(例如:故障转移),重新发现从节点和主节点

sentinel reset mymaster

5、sentinel failover mymaster

可以说相当有用,对指定主节点进行故障转移,(不商量)当故障转移完成之后,其他sentine按照故障转移结果进行更新自身配置,这个命令在后期运维中非常有用

sentinel failover mymaster

5、实现原理

1、三个定时监控任务

1、每隔10秒,每个Sentinel节点会向主节点和从节点发送info命令来获取最新的拓扑结构

1、用来感知其他新加入的主从节点
2、故障转移之后用来更新拓扑信息

2、每隔2秒会对主节点的频道发送该Sentinel对于主节点的判断以及当前Sentinel的信息。同时也会订阅该频道,来了解他们各自关于节点的判断

1、发现新的Sentinel节点并建立连接
2、交换主节点的信息

3、每隔1秒,每隔sentinel节点会向主、从、sentinel及诶单发送ping命令,来确认是不是可达的。

6、主观下线和客观下线

6.1、主观下线

本章之前介绍到,每个Sentinel每隔1秒对主节点、从节点、其他sentinel节点发送ping命令来做心跳检测,当这些超过down-after-millisencondme没有有效回复,则对该节点做失败判定,这个行为就叫做主观下线,那么可以知道这个是当前的sentinel节点的一家之言,有误判的可能

6.2、客观下线

当判定为下线的时候,需要和其他的节点进行商量,才会做出下线处理。这个判断就是客观的。


当一个sentinel节点对主节点做主观下线判断后,回、会向其他sentinel节点发送如下命令

SENTINEL is-master-down-by-addr <masterip> <masterport> <sentinel.current_epoch> <runid>

sentinel.current_epoch 当前配置纪元
runid这个参数有两种类型 
* 与sentinel节点交换关于主节点下线的判断
当runId 等于当前Sentinel节点的runid时候,是当前sentinel节点希望其他sentinel节点成为领导者的请求

举例;发送如下命令,返回结果会包含三个参数

SENTINEL is-master-down-by-addr 127.0.0.1 6379 0 * 

返回结果
down_state 目标sentinel节点对主节点的下线判断,1下线,0在线

leader_runid :当leader_runid等于* 时候、,代表返回结果是用来同意主节点是否不可达,当ledader_runid 等于具体的id代表目标节点同意runid成为领导者
leader_epoch 领导者纪元

6.3、领导者选举

故障转移实际上是一个sentinel节点完成的,所以会进行领导者选举。

1、每个在线的sentinel节点都有资格成为领导者,当他确认主节点主观下线的时候,回向其他节点发送sentinel is-master-down-by-addr 命令要求将自己设置为领导者

2、受到命令的sentinel节点,如果没有同意过其他sentinel节点,将同意它,否则拒绝

3、如果该sentinel节点发送自己得票大于max(quorum,(sentinel)/2+1) 难免他将成为领导者

4、如果没有选出则,将进行下一场选举

7、故障转移

7.1、挑选一个从节点

1、过滤掉不健康的,主观下线,断线,等

2、选择从节点优先级高的slave-proprity,如果存在则返回,不存在则继续

3、选择复制偏移量最大的,存在则返回,不存在则接续

4、选择runid最小的从节点

7.2、将从节点执行slaveof no one命令让他成为主节点

7.3、sentinel会向其它从节点发送命令,让他们成为新主节点的从节点,复制规则和parallel-syncs参数有关

7.4、sentinel结合将原来的主节点更新为从节点,保持它的关注,当其恢复后复制新的主节点信息

8、故障处理

1、节点下线

临时下线

暂时将节点关掉,之后还会重新启动,继续提供服务

永久下线

节点关掉之后不再提供服务,需要做一下清理工作,如删除配置文件,持久化文件,日志文件

主节点下线

在任意一个sentinel节点上执行下面的命令即可,则会自动进行故障转移工作

sentinel fialover mymaseter 

从节点和sentinel下线

下线之后sentinel还会对她进行监控,会造成一定的网络资源浪费

7.2、从节点上线

添加方法:只要加上slaveof [masterip] [masteport] 启动即可 (定时监控10秒)

7.3、添加sentinel节点

添加方法同上,也是配置相关信息即可,sentinel会自动发现(定时监控2秒)

7.4、添加主节点

因为主节点不好掌控,如果需要替换则使用命令手动故障转移

sentinel fialover mymaseter 

8、高可用

在应用方初始化的时候,连接的的是sentinel集合。那么在客户端的时候应该连接从节点集群。这里后期再解释吧!

ContactAuthor