operator

Redis不同集群

2020-03-13  本文已影响0人  holmes000

Redis高可用原理总结

因为所在团队主要负责Infra高可用相关的内容,对项目中所有用到的open source的高可用原理进行了学习。最近学习了一下Redis的高可用原理,这里通过笔记整理一下。

Redis高可用架构

Redis相对于之前的PostgreSQL在高可用方面做的更加成熟,并且实现了分布式的高可用架构,其架构如下图所示:

image

Redis的高可用架构主要分为两个集群,一个是Redis Server集群,另一个是Sentinel集群。

Redis Server集群:

  1. master/slave(1+N)模式。Redis采用master/slave的模式作高可用,一个master可以有多个slave,并且slave还可以级联子slave
  2. 异步同步。出于效率的原因,Redis的同步方式采用的异步同步。master上数据的变化(包括:客户端写数据,key过期和以及其他的操作)都会被sync到slave上。

Sentinel集群的功能:

  1. 监控。对master和slave节点作心跳检测,更新其状态
  2. 通知。当master和slave节点发生故障时,提供API用于通知其他process或者管理者
  3. 自动Failover。当master出现故障时,能够自动failover,promote最佳的slave作为新的master(先比较优先级,然后比较lag,如果都一样则选择最小runid的slave)
  4. 提供访问配置。提供master的ip/port以及密码信息,用于客户端访问master。

Note:

  1. Sentinel是采用Raft协议选举新的master,因此需要建立奇数个Sentinel
  2. 一个Sentinel集群是可以管理多个Redis Server集群(由于Redis是一个单线程的应用,只能使用单核处理,当业务较大时,可以通过创建多个master/slave集群作负载均衡)

重要过程和配置

1. Sentinel初始化和配置

通常Sentinel可以通过下面指令运行:

redis-sentinel /path/to/sentinel.conf 
#or
redis-server /path/to/sentinel.conf --sentinel

Sentinel的配置格式为sentinel <option_name> <master_name> <option_value>,而一个简单的sentinel.conf如下:

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

解释一下其中配置的含义:

  1. sentinel monitor mymaster 127.0.0.1 6379 2表示sentinel监控mymaster集群,其当前master为127.0.0.1,其服务端口为6379,发生failover需要2个及以上的Sentinel同意;
  2. sentinel down-after-milliseconds mymaster 60000表示master超过60s健康监测失败,就认为该master down;
  3. sentinel failover-timeout mymaster 180000当前failover过程超过180s,才认为failover失败,重新选举;
  4. sentinel parallel-syncs mymaster 1表示同一时间只有一个slave可以跟master进行sync(数字设置大,那么可以sync更快)

当然所有配置都可以通过SENTINEL SET command在运行时动态修改。

Ok,当我们通过上面的配置启动Sentinel之后,它会做以下的操作:

  1. 通过配置文件更新mymaster集群的信息;
  2. 连接到mymaster当前master127.0.0.1:6379,创建两个连接:
    • 一个用于发送命令
    • 另一个为master的PUB/SUB的channel—__sentinel__:hello(所有的Sentinel都会在该channel发送hello信息,包含当前集群的最新配置,新加入的Sentinel可以根据该配置更新自己的配置文件);
  3. 通过前面跟master建立的连接,发送INFO命令,并且解析结果,截取所需信息,更新当前集群的topo信息(下面会有详细介绍);
  4. Sentinel会根据添加的slave信息,同样建立两个连接,用于slave的健康监测,failover以及发布配置更新。

Step 3INFO返回的内容如下:

127.0.0.1:6379> INFO
...
run_id:27e26bb8d6d1ec66fa5d8a15e080a17dcb6cb002
...
# Replication
role:master
connected_slaves:2
slave0:ip=172.17.0.6,port=6379,state=online,offset=3578171,lag=1
slave1:ip=172.17.0.8,port=6379,state=online,offset=3578171,lag=1
...

根据上面的信息,Sentinel会修改它的配置文件,添加其slave信息,并且修改epoch,用来表示版本号(新加入的节点会发现自己版本号小于集群中的版本号,会用大版本号的配置更新自己的配置文件):

cat /redis/sentinel.conf
...
sentinel config-epoch mymaster 1
sentinel leader-epoch mymaster 1

sentinel known-slave mymaster 172.17.0.6 6379
sentinel known-slave mymaster 172.17.0.8 6379
sentinel known-sentinel mymaster 172.17.0.7 26379 33b728312e9fbd1323e5b2bcf07ff48aacbc87e4
sentinel known-sentinel mymaster 172.17.0.12 26379 a21447632bbc6bc602f6532103a0fcdeec470356
sentinel current-epoch 1

2. Failover过程

首先说明一下,Sentinel会运行三个定时器:

  1. 每10秒每个sentinel会对master和slave执行之前提到的INFO命令,用于:
    a)发现slave节点
    b)确认主从关系
  2. 每2秒每个sentinel通过PUB/SUB的频道(__sentinel__:hello)交换信息(对节点的"看法"和自身的信息),达成共识。
  3. 每1秒每个sentinel对其他sentinel和redis节点执行PING操作(相互监控),做心跳检测。

一个完整的Failover包含下面的过程:

  1. 对于第3个定时器,当一个Sentinel发现master心跳检测失败超过down-after-milliseconds之后,则会将该master置为SDOWN(主观下线),并且通过PUB/SUB广播(使用的gossip);
  2. 如果足够数量的Sentinel认为该master已经down,则会将master状态置为ODOWN(客观下线);
  3. 置为ODOWN的Sentinel,如果没有给其他leader投过票,则会申请成为candidate,开始请求成为leader,并增加自己的epoch
  4. 在leader选举周期结束之前,收到足够数量投票(超过一半,并且满足quorum数量)的节点就会成为Leader从slave中选取最佳的节点;
  5. 将选取的slave,执行SLAVE OF NO ONE命令,将该slave提升为新master;
  6. 将配置的epoch+1,表示版本号已经更新,并且在PUB/SUB频道内广播该配置。

Note:

  1. 对于上面的第4步,如果定时器结束前,没有节点收到足够票数,则节点会退回follower状态,通过一个随机定时器进行退避,退避过程中没有给其它节点投票,就会又成为candidate。由于是一个随机定时器,所以所有节点都成为candidate的概率比较小,通常一个epoch就可以选出leader;
  2. 如果leader超过failover-timeout时间仍未完成failover,Sentinel集群同样需要重新开始leader选举;
  3. epoch机制是Raft保证时间同步的一个机制,详细原理可以参见Raft的论文

看困了?手动做个failover实验动手验证一下:首先在master节点上执行DEBUG SLEEP 30模拟节点hang住30s,因此Sentinel会进行Failover(为了方便,这里我直接用redis-operator在k8s上部署了一个集群):

➜  ~ kubectl exec -it rfr-redisfailover-0 -- redis-cli DEBUG SLEEP 30

查看任意的Sentinel的log:

kubectl log rfs-redisfailover-64c54477f6-gkqvq -f
1:X 14 Apr 15:36:47.315 # +sdown master mymaster 172.17.0.5 6379
1:X 14 Apr 15:36:47.399 # +odown master mymaster 172.17.0.5 6379 #quorum 2/2
1:X 14 Apr 15:36:47.399 # +new-epoch 2
1:X 14 Apr 15:36:47.399 # +try-failover master mymaster 172.17.0.5 6379
1:X 14 Apr 15:36:47.401 # +vote-for-leader b864933d1fad0ef1dfb58b4faabf75c97f13d91c 2
1:X 14 Apr 15:36:47.404 # 33b728312e9fbd1323e5b2bcf07ff48aacbc87e4 voted for b864933d1fad0ef1dfb58b4faabf75c97f13d91c 2
1:X 14 Apr 15:36:47.404 # a21447632bbc6bc602f6532103a0fcdeec470356 voted for b864933d1fad0ef1dfb58b4faabf75c97f13d91c 2
1:X 14 Apr 15:36:47.477 # +elected-leader master mymaster 172.17.0.5 6379
1:X 14 Apr 15:36:47.477 # +failover-state-select-slave master mymaster 172.17.0.5 6379
1:X 14 Apr 15:36:47.536 # +selected-slave slave 172.17.0.8:6379 172.17.0.8 6379 @ mymaster 172.17.0.5 6379
1:X 14 Apr 15:36:47.536 * +failover-state-send-slaveof-noone slave 172.17.0.8:6379 172.17.0.8 6379 @ mymaster 172.17.0.5 6379
1:X 14 Apr 15:36:47.636 * +failover-state-wait-promotion slave 172.17.0.8:6379 172.17.0.8 6379 @ mymaster 172.17.0.5 6379
1:X 14 Apr 15:36:48.453 # +promoted-slave slave 172.17.0.8:6379 172.17.0.8 6379 @ mymaster 172.17.0.5 6379
1:X 14 Apr 15:36:48.453 # +failover-state-reconf-slaves master mymaster 172.17.0.5 6379
1:X 14 Apr 15:36:48.541 * +slave-reconf-sent slave 172.17.0.6:6379 172.17.0.6 6379 @ mymaster 172.17.0.5 6379
1:X 14 Apr 15:36:49.494 * +slave-reconf-inprog slave 172.17.0.6:6379 172.17.0.6 6379 @ mymaster 172.17.0.5 6379
1:X 14 Apr 15:36:49.495 * +slave-reconf-done slave 172.17.0.6:6379 172.17.0.6 6379 @ mymaster 172.17.0.5 6379
1:X 14 Apr 15:36:49.570 # -odown master mymaster 172.17.0.5 6379
1:X 14 Apr 15:36:49.571 # +failover-end master mymaster 172.17.0.5 6379
1:X 14 Apr 15:36:49.571 # +switch-master mymaster 172.17.0.5 6379 172.17.0.8 6379
1:X 14 Apr 15:36:49.571 * +slave slave 172.17.0.6:6379 172.17.0.6 6379 @ mymaster 172.17.0.8 6379
1:X 14 Apr 15:36:49.572 * +slave slave 172.17.0.5:6379 172.17.0.5 6379 @ mymaster 172.17.0.8 6379
1:X 14 Apr 15:36:54.593 # +sdown slave 172.17.0.5:6379 172.17.0.5 6379 @ mymaster 172.17.0.8 6379
1:X 14 Apr 15:37:12.186 # -sdown slave 172.17.0.5:6379 172.17.0.5 6379 @ mymaster 172.17.0.8 6379

如同前面讲的过程,首先sentinel更新了epoch,并且开始选举leader,然后开始选择最佳的slave,然后promote为master,将之前的master降为slave,更新配置信息,再将之前的slave挂在新的master下面。

附:log的解析格式:<instance-type> <name> <ip> <port> @ <master-name> <master-ip> <master-port>,detail含义:

    +reset-master <instance details> -- 当master被重置时.
    +slave <instance details> -- 当检测到一个slave并添加进slave列表时.
    +failover-state-reconf-slaves <instance details> -- Failover状态变为reconf-slaves状态时
    +failover-detected <instance details> -- 当failover发生时
    +slave-reconf-sent <instance details> -- sentinel发送SLAVEOF命令把它重新配置时
    +slave-reconf-inprog <instance details> -- slave被重新配置为另外一个master的slave,但数据复制还未发生时。
    +slave-reconf-done <instance details> -- slave被重新配置为另外一个master的slave并且数据复制已经与master同步时。
    -dup-sentinel <instance details> -- 删除指定master上的冗余sentinel时 (当一个sentinel重新启动时,可能会发生这个事件).
    +sentinel <instance details> -- 当master增加了一个sentinel时。
    +sdown <instance details> -- 进入SDOWN状态时;
    -sdown <instance details> -- 离开SDOWN状态时。
    +odown <instance details> -- 进入ODOWN状态时。
    -odown <instance details> -- 离开ODOWN状态时。
    +new-epoch <instance details> -- 当前配置版本被更新时。
    +try-failover <instance details> -- 达到failover条件,正等待其他sentinel的选举。
    +elected-leader <instance details> -- 被选举为去执行failover的时候。
    +failover-state-select-slave <instance details> -- 开始要选择一个slave当选新master时。
    no-good-slave <instance details> -- 没有合适的slave来担当新master
    selected-slave <instance details> -- 找到了一个适合的slave来担当新master
    failover-state-send-slaveof-noone <instance details> -- 当把选择为新master的slave的身份进行切换的时候。
    failover-end-for-timeout <instance details> -- failover由于超时而失败时。
    failover-end <instance details> -- failover成功完成时。
    switch-master <master name> <oldip> <oldport> <newip> <newport> -- 当master的地址发生变化时。通常这是客户端最感兴趣的消息了。
    +tilt -- 进入Tilt模式。
    -tilt -- 退出Tilt模式。

3. Client设计Guide Line

由于Redis的集群会发生failover,因此对于client的行为,redis官方给出了下面的Guide Line(当前基本上所有的Redis的客户端都支持Sentinel了)。

服务发现步骤:

  1. 提供Sentinel的地址列表,Client会选择从第一个Sentinel尝试连接(对于Kubernetes,只用一个Headless Service就搞定);
  2. Client通过SENTINEL get-master-addr-by-name master-name命令查询到master节点的信息;
  3. 连接到master节点调用ROLE,验证当前节点是否是真正的master,如果不是就从Sentinel中第二个节点重新开始服务发现

重连处理:
当Client出现连接超时,或者被user中断之后,Redis建议Client重新进行服务发现

Sentinel Failover重连:
当Redis集群发生Failover时,Sentinel会发送CLIENT KILL type normal到Redis节点,强制Client重连。

访问Slaves:
Client可以通过SENTINEL slaves master-name查询slaves的信息,并且通过ROLE命令验证当前slave的状态。

Reference

  1. Sentinel
  2. Replication
  3. Sentinel Clients
  4. Sentinel Spec
  5. Sentinel的一篇blog
上一篇下一篇

猜你喜欢

热点阅读