在上一章我们提到过,如果Redis服务所在的机器硬盘损坏了,即使我们进行了持久化,数据仍然会丢失,所以为了避免这种单点故障,Redis提供了多机热备方案也就是主从复制, 但主从复制存在的一个问题是发生故障时,无法自动进行主从切换恢复使用。针对这个问题,Redis提供了哨兵机制,可以在Redis 主服务器发生故障时自动选择一个Redis 从服务器切换为新的主服务器,继续对外提供服务,进一步提高了系统的高可用性。
Redis Sentinel,即Redis哨兵,在Redis 2.8版本开始引入,由一个或多个Sentinel 实例组成的Sentinel 系统可以 监视任意多个主服务器,以及这些主服务器附属的所有从服务器,并在被监视的主服务器进入下线状态时,自动将下线主服务器属下的某个从服务器升级为新的主服务器。关于哨兵功能描述如下:
监控(Monitoring): Sentinel 会不断地检查你的主服务器和从服务器是否运作正常。提醒(Notification): 当被监控的某个 Redis 服务器出现问题时, Sentinel 可以通过 API 向管理员或者其他应用程序发送通知。配置提供者(Configuration provider):客户端在初始化时,通过连接哨兵来获得当前Redis服务的主节点地址。自动故障迁移(Automatic failover): 当一个主服务器不能正常工作时, Sentinel 会开始一次自动故障迁移操作, 它会将失效主服务器的其中一个从服务器升级为新的主服务器, 并让失效主服务器的其他从服务器改为复制新的主服务器; 当客户端试图连接失效的主服务器时, 集群也会向客户端返回新主服务器的地址, 使得集群可以使用新主服务器代替失效服务器。哨兵架构图如下,
当主服务器server1发生故障掉线以后,
在从服务器中,挑选新的主机,比如server2升为新的主机后,
这里我们通过一个简单的哨兵系统,一主一从一哨兵来演示下,当主节点发生故障时,哨兵的监控和自动故障转移功能。
为了方便起见,我们将用一台服务器复制三个bin实例,并依次修改目录名为master、slave、sentinel进行演示,
主机:这里我们使用默认配置就好,端口为6379
从机:从机只需要指定自己的主节点,端口号即可,其它使用默认配置,其中从机端口号6380,
replicaof 127.0.0.1 6379
哨兵:哨兵本质上是一个特殊的redis节点,它的配置需要注意下,修改的是sentinel.conf文件,这里我们修改端口号为26380,然后指定监控的主节点,sentinel monitor mymaster 127.0.0.1 6379 1,该含义为该哨兵节点监控的主节点是127.0.0.1 6379,这里我们给这个主节点起了个名字叫做mymaster,当节点发生故障时需要有个判断过程,最后的数字1代表法定人数,表示至少需要1个哨兵确定该节点发生了故障后,才可以进行故障迁移。
启动之后,需要检查下主从是否正常,哨兵是否正常。
关于主从的验证,可以通过连接主节点,通过info replication (当然也可以通过数据同步进行验证)命令查看信息,包括当前节点的角色,已连接的从节点信息等,显示如图所示,
关于哨兵的验证,可以连接到哨兵服务器,通过info sentinel命令查看,会列举出当前监控的主节点信息等,显示如图
这里我们演示主节点6379掉线以后,哨兵是否能够进行故障转移,将6380选为新的主节点,继续对外提供服务。
第一步,通过ps -ef|grep redis 查找主节点进程号,然后通过kill命令将主机kill掉,这里由于我采用的前端启动方式,所以直接退出启动界面即可退出服务,如图
第二步,一段时间后,我们通过客户端连接到6380,然后执行info replication查看,这时可以看到,之前的从节点6380此时已变为了master,且当前主节点下没有从节点
第三步,我们重新启动6379,这时候通过info replication命令查看,6379已经变成了6380的从节点了
第四步, 在故障转移过程中,哨兵和主从节点的配置文件都会被改写。 观察故障修复之后的新主节点6380和新从节点6379的配置文件会发现,6380起初配置的replicaof 127.0.0.1 6379 没有了,而在6379配置文件中增加了replicaof 127.0.0.1 6380这样的配置。
通过java程序连接Redis,我们只需要连接哨兵节点即可,在整个过程中,代码不需要显式的指定主、从节点地址;代码中对故障转移也没有任何指定,在哨兵完成故障迁移后自动的切换连接到主节点,这里我们以Jedis 为例给出代码示例,
public void testRedisSentinel() throws Exception { 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 pool = new JedisSentinelPool(masterName, sentinels); Jedis jedis = pool.getResource(); jedis.set("key1", "hello"); pool.close(); }哨兵配置搭建的过程中需要注意下,
(1)哨兵本质上也是redis节点;
(2)哨兵是基于主从复制的,故障发现和迁移由哨兵自主完成;
(3)每个哨兵,只需要配置需要监控的主节点即可,通过主节点可以感知到其他的哨兵和从节点;
(4)哨兵故障迁移阶段,实际上是重写各个节点配置文件的过程;
(5)本例中,只是配置了一主一从一哨兵,实际中,一个哨兵可以配置监控多个主节点,通过配置多条sentinel monitor即可。
(6)如果是使用代码连接redis的话,只需要连接哨兵节点集合即可。
我们前面提到过,哨兵的作用就是监控节点运作是否正常,如果主节点发生故障,哨兵会自动进行故障迁移,保证服务的可用性。其中会涉及一些重要的概念,
定时监控,一定是需要定时进行监测来完成,所以需要定时任务哨兵需要一个判断机制,来判断一个节点是否发生故障,这里面存在一个主观下线和客观下线的概念当确定了故障后,迁移是如何完成的?比如配置了多个哨兵,由谁完成具体的迁移工作,这里涉及到哨兵leader的选举流程下面我们来详细的介绍下这些核心概念。
每个哨兵节点维护了3个定时任务。定时任务的功能分别如下:
向主从节点发送info命令获取最新的主从结构;
每个哨兵节点每10秒会向主节点和从节点发送info命令获取新拓扑结构图,哨兵配置时只要配置对主节点的监控即可,通过向主节点发送info,获取从节点的信息,当有新的从节点加入时可以快速的感知到; 当一个主服务器被 Sentinel 标记为客观下线时, Sentinel 向下线主服务器的所有从服务器发送 INFO 命令的频率会从 10 秒一次改为每秒一次。
通过发布订阅功能获取其他哨兵节点的信息;
每个哨兵节点每隔2秒会向redis数据节点的指定频道上发送该哨兵对于主节点的判断以及当前哨兵节点的信息,同时每个哨兵节点也会订阅该频道,来了解其它哨兵节点的信息及对主节点的判断,其实就是通过消息publish和subscribe来完成的。
通过向其他节点发送ping命令进行心跳检测,判断是否下线;
每个哨兵每隔1秒会向主节点、从节点及其余哨兵节点发送一次ping命令做一次心跳检测,这个也是哨兵用来判断节点是否正常的重要依据。
所谓主观下线(SDOWN),就是在心跳检测的定时任务中,如果其它节点超过一定时间没有回复,就是单个sentinel主观的认为某个服务下线(有可能是接收不到订阅,网络不通等原因)。 sentinel会以每秒一次的频率向所有与其建立了命令连接的实例(master,从服务,其他sentinel)发 ping命令,通过判断ping回复是有效回复,还是无效回复来判断实例是否在线。
sentinel配置文件中的down-after-milliseconds设置了判断主观下线的时间长度,如果实例在downafter-milliseconds毫秒内,返回的都是无效回复,那么sentinel会认为该实例已(主观)下线,修改其 flags状态为SRI_S_DOWN。如果多个sentinel监视一个服务,有可能存在多个sentinel的down-aftermilliseconds配置不同,这个在实际生产中要注意。
当主观下线的节点是主节点时,此时该哨兵会通过指令sentinel is-master-down-by-addr寻求其它哨兵节点对主节点的判断,如果其他的哨兵也认为主节点主观线下了,且当认为主观下线的票数超过了quorum(选举)个数,此时哨兵节点则认为该主节点确实有问题,这样就客观下线了, 也就是客观下线 ODOWN。
在一般情况下, 每个 Sentinel 会以每 10 秒一次的频率向它已知的所有主服务器和从服务器发送 INFO 命令。 当一个主服务器被 Sentinel 标记为客观下线时, Sentinel 向下线主服务器的所有从服务器发送 INFO 命令的频率会从 10 秒一次改为每秒一次。
需要注意的是,客观下线是主节点才有的概念;如果从节点和哨兵节点发生故障,被哨兵主观下线后,不会再有后续的客观下线和故障转移操作。
如果主节点被判定为客观下线之后,各个哨兵节点会进行协商,选取一个哨兵节点作为leader来完成后面的故障转移工作,选举出一个 leader的流程如下:
a)每个在线的哨兵节点都可以成为领导者,当它(比如哨兵2)确认主节点下线时,会向其它哨兵发is-master-down-by-addr命令,征求判断并要求将自己设置为领导者,由领导者处理故障转移;
b)当其它哨兵收到此命令时,可以同意或者拒绝它成为领导者;
c)如果哨兵2发现自己在选举的票数大于等于quorum时,将成为领导者,如果没有超过, 继续选举…………
法定人数(quorum)在哨兵中有两层含义。
第一层含义为:作为判断主节点客观下线的依据,只有一个Master处于客观下线状态时才会开始执行切换。
第二层含义为:假设有5个哨兵,quorum配置为4。首先,判断 客观下线需要4个哨兵才能认定。其次,当开始执行切换时,会从5个 哨兵中选择一个leader执行该次选举,此时一个哨兵也必须得到4票才能被选举为leader,而不是3票。
在从节点中选择新的主节点,sentinel状态数据结构中保存了主服务的所有从节点信息,哨兵leader按照如下的规则在从节点列表中挑选出新的主节点,流程如下
过滤掉主观下线不健康的从节点 ;比如该slave处于主观下线状态,则不能被选中;如果该slave 5s之内没有有效回复ping命令或者与主服务器断开时间过长,则不能被选中;根据优先级选择slave-priority/ replica-priority高的节点,(replica-priority 0的不选择)如果有则返回;如果根据优先级无法区分,则选择出复制偏移量大的从节点,因为复制偏移量越大则数据复制的越完整,如果有就返回;如果无法根据复制偏移量区分,则选择run_id小的节点;更新主从状态,通过slaveof no one命令,让选出来的从节点成为主节点;并通过slaveof命令让其他节点成为其从节点;将已下线的主节点设置为新的主节点的从节点,当该节点重新上线后,它会成为新的主节点的从节点。需要注意的是,执行切换的哨兵如果发生了故障,那么剩余哨兵会重新选主,并且重新开始执行切换流程。
通过观察哨兵日志可以看到,整个的故障迁移工作流程,
1)sentinel masters:返回该哨兵监控的所有Master的相关信息。
2)SENTINEL MASTER:返回指定名称Master的相关信 息。 3)SENTINEL SLAVES:返回指定名称Master的 所有Slave的相关信息。
4)SENTINEL SENTINELS:返回指定名称Master 的所有哨兵的相关信息。 5)SENTINEL IS-MASTER-DOWN-BY-ADDR :如果runid是*,返回由IP和Port指定的Master 是否处于主观下线状态。如果runid是某个哨兵的ID,则同时会要求 对该runid进行选举投票。
6)SENTINEL RESET:重置所有该哨兵监控的匹配模 式(pattern)的Masters(刷新状态,重新建立各类连接)。
7)SENTINEL GET-MASTER-ADDR-BY-NAME:返回指定名称的Master对应的IP和Port。
8)SENTINEL FAILOVER:对指定的Mmaster手 动强制执行一次切换。 9)SENTINEL MONITOR:指定该 哨兵监听一个Master。
10)SENTINEL flushconfig:将配置文件刷新到磁盘。
11)SENTINEL REMOVE:从监控中去除掉指定名称的 Master。
12)SENTINEL CKQUORUM:根据可用哨兵数量,计 算哨兵可用数量是否满足配置数量(认定客观下线的数量);是否满 足切换数量(即哨兵数量的一半以上)。 13)SENTINEL SET[…]:设置指 定名称的Master的各类参数(例如超时时间等)。
14)SENTINEL SIMULATE-FAILURE…:模 拟崩溃。flag可以为crash-after-election或者crash-after-promotion,分 别代表切换时选举完成主哨兵之后崩溃以及将被选中的从服务器推举 为Master之后崩溃。
(1) sentinel monitor
sentinel monitor是哨兵核心配置,其中:
master-name指定了主节点名称,
ip和redis-port指定了主节点地址,
quorum的作用前面已经分析过,这里不再讨论,建议取值为哨兵数量的一半加1。
(2)sentinel down-after-milliseconds
sentinel down-after-milliseconds与主观下线的判断有关,前面已经分析过,这里不再讨论;
down-after-milliseconds的默认值是30000,即30s;可以根据不同的网络环境和应用要求来调整,值越大,对主观下线的判定会越宽松,好处是误判的可能性小,坏处是故障发现和故障转移的时间变长,客户端等待的时间也会变长。如果应用对可用性要求较高,则可以将值适当调小,当故障发生时尽快完成转移;如果网络环境相对较差,可以适当提高该阈值。
(3) sentinel parallel-syncs
sentinel parallel-syncs与故障转移之后从节点的复制有关:它规定了每次向新的主节点发起复制操作的从节点个数。例如,假设主节点切换完成之后,有3个从节点要向新的主节点发起复制;如果parallel-syncs=1,则从节点会一个一个开始复制;如果parallel-syncs=3,则3个从节点会一起开始复制。
parallel-syncs取值越大,从节点完成复制的时间越快,但是对主节点的网络负载、硬盘负载造成的压力也越大;应根据实际情况设置。如果主节点的负载较低,而从节点对服务可用的要求较高,可以适量增加parallel-syncs取值。parallel-syncs的默认值是1。
(4) sentinel failover-timeout
sentinel failover-timeout与故障转移超时的判断有关,但是该参数不是用来判断整个故障转移阶段的超时,而是其几个子阶段的超时,例如如果主节点晋升从节点时间超过timeout,或从节点向新的主节点发起复制操作的时间,但不包括复制数据的时间,超过timeout,都会导致故障转移超时失败。
failover-timeout的默认值是180000,即180s;如果超时,则下一次该值会变为原来的2倍。
本章节我们主要介绍了关于哨兵的一些使用、作用、相关配置、故障迁移以及监控的基本原理等。
前面提到过,哨兵是在主从复制的基础上,对主节点可以自动的进行故障迁移,提高系统的可用性,但是仍然没有解决写负载等问题。在下一章我们会介绍Redis的集群,不见不散,感谢关注。
《Redis设计与实现》
《Redis设计与源码分析》
http://www.redis.cn/
https://www.cnblogs.com/kismetv/p/9609939.html
一节目主持人访问养牛老王,
主持人:咱们牛场的牛,平时都吃什么啊?
老王:你说黄牛,还是白牛?
主持人:黄牛!
老王:苞米面,苞米gai子!
主持人:那白牛呢?
老王:苞米面,苞米gai子啊!
主持人:那一头牛,一顿能吃多少苞米面,苞米gai子啊?
老王:你说黄牛,还是白牛?
主持人:白牛!
老王:一斤多!
主持人:那黄牛呢?
老王:也是一斤多啊!
主持人:哎?那你为啥同样的问题,非得让我问两遍啊??
老王:因为黄牛是我家的啊!
主持人:那白牛呢?
老王:也是我家的啊!
主持人:emmmmmm。。。。。