Redis高可用架构之Sentinel
声明:本文系整理文章
原文链接:http://shift-alt-ctrl.iteye.com/blog/1884370
Redis sentinel(哨兵)模块已经被集成在redis2.4+的版本中,主要为 Redis 主从架构(M-S)的系统提供:
监控:系统中master/slave 可用性监控
通知:当被监控的redis实例出问题时,通知应用程序
自动故障转移,M-S角色转换等能力。从一个方面说是提高了redis集群的可用性.
Redis Sentinel 是一个分布式系统,你可以在一个系统中运行多个 Sentinel 进程(progress),这些进程使用流言协议(gossip protocols)来接收关于master是否下线的信息,并使用投票协议(agreement protocols)来决定是否执行自动故障迁移,以及选择哪个从服务器作为新的主服务器。
sentinel的一些设计思路和zookeeper非常类似,事实上,你可以不使用sentinel,而是自己开发一个监控redis的zk客户端也能够完成相应的设计要求。
一、环境部署
准备3个redis服务,简单构建一个小的M-S环境(伪分布式,如果是全分布式,配置信息适度修改即可)。它们各自的redis.conf配置项中,除了port不同外,要求其他的配置完全一样(包括aof/snap,memory,rename以及授权密码等)。
原因是基于sentinel做故障转移,所有的server运行机制都必须一样,它们只不过在运行时”角色”不同,而且它们的角色可能在故障时会被转换;slave在某些时刻也会成为master,尽管在一般情况下,slave的数据持久方式经常采取snapshot,而master为aof,不过基于sentinel之后,slave和master均要采取aof(通过bgsave,手动触发snapshot备份)。
1) redis.conf:
redis-0:默认为master
port 6379
##授权密码,请各个配置保持一致
requirepass 012_345^678-90
masterauth 012_345^678-90
##暂且禁用指令重命名
##rename-command
##开启AOF,禁用snapshot
appendonly yes
save “”
##slaveof no one
slave-read-only yes
redis-1:通过启动参数配置为slave,配置文件保持独立
port 6479
slaveof 127.0.0.1 6379
##-----------其他配置和master保持一致-----------##
redis-2:通过启动参数配置为slave,配置文件保持独立
port 6579
slaveof 127.0.0.1 6379
##-----------其他配置和master保持一致-----------##
2) sentinel.conf
请首先在各个redis服务中sentinel.conf 同目录下新建 local-sentinel.conf,并将复制如下配置信息。
redis-0:
##sentinel实例之间的通讯端口
port 26379
##此指令最后一个参数为<quorum>,
##表示选举时至少有quorum个sentinel实例推选某redis,它才能成为master
sentinel monitor def_master 127.0.0.1 6379 2
sentinel auth-pass def_master 012_345^678-90
sentinel down-after-milliseconds def_master 30000
sentinel can-failover def_master yes
sentinel parallel-syncs def_master 1
sentinel failover-timeout def_master 900000
redis-1:
port 26479
##--------其他配置同上-------##
redis-2:
port 26579
##--------其他配置同上-------#
3) 启动与检测
redis-0:默认为master
> ./redis-server --include ../redis.conf
##启动sentinel组件
> ./redis-sentinel ../local-sentinel.conf
按照上述指令,依次启动redis-0,redis-1,redis-2。
在启动redis-1和redis-2的时候,你会发现在redis-0的sentinel控制台会输出”+sentinel …”字样,表示有新的sentinel实例加入到监控。不过此处需要提醒,首次构建sentinel环境时,必须首先启动master机器。
扩展:实际上sentinel只是一个运行在特殊模式下的 Redis 服务器, 因此也可以在启动一个普通 Redis 服务器时通过给定 —sentinel 选项来启动 Redis Sentinel:redis-server /path/to/sentinel.conf —sentinel
此后你可以使用任意一个”redis-cli”窗口,输入”INFO”命令,可以查看当前server的状态:
> ./redis-cli -h 127.0.0.1 -p 6379
##如下为打印信息摘要:
#Replication
role:master
connected_salves:2
slave0:127.0.0.1,6479,online
slave1:127.0.0.1.6579,online
“INFO”指令将会打印完整的服务信息,包括集群,我们只需要关注”replication”部分,这部分信息将会告诉我们”当前server的角色”以及指向它的所有的slave信息。可以通过在任何一个slave上,使用”INFO”指令获得当前slave所指向的master信息。实际上sentinel组件也是通过向master实例发送 “INFO”指令 来获取集群信息。
当上述部署环境稳定后,我们直接关闭redis-0,在等待”down-after-milliseconds”秒之后(30秒),redis-0/redis-1/redis-2的sentinel 窗口会立即打印 “+sdown” “+odown” “+failover” “+selected-slave” “+promoted-slave” “+slave-reconf” 等等一系列指令。这些指令标明当master失效后,sentinel组件进行failover的过程。
当环境再次稳定后,我们发现,redis-1被提升(“promoted”)为master,且redis-2也通过”slave-reconf”过程之后跟随了redis-1.
如果此时想再次让redis-0加入集群,你需要首先通过”INFO”指令找到当前的masterip + port,并在启动指令中明确指明slaveof参数:
./redis-server --include ../redis.conf --slaveof 127.0.0.1 6479
sentinel实例需要全程处于启动状态,如果只启动server而不启动相应的sentinel,仍然不能确保server能够正确的被监控和管理。
核心过程:
A)启动master实例
B)依次启动各个slaves实例,使用“--slaveof”参数。到此为止,一个默认的M-S拓扑结构已经构建完成,接下来我们使用sentinels监控集群状况。
C)依次启动每个redis实例上的sentinel进程。(实际上不需要启动所有redis对应的sentinel进程,但是最终启动的进程个数不得小于sentinel.conf文件中<quorum>个)
D)因为每个sentinel实例的配置文件中,都指定了初始master的地址和端口,所以它们可以监控master的状态。
E)每个sentinel可以通过向master实例发送“INFO”指令(间歇性,以确保新的slaves加入集群),来获取此master所有的slaves信息。“INFO”指令是sentinels获取M-S拓扑状态的途径,每个sentinel都会与所有的redis实例建立连接,以便监控实例的存活情况。
F)master上会有一个专门的topic用于保存sentinel的状态信息,每个sentinel启动后,都会向master的topic中发布自己的信息,那么其他sentinel将会收到消息;同时每个sentinel也订阅此topic,用于检测所有sentinels的状态变更。所有的sentinels将通过topic用于互相发现。同时一旦发现新的sentinel加入,其他sentinel则立即与其建立连接,便于此后的选举。
二、sentinel原理
名词解释:SDOWN & ODOWN
SDOWN:subjectively down,直接翻译的为”主观”失效,即当前sentinel实例认为某个redis服务为”不可用”状态.
ODOWN:objectively down,直接翻译为”客观”失效,即多个sentinel实例都认为master处于”SDOWN”状态,那么此时master将处于ODOWN。ODOWN可以简单理解为master已经被sentinel确定为”不可用”,将会开启failover.
SDOWN适用于master和slave,但是ODOWN只会使用于master;当slave失效超过”down-after-milliseconds”后,那么所有sentinel实例都会将其标记为”SDOWN”。
1) SDOWN与ODOWN转换过程:
每个sentinel实例在启动后,都会和已知的 master/slaves 以及其他sentinels建立TCP连接,并周期性发送PING(默认为1秒)
在交互中,如果redis-server无法在”down-after-milliseconds”时间内响应或者响应错误信息,都会被认为此redis-server处于SDOWN状态
(☆)如果 2)中SDOWN的server为master,那么此时sentinel实例将会向其他sentinel间歇性(一秒)发送”is-master-down-by-addr
“指令并获取响应信息,如果足够多的sentinel实例检测到master处于SDOWN,那么此时当前sentinel实例标记master为ODOWN… 其他sentinel实例做同样的交互操作。(配置项 “sentinel monitor “,如果检测到master处于SDOWN状态的slave个数达到 ,那么此时此sentinel实例将会认为master处于ODOWN) 经过上述过程后,所有的sentinel对master失效达成一致后,开始failover.
2) Sentinel与slaves的”自动发现”机制:
系统中多个 Sentinel 之间可以互相检查对方的可用性(发送”PING”),并进行信息交互(”ODOWN”和”failover”过程)。但无须为每个 Sentinel 分别设置其他 Sentinel 的地址, 因为 Sentinel 可以通过发布与订阅功能来自动发现正在监视相同主服务器的其他 Sentinel , 这一功能是通过向频道 “__sentinel__:hello” 发布/订阅信息来实现的:
127.0.0.1:6379> SUBSCRIBE __sentinel__:hello
1) "message"
2) "__sentinel__:hello"
3) "127.0.0.1,26579,0adbb77ca63f72088f031894fcb54e608351feb0,0,mymaster,127.0.0.1,6379,0"
可以发现pub的信息中含有当前Sentinel的ip+port。
每个sentinel实例均会周期性地(2秒)向”__sentinel__:hello”主题中发布自己的ip+port,目的就是让后续加入集群的sentinel实例也能或得到自己的信息。
此外,启动Sentinel事也不必手动配置master下的所有slave, 因为 Sentinel 可以通过”INFO”指令获得当前master中已有的slave列表,并和slave建立链接并通过PING检测其存活性。
这一过程(自动发现)依赖于master的存活。
3) Leader选举:
在failover的过程中,仍然需要一个“Leader”来调度整个过程:master的选举以及slave的重配置(reconf)和同步。当集群中有多个sentinel实例时,如何选举其中一个sentinel为leader呢?
这一个过程是通过配置文件中“can-failover”“quorum”参数,以及“is-master-down-by-addr”指令配合来完成的。
A) “can-failover”用来表明当前sentinel是否可以参与“failover”过程,如果为“YES”则表明它将有能力参与“Leader”的选举,否则它将作为“Observer”,observer参与leader选举投票但不能被选举;
B) “quorum”不仅用来控制master ODOWN状态确认,同时还用来确定选举leader时最小“赞同票”数;
C) “is-master-down-by-addr”,在上文中以及提到,它可以用来检测“ip + port”的master是否已经处于SDOWN状态,不过此指令不仅能够获得master是否处于SDOWN,同时它还额外的返回当前sentinel本地“投票选举”的Leader信息(runid,理解为Sentinel运行id,每次运行时都会产生不同的runid)
每个sentinel实例都持有其他的sentinels信息,在Leader选举过程中,sentinel实例将从所有的sentinels集合中去除“can-failover = no”和状态为SDOWN的sentinels,在剩余的sentinels列表中按照runid按照“字典”顺序排序后,取出runid最小的sentinel实例,并将它“投票选举”为Leader,并在其他sentinel发送的“is-master-down-by-addr”指令时将推选的runid追加到响应中。每个sentinel实例都会检测“is-master-down-by-addr”的响应结果,如果“投票选举”的leader为自己,且状态正常的sentinels实例中,“赞同”自己的sentinel个数不小于(>=) 50% + 1,且不小与
在sentinel.conf文件中,我们期望有足够多的sentinel实例配置“can-failover yes”,这样能够确保当leader失效时,能够选举某个sentinel为leader,以便进行failover。如果leader无法产生,比如较少的sentinels实例有效,那么failover过程将无法继续。
4) failover过程
在Leader触发failover之前,首先wait数秒(随机0~5),以便让其他sentinel实例准备和调整(有可能多个leader??)。如果一切正常,那么leader就需要开始将一个salve提升为master,此slave必须为状态良好(不能处于SDOWN/ODOWN状态)且权重值最低的(redis.conf中)。当master身份被确认后,开始failover
A)“+failover-triggered”: Leader开始进行failover,此后紧跟着“+failover-state-wait-start”,wait数秒。
B)“+failover-state-select-slave”: Leader开始查找合适的slave
C)“+selected-slave”: 已经找到合适的slave
D) “+failover-state-sen-slaveof-noone”: Leader向slave发送“slaveof no one”指令,此时slave已经完成角色转换,此slave即为master
E) “+failover-state-wait-promotition”: 等待其他sentinel将该slave提升(promoted)为master
F)“+promoted-slave”:OK,都提升了
G)“+failover-state-reconf-slaves”: 开始对slaves进行reconfig操作。
H)“+slave-reconf-sent”:向指定的slave发送“slaveof”指令,告知此slave跟随新的master
I)“+slave-reconf-inprog”: 此slave正在执行slaveof + SYNC过程,如过slave收到“+slave-reconf-sent”之后将会执行slaveof操作。
J)“+slave-reconf-done”: 此slave同步完成,此后leader可以继续下一个slave的reconfig操作。循环G)
K)“+failover-end”: 故障转移结束
L)“+switch-master”:故障转移成功后,各个sentinel实例开始监控新的master。
三、Sentinel.conf详解
运行一个 Sentinel 所需的最少配置如下所示:
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 60000
sentinel failover-timeout mymaster 180000
sentinel parallel-syncs mymaster 1
sentinel monitor resque 192.168.1.3 6380 4
sentinel down-after-milliseconds resque 10000
sentinel failover-timeout resque 180000
sentinel parallel-syncs resque 5
第一行配置指示 Sentinel 去监视一个名为 mymaster 的主服务器,这个主服务器的 IP 地址为 127.0.0.1,端口号为 6379 ,而将这个主服务器判断为失效至少需要 2 个 Sentinel 同意 (只要同意 Sentinel 的数量不达标,自动故障迁移就不会执行)。
不过要注意,无论你设置要多少个 Sentinel 同意才能判断一个服务器失效,一个 Sentinel 都需要获得系统中多数(majority) Sentinel 的支持,才能发起一次自动故障迁移。换句话说, 在只有少数(minority) Sentinel 进程正常运作的情况下, Sentinel 是不能执行自动故障迁移的。
其他配置项的基本格式如下:
sentinel <选项的名字> <主服务器的名字> <选项的值>
down-after-milliseconds: Sentinel 认为服务器已经断线所需的毫秒数。如果服务器在给定的毫秒数之内, 没有返回 Sentinel 发送的 PING 命令的回复, 或者返回一个错误, 那么 Sentinel 将这个服务器标记为主观下线。只有在足够数量的 Sentinel (这个值在第一行配置了)都将一个服务器标记为主观下线之后, 服务器才会被标记为客观下线, 这时自动故障迁移才会执行。
failover-timeout:
parallel-syncs: 故障迁移时,最多可以有多少个slave同时和新的master进行同步。尽管复制过程的绝大部分步骤都不会阻塞slave,但slave在载入master发来的 RDB 文件时, 仍然会造成从服务器在一段时间内不能处理命令请求,如果全部从服务器一起对新的主服务器进行同步, 那么就可能会造成所有从服务器在短时间内全部不可用的情况出现。你可以通过将这个值设为 1 来保证每次只有一个从服务器处于不能处理命令请求的状态。
附 sentinel.conf 配置:
##sentinel实例之间的通讯端口
##redis-0
port 26379
##sentinel需要监控的master信息:<mastername> <masterIP> <masterPort> <quorum>
##<quorum>应该小于集群中slave的个数,只有当至少<quorum>个sentinel实例提交"master失效"
##才会认为master为O_DWON("客观"失效)
sentinel monitor def_master 127.0.0.1 6379 2
sentinel auth-pass def_master 012_345^678-90
##master被当前sentinel实例认定为“失效”的间隔时间
##如果当前sentinel与master直接的通讯中,在指定时间内没有响应或者响应错误代码,那么
##当前sentinel就认为master失效(SDOWN,“主观”失效)
##<mastername> <millseconds>
##默认为30秒
sentinel down-after-milliseconds def_master 30000
##当前sentinel实例是否允许实施“failover”(故障转移)
##no表示当前sentinel为“观察者”(只参与"投票".不参与实施failover),
##全局中至少有一个为yes
sentinel can-failover def_master yes
##当新master产生时,同时进行“slaveof”到新master并进行“SYNC”的slave个数。
##默认为1,建议保持默认值
##在salve执行salveof与同步时,将会终止客户端请求。
##此值较大,意味着“集群”终止客户端请求的时间总和和较大。
##此值较小,意味着“集群”在故障转移期间,多个salve向客户端提供服务时仍然使用旧数据。
sentinel parallel-syncs def_master 1
##failover过期时间,当failover开始后,在此时间内仍然没有触发任何failover操作,
##当前sentinel将会认为此次failoer失败。
sentinel failover-timeout def_master 900000
##当failover时,可以指定一个“通知”脚本用来告知系统管理员,当前集群的情况。
##脚本被允许执行的最大时间为60秒,如果超时,脚本将会被终止(KILL)
##脚本执行的结果:
## 1 -> 稍后重试,最大重试次数为10;
## 2 -> 执行结束,无需重试
##sentinel notification-script mymaster /var/redis/notify.sh
##failover之后重配置客户端,执行脚本时会传递大量参数,请参考相关文档
# sentinel client-reconfig-script <master-name> <script-path>
更详细信息,需要参考src/sentinel.c源码。配置文件加载过程参见方法:sentinelHandlerConfiguration(..)
四、Jedis客户端与Setinel
Set<String> sentinels = new HashSet<String>(16);
sentinels.add("127.0.0.1:26379");//集群中所有sentinels的地址
sentinels.add("127.0.0.1:26479");
sentinels.add("127.0.0.1:26579");
GenericObjectPoolConfig config = new GenericObjectPoolConfig();
config.setMaxTotal(32);
// setinel客户端提供了master自动发现功能
JedisSentinelPool jedisSentinelPool = new JedisSentinelPool("def_master",sentinels,config,"012_345^678-90");
Jedis jedis = jedisSentinelPool.getResource();
try{
//
jedis.set("key","value");
} finally {
jedisSentinelPool.returnResource(jedis);
}
jedisSentinelPool.close();
JedisSentinelPool将遍历sentinels列表,并尝试与每个sentinel建立连接直到成功为止;如果建立连接成功,就向此sentinel发送“get-master-addr-by-name”指令,获取master的位置,并建立与master的连接。
此后JedisSentinelPool还会启动一个监测线程,用于监测master的存活情况,如果master角色迁移,则重新获取新的master地址,并重新初始化连接池。注意JedisSentinelPool并没有提供“M-S”下读写分离的设计,即读写操作均在master上发生。不过很遗憾,我觉得还是需要一种客户端解决方案,能够实现读写分离。
五、归结
1、sentinel进程,也是一个redis进程。
2、集群中需要至少三个sentinal进程,才能有效的决策master的状态。
3、sentinels之间,需要选举出leader,来监管failover的过程和结果。
4、sentinels之间会互相建立连接,通过特殊的端口通讯;sentinels与redis实例也会建立连接,用于检查redis实例的存活性。
5、sentinels互相发现,是通过在master上特殊的topic中发布各自的address信息来实现的,每个sentinel都会订阅此topic,用于发现其他sentinel的加入或者离开。
6、当sentinel发现master不可用时(SDOWN),会相互交互信息,当足够的sentinel都检测到master不可用时,leader将会判定此master下线的事实(ODOWN),并进行failover。
7、在master失效后,sentinel的leader负责failover,将根据slaves的数据新鲜程度、权重、server ID等做决策,选举出新的master,即选举数据最新的slave作为master。
更多详细信息可参考:
官方文档:http://redis.cn/topics/sentinel.html
Redis哨兵原理:https://my.oschina.net/u/172871/blog/596976
还没有评论,来说两句吧...