Redis学习笔记五、哨兵(Sentinel)

哨兵(Sentinel)

  从本篇文章开始总结哨兵相关的内容,哨兵相关的知识点分为哨兵系统、启动并初始化Sentinel、Sentinel通信三个部分。

1. 哨兵系统

  Redis2.8开始提供了Sentinel架构,哨兵是Redis的高可用(high availability)解决方案:由一个或者多个Sentinel实例组成的Sentinel系统可以监视任意多个主服务器,以及这些主服务器属下的所有从服务器,并在被监视的主服务器进入下线状态时,自动将下线主服务器属下的某个从服务器升级为新的主服务器,然后由新的主服务器代替已下线的主服务器继续处理命令请求。

1.1. 主节点故障转移

  假设现在server1为主服务器,当server1的下线时长超过用户设定的下线时长的上限时,Sentinel系统就会对server1进行故障转移操作,分为三步:

  (1)首先,Sentinel系统会挑选server1属下的其中一个从服务器,并将这个被选中的从服务器升级为新的主服务器。
  (2)之后,Sentinel系统会向server1属下的所有从服务器发送新的复制指令,让他们成为新的主服务器的从服务器,当所有从服务器都开始复制新的主服务器时,故障转移操作执行完毕。
  (3)另外,Sentinel系统还会继续监视已下线的server1,并在它重新上线时,将他设置为新的主服务器的从服务器。

2. 启动并初始化Sentinel

  可以使用redis-sentinel sentinel.conf命令启动一个Sentinel。Sentinel其实就是一个特殊的Redis服务器,它和普通的服务器有些区别,比如它不需要数据库,所以不需要RDB和AOF文件相关操作。

启动一个Sentinel会执行以下操作:

  • 初始化服务器
  • 将普通Redis服务器使用的代码替换成Sentinel专用代码
  • 初始化Sentinel状态
  • 根据sentinel.conf,初始化Sentinel的监视主服务器列表
  • 创建连向主服务器的网络连接

2.1. 初始化服务器

  Sentinel本质上是一个运行在特殊模式下的Redis服务器,故启动Sentinel的第一步,就是初始化一个普通的Redis服务器。
  Sentinel的任何和普通Redis服务器的任务不同,因此初始化过程也并不完全一致。例如,Sentinel不需要向普通Redis服务器那样,初始化时不需要载入RDB文件或AOF文件来还原数据库状态。

2.2. 使用Sentinel专用代码

  启动Sentinel还需要将一部分普通的Redis服务器使用的代码换成Sentinel专用代码。例如,普通服务器使用redis.h/REDIS_SERVERPORT常量的值作为服务器端口,#define REDIS_SENTINEL_PORT 6379。Sentinel则使用sentinel.c/REDIS_SENTINEL_PROT常量作为服务器端口,#define REDIS_SENTINEL_PORT 26379

  另外,普通Redis服务器使用redis.c/redisCommandTable作为服务器命令表,而Sentinel则使用sentinel.c/sentinelcmds作为服务器的命令表。

  sentinelcmds命令表也解释了为什么在Sentinel模式下,Redis服务器不能执行注入SET、DBSIZE、EVAL等,因为服务器根本没有在命令表中载入这些命令。客户端对Sentinel的命令总共有七个,分别是PING、SENTINEL、INFO、SUBSCRIBE、UNSUBSCRIBE、PSUBSCRIBE、PUNSUBSCRIBE

2.3. 初始化Sentinel状态

  sentinel.c/sentinelState结构体保存了服务器中所有和Sentinel功能有关的状态,服务器会初始化一个sentinelState结构体。

	/* Sentinel 的状态结构 */
	struct sentinelState {
	
	    // 当前纪元
	    uint64_t current_epoch;     /* Current epoch. */
	
	    // 保存了所有被这个 sentinel 监视的主服务器
	    // 字典的键是主服务器的名字
	    // 字典的值则是一个指向 sentinelRedisInstance 结构的指针
	    dict *masters;      /* Dictionary of master sentinelRedisInstances.
	                           Key is the instance name, value is the
	                           sentinelRedisInstance structure pointer. */
	
	    // 是否进入了 TILT 模式?
	    int tilt;           /* Are we in TILT mode? */
	
	    // 目前正在执行的脚本的数量
	    int running_scripts;    /* Number of scripts in execution right now. */
	
	    // 进入 TILT 模式的时间
	    mstime_t tilt_start_time;   /* When TITL started. */
	
	    // 最后一次执行时间处理器的时间
	    mstime_t previous_time;     /* Last time we ran the time handler. */
	
	    // 一个 FIFO 队列,包含了所有需要执行的用户脚本
	    list *scripts_queue;    /* Queue of user scripts to execute. */
	
	} sentinel;

2.4. 初始化Sentinel状态的master属性

  Sentinel状态中masters字典,记录了所有被Sentinel监视的主服务器相关信息。字典的键时被监视主服务器的名字,而字典的值则是被监视主服务器对应的sentinel.c/SentinelRedisInstance结构。

  每个sentinelRedisInstance结构代表一个被Sentinel监视的Redis服务器实例,该实例可以是主服务器、从服务器或另外一个Sentinel。

2.5. 创建连向主服务器的网络连接

  初始化Sentinel最后一步是创建连向被监视主服务器的网络连接,Sentinel将成为主服务器的客户端。

  Sentinel会创建两个连向主服务器的异步网络连接:(1)命令连接,专门用于向主服务器发送命令,并接收命令回复。(2)订阅连接,专门用于订阅主服务器的_sentinel_:hello频道。

3. Sentinel通信

  Sentinel通过命令连接和订阅连接与被监视主服务器进行通信。

3.1. 获取主服务器信息

  Sentinel默认会以10秒一次的频率,通过命令连接向被监视的主服务器发送INFO命令,获取的信息包括两个方面:1. 主服务器本身的信息 2. 主服务器属下所有从服务器的信息。

  Sentinel根据run_id域和role域记录的信息,对主服务器的实例结构进行更新。例如,主服务器重启后,run_id发生变化。主服务器返回的从服务器信息,会被用于更新主服务器结构体中的slaves字典,该字典记录了主服务器属下从服务器的名单。字典的键由Sentinel自动设置,格式为ip:port,字典的值是从服务器对应的实例。

3.2. 获取从服务器信息

  Sentinel默认会以10秒一次的频率,通过命令连接向从服务器发送INFO命令。当Sentinel发现主服务器有新的从服务器出现时,Sentinel会为这个新的从服务器创建实例,同时建立连接到从服务器的命令连接和订阅连接。创建命令连接之后,Sentinel默认每十秒一次,通过命令连接向从服务器发送INFO命令。

3.3. 向主服务器和从服务器发送信息

  默认情况下,Sentinel会以每2秒一次的频率,通过命令连接向所有被监视的主服务器发送命令,该命令向服务器的_sentinel_:hello频道发送一条消息。

3.4. 接收来自主服务器和从服务器的频道信息

  当Sentinel与一个主服务器或者从服务器建立起订阅连接之后,Sentinel就会通过订阅连接,向服务器发送命令SUBSCRIBE _sentinel_:hello,Sentinel对_sentinel_:hello频道的订阅会一直持续到Sentinel与服务器的连接断开为止。
  Sentinel既通过命令连接向服务器的_sentinel_:hello频道发送信息,又通过订阅连接从服务器的_sentinel_:hello频道接收信息。
  对于监视同一个服务器的多个Sentinel来说,一个Sentinel发送的信息会被其他Sentinel接收到,这些信息会被用于更新其他Sentinel对发送信息Sentinel的认知,也会被用于更新其他Sentinel对被监视服务器的认知。

3.5. 检测主观下线状态

  默认情况下,Sentinel会以每秒一次的频率向所有与它创建连接的实例发送PING命令,并通过实例返回的PING命令恢复来判断实例是否在线。

3.6. 检测客观下线状态

  当Sentinel将一个主服务器判断为主观下线之后,为了确认这个主服务器是否真的下线,它会向同样监视这一主服务器的其他Sentinel进行询问,当Sentinel从其他Sentinel哪里收到足够数量已下线判断之后,就会将从服务器判定为客观下线,并对主服务器执行故障转移操作。

3.7. 选举领头Sentinel

  当一个服务器被判断为客观下线时,监视这个下线主服务器的各个Sentinel会进行协商,选出一个领头Sentinel,并由领头Sentinel对下线主服务器执行故障转移操作。

3.8. 故障转移

  选举出领头Sentinel之后,领头Sentinel将对已下线的主服务器执行故障转移操作。

故障转移操作执行以下三个步骤:

  1. 在已下线主服务器属下所有从服务器里,挑选出一个,转为主服务器。
  2. 将已下线主服务器属下的所有从服务器改为复制新的主服务器。
  3. 将已下线主服务器设置为新的主服务器的从服务器,当这个旧的主服务器重新上线后,它就称为新的主服务器的从服务器。

4. 参考文献

[1] 黄健宏.Redis设计与实现[M].北京:机械工业出版社

你可能感兴趣的:(Redis,redis,sentinel)