redis sentinel

Sentinel(哨兵)是Redis的高可用(HA)解决方案:

  1. 由一个或多个sentinel实例组成的sentinel系统,可以监控任意多个主服务器(master),以及其下属的从服务器(slavers);
  2. 当主服务器(master)下线时,自动将下线主服务器属下的某个从服务器升级成新的主服务器,由新的master代替下线master继续提供服务;
  3. sentinel系统会向下线主服务器所有从服务器发送新的复制指令,让他们成为新主服务器的从服务器;
  4. sentinel系统会继续监控下线的master,并在其上线时,将它设置为新master的从服务器;

具体说明如下图:如图1,server1为master,server2-4为其slave;当sentinel系统察觉server1下线后,会进行故障转移(如图2),将server2升级为master,并设置server3-4为server2的slave;同时,sentinel会继续监控server1,当其上线后,将server1降级为server2的slave。

一、sentinel原理:

1、启动并初始化sentinel:

1)sentinel本质上是一个运行在特殊模式下的Redis服务器,可以使用下面两种方式启动sentinel服务:

$ redis-sentinel /path/sentinel.conf

$ redis-server /path/sentinel.conf –sentinel

2)初始化sentinel服务:

  1. 当sentinel服务启动后,会初始化一个sentinelState结构,其内部masters字典记录了所有被sentinel监视的主服务器(master)相关信息,这些信息一开始是通过sentinel.conf配置文件中读取的(后面会通过INFO命令的回复进行更新);
  2. sentinel服务器会对所有监视的主服务器(master)创建两个异步网络连接:
  • 命令连接:用来向主服务器发送命令,并接受回复;(sentinel作为主服务器的客户端)
  • 订阅链接:用来订阅主服务器的sentinel:hello频道;

2、sentinel获取主、从服务器信息:

1)获取主服务器信息:
sentinel默认以每10秒一次的频率,通过命令连接向其监视的所有主服务器发送INFO命令,并通过分析命令的回复来获取主、从服务器的信息:

  • 关于主服务器的信息,如:runID、role域记录等;
  • 关于主服务器下属的从服务器信息,包括:ip、port等;(sentinel无需用户在配置文件中提供从服务器的地址信息,就可以自动发现从服务器

获取的主服务器信息,会更新到sentinelState结构内部的masters字典;对于主服务器下属的从服务器信息会被保存到sentinelRedisInstane结构的slaves字典中(如果从服务器已经存在,则更新;不存在则添加)。如下图

说明:

  • 主服务器的实例结构flags属性是SRI_MASTER,而从服务器的为SRI_SLAVE;
  • 主服务器实例结构的name属性是根据sentinel.conf中读取的;而从服务器实例结构的name属性值是ip:port自动设置的;

2)获取从服务器信息:
当sentinel发现主服务器有新的从服务器时,sentinel除了会创建相应的从服务器实例结构外,还会创建从服务器的命令连接和订阅连接。同样,sentinel也会以以每10秒一次的频率,通过命令连接向从服务器发送INFO命令,并分析其回复信息:

  • 从服务器run_id、role、优先级、从服务器的复制偏移量;
  • 主服务器ip、port信息;

这些信息都会更新到从服务器实例结构中。

3、sentinel向主、从服务器发送、接收信息:

1)默认sentinel会以每2秒频率,通过命令连接向所有被监视的主、从服务器发送下面格式的命令(向指定频道发布消息):

1
Publish  _sentinel_:hello <s_ip>,<s_port>,<s_runid>,<s_epoch>,<m_name>,<m_ip>,<m_port>,<m_epoch>

其中,s开头的是sentinel本身信息,m开头的是主服务器信息;

2)接收来自主、从服务器的频道信息:
当sentinel与一个主服务器或者从服务器建立起订阅连接后,sentinel就会通过订阅连接,向服务器发送:subscribe sentinel:hello 命令,该频道的订阅会一直持续到sentinel与服务器连接断开位置。也就是说,对于每个与sentinel连接的服务器,sentinel既通过命令连接向服务器的sentinel:hello频道发布消息;同时,又通过订阅连接从服务器的sentinel:hello频道接收信息。

4、sentinel间通信:
1)对于监视同一个服务器的多个sentinel而言,一个sentinel发送的信息会被其他sentinel接收到,这些信息会被用于更新其他sentinel对于发送信息的sentinel的认知,也会被用于更细其他sentinel对被监视服务器的认知。

假如:sentinel1、sentinel2、sentinel3同时监控一个主服务器,那么sentinel1向sentinel:hello频道发送一条信息(按上面的格式),所有订阅了sentinel:hello频道的sentinel(包括sentinel1自己)都会收到该信息。sentinel收到信息后,先取出sentinel ip、port,并检查sentinel ip、port和自己的是否一样:

  • 如果一样,则不处理(证明是自己发的);
  • 如果不一样,说明是监视同一服务器的其他sentinel发送的,接收者从信息中取出主服务器信息,对相应主服务器实例结构进行更新。

2)更新sentinel字典:

sentinel为主服务器(MASTER)创建的实例结构中,sentinels字典保存除了sentinel本身之外,还保存了所有监视这个主服务器的其他sentinel信息。当一个sentinel接收到其他sentinel信息时(前者为目标sentinel,后者为员sentinel),目标sentinel会从信息中取出:

  • 与sentinel相关的信息:源sentinel的ip、port、run_id、配置元等;
  • 与主服务器相关信息:源sentinel监视的主服务器ip、port、配置元…;

目标sentinel根据主服务器信息,会在自己的sentinelState结构的masters字典中查找对应的主服务器结构,然后根据信息的sentinel部分查找主服务器实例结构的sentinels字典中检查员sentinel是否存在:

  • 如果存在,则更新;
  • 不存在,创建;(说明源sentinel是刚刚开始监视主服务器的)

说明:因为一个sentinel可以通过分析接收到的频道信息来获知其他sentinel的存在,并通过发送频道信息让其他sentinel感知自己,所以用户在使用sentinel时并不需要提供各个sentinel配置信息,监视同一个主服务器的多个sentinel可以自动发现对方。

3)创建连向其他sentinel的命令连接:

当一个sentinel通过频道信息发现一个新的sentinel时,它不仅会为新的sentinel在主服务器的sentinels字典中创建对应结构外,还会创建一个连向新sentinel的命令连接;同样新的sentinel也会创建这个sentinel的命令连接。最终,监视同一个主服务器的多个sentinel会形成一个连接网。

注:

  • 各个sentinel通过命令连接向其他sentinel发送命令请求,来进行信息交互;(确认实例是否客观下线时需要用到);
  • sentinel间不需要订阅连接;

5、sentinel检测主观下线:

默认情况下,sentinel会以每1秒的频率,向所有与它创建了命令连接的实例(主服务器、从服务器、其他sentinel服务器)发送PING命令,并通过回复信息判断实例是否在线。PING的回复分为:

  • 有效回复:实例返回+PING、-LOADING、-MASTERDOWN三种中的一种;
  • 无效回复:除了以上的回复;

1)主观下线:

在sentinel.conf配置文件中,down-after-milliseconds选项指定了sentinel判断实例进入主观下线的时间长度。如果在down-after-milliseconds指定毫秒内,实例连续返回无效回复,那么sentinel认为该实例主观下线,同时修改该实例结构中flags属性为SRI_S_DOWN。
注:down-after-milliseconds选项值可以作为主服务器(master)、从服务器(slaves)、其他sentinel主观下线的条件。

2)多个sentinel设置的主观下线时间长度不等:

对于监视同一个主服务器的多个sentinel来说,这些sentinel在配置文件中设置的down-after-milliseconds选项值可能不同,所以当一个sentinel判断某个实例主观下线后,其他sentinel可能认为该实例仍然在线。例如:

1
2
3
4
5
6
sentinel1配置:
sentinel monitor def_master 127.0.0.1 6379 2
sentinel down-after-milliseconds def_master 30000
sentinel2配置:
sentinel monitor def_master 127.0.0.1 6379 2
sentinel down-after-milliseconds def_master 50000

6、sentinel检测客观下线:

当sentinel将一个主服务器判断为主观下线后,为了确认这个主服务器是否真的下线,他会向同样监视这个主服务器的其他sentinel进行询问,看它们是否认为该主服务器是否下线(主观或者客观)。当sentinel从其他sentinel那里接收到足够数量的已下线判断之后,sentinel就将主服务器判断为客观下线,并对主服务器进行故障转移(failover)。

1)发送sentinel is-master-down-by-addr 命令:
sentinel通过命令连接,向其他sentinel发送sentinel is-master-down-by-addr 命令,其中:

  • s_ip:被认为主观下线的主服务器ip;
  • s_port:被认为主观下线的主服务器port;
  • current_epoch:sentinel当前的配置元信息,用来选举领头sentinel;
  • runid:可以为或者sentinel的runid。当是代表命令仅仅用于检测主服务器的客观下线状态;当为runid时,用来选举领头sentinel;

2)回复sentinel is-master-down-by-addr 命令:
当目标sentinel接收到sentinel is-master-down-by-addr 命令后,会取出主服务器的ip和port,检查其是否已下线,然后向源sentinel返回含有三个参数的Multi bulk回复:

  • down_state:0或1,0代表主服务器下线,1代表在线;
  • eader_runid:或者目标sentinel的局部领头sentinel的runid;当是代表命令仅仅用于检测主服务器的客观下线状态;当为runid时,用来选举领头sentinel;
  • leader_epoch:目标sentinel的局部领头sentinel的配置元;只有在leader_runid不为*时有效,否则始终为0;

3)客观下线:
源sentinel接收到sentinel is-master-down-by-addr 命令的回复后,会统计其他sentinel同意主服务器下线的数量,当这一数量达到sentinel.conf配置文件中指定的值时,sentinel会将主服务器实例结构flags属性设置为S_O_DOWN。例如:
sentinel monitor def_master 127.0.0.1 6379 2
只要两个sentinel认为主服务器已经下线,那么sentinel就认为该主服务器已经下线。

4)不同sentinel判断客观下线的条件可能不同:
对于监视同一个主服务器的多个sentinel来说,这些sentinel在配置文件中设置的quorum肯能不一样,所以当一个sentinel认为主服务器客观下线,其他sentinel可能并不认为。

二、选举领头sentinel:

当一个主服务器被判断成客观下线后,监视这个主服务器(master)的各个sentinel会进行协商,选举一个领头sentinel,并由领头sentinel对下线主服务器执行故障转移。

1、领头sentinel选举算法:

  • 所有在线的sentinel都有资格被选为领头sentinel;
  • 每次进行领头选举之后,不论选举成功?失败,所有sentinel的配置元(config epoch)都会自增一次。配置元实际就是计数器;
  • 在一个配置元里,所有sentinel都有一次将某个sentinel设置为局部领头sentinel的机会,并且局部领头一旦设置,这个配置元里面就不能更改;
  • 每个发现主服务器客观下线的sentinel,都会要求其他sentinel将自己设置为局部领头sentinel;
  • 当一个源sentinel向一个目标sentinel发送sentinel is-master-down-by-addr 命令,并且不为*时(为源sentinel的runid),表示源sentinel要求目标sentinel将前者设置为局部领头sentinel;
  • sentinel设置局部领头sentinel的规则是先到先得:最先向目标sentinel发送设置要求的源sentinel将成为目标sentinel的局部领头sentinel,而之后收到的所有设置都会被目标sentinel拒绝;
  • 目标sentinel在收到sentinel is-master-down-by-addr 命令后,将回复一条带有三个参数的消息,其中leader_runid参数和leader_epoch分别是目标sentinel的局部领头sentinel的runid和配置元;
  • 源sentinel再接受到回复后,会检查回复中的leader_epoch参数和自己的进行对比,如果相同,再取出leader_runid,如果leader_runid和源sentinel一样,那么表示目标sentinel将源sentinel设置成了局部领头sentinel;
  • 如果某一个sentinel被半数以上的sentinel设置成了领头sentinel,那么这个sentinel就成为了领头sentinel;
  • 因为领头sentinel的产生需要半数以上sentinel支持,且每个sentinel在每个配置元中只能设置一次局部领头sentinel,所以一个配置元里只能出现一个领头sentinel;
  • 如果在指定时间里,没有一个sentinel被选为领头sentinel,那么各个sentinel将在一段时间后再次选举,知道选出为止。

2、故障转移:
在选出了领头sentinel后,领头sentinel将对下线的主服务器进行故障转移,步骤:

  1. 在已下线的主服务器属下的所有从服务器中,挑一个从服务器,并将其转换为主服务器;
  2. 让已下线主服务器属下的所有从服务器更改为复制新的主服务器;(通过slave of命令实现)
  3. 将已下线主服务器设置为新的主服务器的从服务器,当这个旧主服务器从新上线后,它就成为新主服务器的从服务器(因为旧主服务器已经下线,所以只能将slave of命令保存到sentinel系统的旧主服务器实例结构中,当旧主服务器重新上线后,sentinel就会向他发送slave of命令);

3、选取新的主服务器:

1)升级主服务器:
从已下线的主服务器属下中,挑一个状态良好、数据完整的从服务器,然后向其发送slave of no one命令,将这个从服务器升级为主服务器。在发送slave of on one后,领头sentinel会以每1秒的频率(平时是每10秒),向新的主服务器发送INFO命令,并观察回复中的role信息,当role从原来的slave变成master后,领头sentinel就认为从服务器已经升级成为主服务器了。

2)选取新的主服务器规则:

  • 领头sentinel将已下线的主服务器的所有从服务器保存到一个列表中,然后按下面进行过滤:
  • 删除列表中所有处于下线或断线转发太的从服务器;
  • 删除列表中所有最近5s内没有回复过领头sentinel的INFO命令的从服务器;
  • 删除所有与已下线的主服务器连接断开超过down-after-millisecondes * 10毫秒的从服务器;

之后,领头sentinel将根据从服务器的优先级排序(选出优先级最高的);如果具有多个相同最高优先级的从服务器,那么再按照从服务器的复制偏移量排序(选出偏移量最大的);如果还有相同的从服务器,那么按照runid进行排序,选出其中runid最小的。