Sentinel(哨兵)是 Redis 的高可用解决方案:由一个或多个 Sentinel 实例组成的 Sentinel 系统可以监视任意多个主服务器,以及这些主服务器属下的所有从服务器,并在被监视的主服务器进入下线状态时,自动将下线主服务器属下的某个从服务器升级为新的主服务器,然后由新的主服务器代替已下线的主服务器继续处理命令请求。
服务器与 Sentinel 系统的关系如下图所示(出自《Redis设计与实现第二版》第十六章:Sentinel):
当 serverl 的下线时长超过用户设定的下线时长上限时,Sentinel 系统就会对 serverl 执行故障转移操作:
- 首先, Sentinel 系统会挑选 serverl 属下的其中一个从服务器,并将这个被选中的从服务器升级为新的主服务器。
- 之后, sentinel 系统会向 serverl 属下的所有从服务器发送新的复制指令,让它们成为新的主服务器的从服务器,当所有从服务器都开始复制新的主服务器时,故障转移操作执行完毕。故障转移过程如下图所示(出自《Redis设计与实现第二版》第十六章:Sentinel):
- 另外, sentinel 还会继续监视已下线的 serverl,并在它重新上线时,将它设置为新的主服务器的从服务器。如下图所示(出自《Redis设计与实现第二版》第十六章:Sentinel):
启动并初始化 Sentinel
启动 Sentinel 的命令如下:
redis-sentinel /path/to/your/sentinel.conf 或
redis-server /path/to/your/sentinel.conf --sentinel
当一个 Sentinel 启动时,他需要执行以下步骤:
-
初始化服务器。
因为 Sentinel 本质上只是一个运行在特殊模式下的 Redis 服务器,所以需要初始化一个普通的 Redis 服务器。 -
将普通 Redis 服务器使用的代码替换成 Sentinel 专用代码。
将一部分普通 Redis 服务器使用的代码替换成 Sentinel 专用代码。比如,端口、命令表等。 -
初始化 Sentinel 状态。
-
根据给定的配置文件,初始化 Sentinel 的监视主服务器列表。
-
创建连向主服务器的网络连接。
Sentinel 会创建两个连向主服务器的异步网络连接(Sentinel 将成为主服务器的客户端,可以向主服务器发送命令,并从命令回复中获取相关信息。),如下:
- 命令连接:这个连接专门向主服务器发送命令,并接受命令回复。
- 订阅连接:这个连接专门用于订阅主服务器的 __sentinel__:hello 频道
获取主服务器的信息
Sentinel 默认会以每十秒一次的频率,通过命令连接向被监视的主服务器发送 INFO 命令,并通过分析 INFO 命令的回复来获取当前主服务器和从服务器的的信息。如下图所示(出自《Redis设计与实现第二版》第十六章:Sentinel):
Sentinel 通过解析主服务器返回的信息,可以获取以下两方面的信息:
- 关于主服务器本身的信息。
- 主服务器属下所有从服务器的信息,比如:ip、port。
获取从服务器的信息
当 Sentinel 发现主服务器有新的从服务器时,Sentinel 除了会为这个新的从服务器创建相应的实例结构之外,Sentinel 还会创建连接到从服务器的命令和订阅连接。如下图所示(出自《Redis设计与实现第二版》第十六章:Sentinel):
创建连接之后,Sentinel 会默认每十秒一次的频率通过命令连接向从服务器发送 INFO 命令,并获得如下信息:从服务器的运行 ID、主服务器的 IP:port、主从服务器的连接状态、从服务器的优先级、从服务器的复制偏移量等。
向主服务器和从服务器发送和接收信息
在默认情况下, Sentinel 会以每两秒一次的频率,通过命令连接向所有被监视的主从服务器发送如下格式的命令:
PUBLISH \_\_sentinel__:hello "<s_ip>,<s_port>,<s_runid>,<s_epoch>,<m_name>,<m_ip>,<m_port><m_epoch>"
这条命令向服务器的 __sentinel__:hello 频道发送了一条消息,信息内容的参数如下图所示(出自《Redis设计与实现第二版》第十六章:Sentinel):
当 Sentinel 与一个主服务器或者从服务器建立订阅连接后, Sentinel 就会通过订阅连接,向服务器发送一下命令:
SUBSCRIBE __sentinel__:hello
表示 Sentinel 对 __sentinel__:hello 频道的订阅会一直持续到 Sentinel 与服务器的连接断开为止。
也就是说: 对于每个与 Sentinel 连接的服务器, Sentinel 既通过命令连接向服务器的 __sentinel__:hello 频道发送消息,又通过订阅连接从服务器的 __sentinel__:hello 频道接收消息,如下图所示(出自《Redis设计与实现第二版》第十六章:Sentinel):
检测主观下线状态
在默认情况下,Sentinel 会以每秒一次的频率向所有与它创建了命令连接的实例(包括主服务器、从服务器、其他Sentinel)发送 PING 命令,并通过实例返回的 PING 命令回复来判断该实例是否在线。如下图所示(出自《Redis设计与实现第二版》第十六章:Sentinel):
实例对 PING 命令的回复可以分为以下两种情况:
- 有效回复:实例返回 +PONG、-LOADING、-MASTERDOWN 三种回复中的一种。
- 无效回复:其他回复。
如果一个实例在 down-after-milliseconds 毫秒(配置文件中的值)内,连续向 Sentinel 返回无效回复,那么该 Sentinel 便认为这个实例已经进入主观下线状态。
注意:对于监视同一个服务器的多个 Sentinel 来说,这些 Sentinel 所设置的 down-after-milliseconds 的值也可能不同。因此,当一个 Sentinel 将主服务器判断为主观下线时,其他 Sentinel 可能仍然会认为主服务器处于在线状态。
检测客观下线状态
当 Sentinel 将一个主服务器判断为主观下线之后,为了确认这个主服务器是否真的下线了,它会向同样监视这一主服务器的其他 Sentinel 进行询问,看它们是否也认为主服务器已经进入了下线状态(可以是主观下线或者客观下线)。当 Sentinel 从其他 Sentinel 那里接收到足够数量的已下线判断之后, Sentinel 就会将从服务器判定为客观下线,并对主服务器执行故障转移操作。
客观下线状态的判断条件如下:
当认为主服务器已经进入下线专题的 Sentinel 的数量,超过 Sentinel 配置中设置的 quorum 参数的值,那么该 Sentinel 就会认为主服务器已经进入客观下线状态。
注意:不同 Sentinel 判断客观下线的条件可能不同:对于监视同一个主服务器的多个 Sentinel 来说,他们讲主服务器判断为客观下线的条件可能也不同,当一个 Sentinel 将主服务器判断为客观下线时,其他 Sentinel 可能并不是那么认为的。
选举领头 Sentinel
当一个主服务器被判断为客观下线时,监视这个下线主服务器的各个 Sentinel 会进行协商,选举出一个领头 Sentinel,并由领头 Sentinel 对下线主服务器执行故障转移操作。
故障转移
在选举产生出领头 Sentinel 之后,领头 Sentinel 将对已下线的主服务器执行故障转移操作,该操作包含以下三个步骤:
- 在已下线主服务器属下的所有从服务器里面,挑选出一个从服务器,并将其转换为主服务器。
- 让已下线主服务器属下的所有从服务器改为复制新的主服务器。
- 将已下线主服务器设置为新的主服务器的从服务器,当这个旧的主服务器重新上线时,它就会成为新的主服务器的从服务器。
选出新的主服务器
故障转移的第一步就是在已下线的主服务器属下的所有从服务器中,挑选出一个状态良好、数据完整的从服务器,然后向这个从服务器发送 SLAVEOF no one 命令,将这个从服务器转换为主服务器。如下图所示(出自《Redis设计与实现第二版》第十六章:Sentinel):
修改从服务器的复制目标
当新的主服务器出现之后,领头 Sentinel 下一步要做的就是,让已下线的主服务器属下的所有从服务器去复制新的主服务器(向从服务器发送 SLAVEOF 命令)。如下图所示(出自《Redis设计与实现第二版》第十六章:Sentinel):
此时各个服务器以及领头 Sentinel 如下图所示(出自《Redis设计与实现第二版》第十六章:Sentinel):
将旧的主服务器变为从服务器
故障转移操作最后要做的就是,当将已下线的主服务器从新上线时设置为新的主服务器的从服务器。如下图所示(出自《Redis设计与实现第二版》第十六章:Sentinel):