在以前的系列文章中介绍了redis的入门、持久化以及复制功能,若是不了解请移步至redis系列进行阅读,固然我也是抱着学习的知识分享,若是有什么问题欢迎指正,也欢迎你们转载。而本次将介绍哨兵集群相关知识,包括哨兵集群部署、哨兵原理、相关配置、故障转移等内容,正由于redis有了哨兵机制,而在不少企业(包括笔者自身的公司)采用的是哨兵模式下的redis主从。html
哨兵(后文统称sentinel)是官方推荐的的高可用(HA)解决方案。在以前的文章中介绍过redis的主从高可用解决方案,这种方案的缺点在于当master故障时候,须要手动进行故障恢复,而sentinel是一个独立运行的进程,它能监控一个或多个主从集群,并能在master故障时候自动进行故障转移,更为理想的是sentinel自己是一个分布式系统,其分布式设计思想有点相似于zookeeper,当某个时候Master故障后,sentinel集群采用Raft算法来选取Leader,故障转移由Leader完成。而对于客户端来讲,操做redis的主节点,咱们只须要询问sentinel,sentinel返回当前可用的master,这样一来客户端不须要关注的切换而引起的客户端配置变动。一个典型的sentinel架构以下图:redis
sentinel的主要功能:算法
配置中心(Configuration provider):若是故障转移发生了,sentinel会返回新的master地址。数据库
本次部署过程当中将分别部署三个哨兵节点来监控一主二从的redis集群,主从的搭建过程能够参考笔者博文《redis系列--主从复制以及redis复制演进》,如下是环境规则:安全
sentinel安装与redis安装过程一致,请参考redis系列文章,而在源码中,redis提供了参考配置示例sentinel.conf(可使用命令grep -E -v ^# sentinel.conf 查看),以下:服务器
port 26379 dir /tmp 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
配置说明:架构
sentinel monitor mymaster 127.0.0.1 6379 2
这行配置表明sentinel监控的master名字叫作mymaster(能够本身取),地址是127.0.0.1,端口是6379。最后一个2表明当sentinel集群中有2个sentinel认为master故障时候才断定master真正不可用。官方把该参数称为quorum,在后续选举领头哨兵时候会用到,在下文将进行介绍。并发
sentinel down-after-milliseconds mymaster 30000
sentinel会向master发送心跳PING来确认master是否存活,若是master在“必定时间范围”内不回应PONG 或者是回复了一个错误消息,那么这个sentinel会主观地(单方面地)认为这个master已经不可用了(subjectively down, 也简称为SDOWN)。而这个down-after-milliseconds就是用来指定这个“必定时间范围”的,单位是毫秒,在这里表示30秒时间内master不回应PONG则主观不可用。分布式
sentinel parallel-syncs mymaster 1
该配置代表在发生failover主备切换时候,最多容许多少个slave同时同步新的master。这个数字越小,完成failover所需的时间就越长,可是若是这个数字越大,就意味着越多的slave由于replication而不可用。能够经过将这个值设为 1 来保证每次只有一个slave处于不能处理命令请求的状态。ide
sentinel failover-timeout mymaster 180000
failover-time超时时间,当failover开始后,在此时间内仍然没有触发任何failover操做,当前sentinel将会认为这次failover失败,单位毫秒。
不难发现关于sentinel的配置都是固定格式以下:
sentinel <option_name> <master_name> <option_value>
启动sentinel的方式有两种,两种方式都必须指定配置文件:
#第一种(推荐) redis-sentinel /path/to/sentinel.conf #第二种 redis-server /path/to/sentinel.conf --sentinel
如下是笔者三个节点的配置文件,并同时拷贝到三个节点进行启动:
bind 10.1.210.32 #IP地址 port 26379 #端口 dir /opt/db/redis # 数据存储目录 daemonize yes #后台运行 logfile /opt/db/redis/sentinel.log #日志 sentinel monitor mymaster 10.1.210.69 6379 2 sentinel down-after-milliseconds mymaster 30000 sentinel parallel-syncs mymaster 1 sentinel failover-timeout mymaster 180000
bind 10.1.210.33 #IP地址 port 26379 #端口 dir /opt/db/redis # 数据存储目录 daemonize yes #后台运行 logfile /opt/db/redis/sentinel.log #日志 sentinel monitor mymaster 10.1.210.69 6379 2 sentinel down-after-milliseconds mymaster 30000 sentinel parallel-syncs mymaster 1 sentinel failover-timeout mymaster 180000
bind 10.1.210.34 #IP地址 port 26379 #端口 dir /opt/db/redis # 数据存储目录 daemonize yes #后台运行 logfile /opt/db/redis/sentinel.log #日志 sentinel monitor mymaster 10.1.210.69 6379 2 sentinel down-after-milliseconds mymaster 30000 sentinel parallel-syncs mymaster 1 sentinel failover-timeout mymaster 180000
经过redis-sentinel /opt/db/redis/sentinel.conf启动每一个sentinel,如下是启动日志(能够发现sentinel自动经过master发现slave和其余sentinel):
此时,一个sentinel集群就搭建完成。
和redis同样,sentinel能够经过客户端使用命令操做,例如查看master状态SENTINEL masters,示例:
如下是全部命令以及解释:
SENTINEL masters #列出全部被监视的master,以及当前master状态 SENTINEL master <master name> #列出指定的master SENTINEL slaves <master name> #列出给定master的全部slave以及slave状态 SENTINEL sentinels <master name> #列出监控指定的master的全部sentinel SENTINEL get-master-addr-by-name <master name> #返回给定master名字的服务器的IP地址和端口号 SENTINEL reset <pattern> #重置全部匹配pattern表达式的master状态 SENTINEL failover <master name> #当msater失效时, 在不询问其余 Sentinel 意见的状况下, 强制开始一次自动故障迁移,可是它会给其余sentinel发送一个最新的配置,其余sentinel会根据这个配置进行更新 SENTINEL ckquorum <master name> #检查当前sentinel的配置可否达到故障切换master所需的数量,此命令可用于检测sentinel部署是否正常,正常返回ok SENTINEL flushconfig #强制sentinel将运行时配置写入磁盘,包括当前sentinel状态
在介绍sentinel原理以前,须要了解的两个概念SDOWN和ODOWN:
从sentinel的角度来看,若是发送了PING心跳后,在必定时间内没有收到合法的回复,就达到了SDOWN的条件。这个时间在配置中经过master-down-after-milliseconds参数配置。
当sentinel发送PING后,如下回复之一都被认为是合法的:
PING replied with +PONG. PING replied with -LOADING error. PING replied with -MASTERDOWN error.
其它任何回复(或者根本没有回复)都是不合法的。
从SDOWN切换到ODOWN不须要任何一致性算法,只须要一个gossip协议:若是一个sentinel收到了足够多的sentinel发来消息告诉它某个master已经down掉了,SDOWN状态就会变成ODOWN状态。若是以后master可用了,这个状态就会相应地被清理掉。
真正进行failover须要一个受权的过程,这个受权的过程便是leader选取过程,可是全部的failover都开始于一个ODOWN状态。ODOWN状态只适用于master,对于不是master的redis节点sentinel之间不须要任何协商,slaves和sentinel不会有ODOWN状态。
一个sentinel启动时会读取配置文件,并经过sentinel monitor <master-name> <ip> <port> <quorum>配置寻找要监控的主数据库,这个配置在以前已经进行详细说明,其中master-name是由一个大小写字母、数字、和“._-”组成的数据库主库名字,为了考虑到主库的IP地址和端口可能在故障切换后发生变化,因此还须要ip和port来标示这个主库。一个哨兵可监控多个主从系统从而造成网状结构,正如在前面简介的图示同样。
sentinel启动后,会与监控的数据库创建两条链接,以下图(主库10.1.210.69:6379与10.1.210.32的sentinel节点两条连接):
这两个链接与普通客户端同样,其中一条链接用来订阅master的__sentinel__:hello频道用于获取其余监控该数据库的sentinel节点信息,另一条用于哨兵按期向主数据库发送INFO等命令获取主库自己信息,缘由在于当客户端进入订阅模式之后只能接受消息,不能发送命令,因此还须要创建一条链接。
与监控的主库创建链接完成后,sentinel定时执行如下操做:
这三个操做贯穿了哨兵整个生命周期,很是重要,也是其原理的核心,因此如下将详细介绍该操做过程。
首先,sentinel启动后,向主库发送INFO命令使得sentinel能够获取当前主库的相关信息(包括运行的ID,复制信息、以及属于该主库的从库节点信息),这也是为何在配置监控时候只须要配置监控的主库信息sentinel就自动找到其对应的从库,进而实现从库的监控。然后和每一个从库一样创建两个链接,这两个链接和上文介绍的与主库的连个链接彻底一致,在此以后,哨兵会每10s定时向已知全部主从发送INFO命令获取信息更新并进行相应操做,好比对新增的从库创建链接并加入监控队列、又或者是主库信息发生变化(由failover引发的)进行信息更新等。
接下来哨兵向master和slave的__sentinel__:hello频道发送信息与一样监控该redis示例的其余哨兵分享本身的信息。发送的消息内容为:
<哨兵地址> ,<哨兵端口>,<哨兵运行的ID>,<哨兵配置的版本>,<主库名称>,<主库地址>,<主库端口>,<主库配置版本>,该消息包含了哨兵基本信息以及监控的主库信息,当其余sentinel收到消息后会判断发消息的哨兵是否是新的哨兵,若是是则将其加入已发现的哨兵列表,并建立一个到其的链接(与数据库不一样)哨兵与哨兵之间只会建立一条链接用于发送PING命令,同时sentinel会判断主数据库的配置版本,若是该版本比记录数据库版本高,则更新主数据库的数据,其做用在后续介绍。
实现了自动发现从数据库和其余sentinel节点后,sentinel后续要作的任务是定时监控这些已经发现的主从节点和sentinel节点是否在线。这种监控实现方式是在经过必定时间间隔发送PING命令实现,时间间隔配置经过down-after-milliseconds指定,当超过down-after-milliseconds配置的时间后,若是被PING的数据库或者sentinel未回复,则哨兵认为其主观下线(主观下线在上面已经介绍了),若是该节点是主库sentinel会进一步进行判断是否须要对其进行故障恢复(failover):sentinel会发送SENTINEL is-master-down-by-addr命令询问其余sentinel节点是否也认为该主库主观下线,若是达到指定数量(在示例配置中也进行了说明,示例配置的是2)时,哨兵会认为其客观下线,并选取领头的哨兵(leader)进行故障恢复,选举过程后续介绍。
选举完零头哨兵后,领头哨兵会开始对主数据库进行故障恢复,这一过程称为failover,在选取新的master时候,sentinel会考虑如下状况:
具体的选取顺序以下:
若是一个slave跟master断开链接已经超过了down-after-milliseconds的10倍,外加master宕机的时长,那么slave就被认为不适合选举为master,计算公式以下:
(down-after-milliseconds * 10) + milliseconds_since_master_is_in_SDOWN_state
接下来会对slave进行排序
选出从库后,零头哨兵将向从数据库发送SLAVEOF NO ONE命令升级其为新的主库,而后在向其余从库发送SLAVEOF命令将从的主库升级到最新的主库,最后更新内部记录将已经中止的主库更新为新的主库的从库,使得当该故障的主库再次恢复时候自动以从库角色继续提供服务,从启动到故障恢复完成这一些列过程便是哨兵的工做的完整流程也是其原理所在。
在原理中说起到了,当sentinel发现主库客观下线时候会进行领头哨兵选举进行故障恢复,其选举算法采用Raft算法,这也为何说其设计思想相似与zookpeer,选举过程大致以下:
配置版本号做用
一样,在原理介绍时候说起到了master的配置版本号,当一个sentinel被受权后,它将会得到宕掉的master的一份最新配置版本号,当failover执行结束之后,这个版本号将会被用于最新的配置。由于大多数sentinel都已经知道该版本号已经被要执行failover的sentinel拿走了,因此其余的sentinel都不能再去使用这个版本号。这意味着,每次failover都会附带有一个独一无二的版本号。咱们将会看到这样作的重要性。
并且,sentinel集群都遵照一个规则:若是sentinel A推荐sentinel B去执行failover,A会等待一段时间后,自行再次去对同一个master执行failover,这个等待的时间是经过failover-timeout配置项去配置的。从这个规则能够看出,sentinel集群中的sentinel不会再同一时刻并发去failover同一个master,第一个进行failover的sentinel若是失败了,另一个将会在必定时间内进行从新进行failover,以此类推。
sentinel保证了活跃性:若是大多数sentinel可以互相通讯,最终将会有一个被受权去进行failover.
sentinel也保证了安全性:每一个试图去failover同一个master的sentinel都会获得一个独一无二的版本号。
snetinel的状态会被持久化地写入sentinel的配置文件中。每次当收到一个新的配置时,或者新建立一个配置时,配置会被持久化到硬盘中,并带上配置的版本戳。这意味着,能够安全的中止和重启sentinel进程。下面是被重写的配置文件截图:
一旦一个sentinel成功地对一个master进行了failover,它将会把关于master的最新配置经过广播形式通知其它sentinel,其它的sentinel则更新对应master的配置,一个faiover要想被成功实行,sentinel必须可以向选为master的slave发送SLAVE OF NO ONE命令,而后可以经过INFO命令看到新master的配置信息。
当将一个slave选举为master并发送SLAVE OF NO ONE`后,即便其它的slave还没针对新master从新配置本身,failover也被认为是成功了的,而后全部sentinels将会发布新的配置信息。
新配在集群中相互传播的方式,就是为何咱们须要当一个sentinel进行failover时必须被受权一个版本号的缘由。
每一个sentinel使用发布/订阅的方式持续地传播master的配置版本信息,配置传播的发布/订阅管道是:__sentinel__:hello,咱们能够经过订阅其频道查看频道中的消息,以下:
由于每个配置都有一个版本号,因此以版本号最大的那个为标准。例如:假设有一个名为mymaster的地址为10.1.210.69:6379。一开始,集群中全部的sentinel都知道这个地址,因而为mymaster的配置打上版本号1。一段时候后mymaster死了,有一个sentinel被受权用版本号2对其进行failover。若是failover成功了,假设地址改成了10.1.210.69:6380,此时配置的版本号为2,进行failover的sentinel会将新配置广播给其余的sentinel,因为其余sentinel维护的版本号为1,发现新配置的版本号为2时,版本号变大了,说明配置更新了,因而就会采用最新的版本号为2的配置。
从redis的入门再到哨兵模式,再到平时使用其API进行相关操做,经过一段时间的研究对redis也算有了必定层次的认识,因此把这些过程都记录下来,分享给其余人,但愿有更多的人不只知道如何使用,更能明白其中的原理,在出问题时候能即便的定位问题。固然可能在文章中可能存在不正确的地方也欢迎你们指正,毕竟没有源码级别的理解。最后可能须要研究的部分就是redis的集群,后续在研究完以后写文章介绍,这也算对redis有一个比较全面的认识。