zoukankan      html  css  js  c++  java
  • Redis15:Sentinel

    Sentinel

    sentinel是redis高可用性的解决方案,一个或多个sentinel实例组成sentinel系统,它可以监视任意多个主服务器以及这些主服务器下的从服务器,当被监视的主服务器进入下线状态时(下线时长超过上限时),自动将下线服务器属下的某个从服务器升级为新的主服务器,如果原来下线的服务器重新上限,sentinel负责将新上线的服务器设置为一个从服务器。

    启动和初始化sentinel

    启动sentinel需要在dos下执行命令:

    redis-sentinel /path/to/your/sentinel.conf
    

    或:

    redis-server /path/to/your/sentinel.conf --sentinel
    

    初始化服务器

    sentinel本质上就是一个特殊的redis服务器,所以启动sentinel的第一步就是初始化一个普通的redis服务器,sentinel在初始化的时候不会载入RDB或AOF文件,sentinel模式下redis服务器主要功能的使用情况如下:

    使用sentinel专用代码

    sentinel内部运行的代码与普通redis服务器不同,比如命令表和普通服务器完全不同,因此很多命令在sentinel是执行不了的。能在客户端对sentinel执行的全部命令有:ping、sentinel、info、subscribe、unsubscribe、psubscribe、punsubscribe。

    初始化sentinel状态结构

    服务器内部会初始化一个sentinelState结构:

    struct sentinelState{
    	//当前纪元
    	uint64_t current_epoch;
    	//保存了所有被sentinel监视的主服务器
    	dict *masters;
    	//是否进入了TILT模式
    	int tilt;
    	//目前正在执行的脚本数量
    	int running_scripts;
    	//进入TILT模式的时间
    	mstime_t tilt_start_time;
    	//最后一次执行时间处理器的时间
    	mstime_t previous_time;
    	//一个FIFO队列,包含了所有需要执行的用户脚本
    	list *scripts_queue;
    }sentinel;
    

    初始化masters属性

    masters是一个字典,输入一个被监视服务器的名字就能找到一个sentinelRedisInstance结构,它代表一个被sentinel监视的redis服务器实例,实例的一部分属性如下:

    typedef struct sentinelRedisInstance{
    	//标识值,记录了实例的类型,以及该实例的当前状态
    	int flags;
    	//实例的名字,主服务器的名字由用户在配置文件中设置,从服务器及sentinel的名字自动设置为ip:port
    	char *name;
    	//实例的运行ID
    	char *runid;
    	//配置纪元
    	uint64_t config_epoch;
    	//实例的地址,sentinelAddr包括ip和端口号
    	sentinelAddr *addr;
    	//实例无响应多少毫秒被判定为主观下线
    	mstime_t down_after_period;
    	//判定这个实例为客观下线需要的支持投票数量
    	int quorum;
    	//执行故障转移操作时,可以同时对新的主服务器进行同步的从服务器数量
    	int parallel_syncs;
    	//刷新故障迁移状态的最大时限
    	mstime_t failover_timeout;
    }sentinelRedisInstance;
    

    masters字典的初始化是根据被载入的sentinel配置文件来进行的,如果配置文件如下:

    那么对应的两个sentinelRedisInstance实例就如下(此时都是主服务器):

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

    对于每个被sentinel监视的主服务器来说,sentinel会创建两个连向主服务器的异步网络连接:

    1、一个是命令连接,专门用于向主服务器发送命令,并接受命令回复

    2、一个是订阅连接,专门用于订阅主服务器的_sentinel_:hello频道(这主要是因为redis发布与订阅功能中,被发送的信息都不会报存在服务器中,如果接受信息的客户端断线就会导致信息丢失,sentinel用一个专门的连接来接受该频道的信息)

    下图就是一个sentinel向被它监视的两个主服务器创建命令连接和订阅连接:

    获取主服务器信息

    sentinel默认会以每10秒一次的频率通过命令连接向被监视的主服务器发送info命令,并通过分析info命令的回复来获取主服务器的当前信息。

    通过分析主服务器返回的info命令回复,sentinel可以获取两个方面的信息:

    1、主服务器的运行ID

    2、主服务器属下所有从服务器的信息,包括ip和端口号

    sentinel根据返回的信息对主服务器的实例结构进行更新,主服务器sentinelRedisInstance实例结构中有一个字段名为slaves,slaves是一个字典,记录了主服务器属下从服务器的名单,根据返回的信息就可以更新这个名单。输入从服务器名,即ip:port,就能找到对应的从服务器实例结构sentinelRedisInstance。

    下图就是某个主服务器实例和它的从服务器实例的关系:

    主从服务器对应的实例结构有一些不同:

    1、主服务器实例结构中的flags属性是SRI_MASTER,从服务器实例结构中的flags属性是SRI_SLAVE。

    2、主服务器实例结构中的name属性的值是用户根据配置文件设置的,从服务器实例结构中的name值是自动设置为ip:port的。

    获取从服务器信息

    sentinel会创建到从服务器的命令连接和订阅连接,这是通过获取主服务器的信息间接得到从服务器的信息从而建立联系的。创建命令连接后,sentinel默认每10秒一次的频率通过命令连接向从服务器发送info命令,根据从服务器的回复sentinel能获取以下信息:

    服务器的运行ID、主服务器的ip和端口、主服务器的连接状态、从服务器的优先级和复制偏移量,根据这些信息sentinel会对从服务器的信息进一步更新。

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

    默认sentinel会以每两秒一次的频率,通过命令连接向所有被监视的主服务器和从服务器发送以下格式的命令:

    publish _sentinel_:hello "<s_ip>,<s_port>,<s_runid>,<s_epoch>,<m_name>,<m_ip>,<m_port>,<m_epoch>"
    

    这条命令向服务器的_sentinel_频道发送了一条信息,信息的内容由多个参数组成,其中以s开头的参数是记录sentinel本身的信息,包括ip、端口、运行ID、配置纪元。以m开头的参数是记录主服务器的信息,如果sentinel正在监视的是主服务器,那么就是它的信息,如果正在监视从服务器,那么这个信息就是它的主服务器的信息,包括主服务器名、ip、端口、配置纪元。

    接受来自主服务器和从服务器的频道信息

    当sentinel与一个主服务器或者从服务器建立起订阅连接后,sentinel就会通过订阅连接向服务器发送以下命令:

    subscribe _sentinel_:hello
    

    sentinel对_sentinel_:hello频道的订阅会一直持续到sentinel与服务器的连接断开为止。sentinel既通过命令连接向服务器的_sentinel_:hello频道发送消息,又通过订阅连接从服务器的_sentinel_:hello频道接受消息:

    对于监视同一个服务器的多个sentinel,一个sentinel发送的信息会发送到对应频道,然后通过频道所有sentinel都会接受到这个信息:

    一个sentinel从频道收到消息时,会对信息进行分析,提取出其中的sentinel ip地址、端口号、运行ID等,如果这个运行ID和自己的一样,说明这条消息是自己发的,此时sentinel会把消息丢弃,如果运行ID和自己的不同,说明这条消息是从监视同一个服务器的其他sentinel发来的,此时sentinel会把消息中的信息提取,然后更新实例结构的信息。

    更新sentinels字典

    在sentinel为某个它监视的主服务器建立的sentinelRedisInstance结构中,有一个sentinels属性,它是一个字典,保存了所有监视这个主服务器的sentinel(除了本sentinel的信息),输入一个sentinel名(格式为ip:port),就能得到一个对应sentinel的实例结构。

    当一个sentinel接受到其他sentinel发来的消息时(通过频道),sentinel会从信息中分析两方面参数:

    1、源sentinel的IP地址、端口号、运行ID和配置纪元

    2、源sentinel正在监视的主服务器名、IP地址、端口号和配置纪元

    根据信息中的主服务器参数,sentinel会在自己sentinel状态中的masters字典中找到对应主服务器的sentinelRedisInstance,然后根据信息中的sentinel参数,检查主服务器实例中的sentinels字典中有没有对应的源sentinel,如果不存在就新建一个,如果已经存在就更新其信息。

    通过这样的机制,一个sentinel可以通过分析频道信息获知其他sentinel的存在,而且通过发送频道信息让其他sentinel知道自己的存在,这样用户在使用sentinel的时候不需要提供每个sentinel的地址信息,监视同一个服务器的多个sentinel可以自动发现对方。

    创建连向其他sentinel的命令连接

    当sentinel通过频道信息发现一个新sentinel时,不仅会为新sentinel在字典中创建对应的实例结构,还会创建一个连向新sentinel的命令连接,最终监视同一主服务器的多个sentinel将形成相互连接的网络:

    通过命令连接相连的sentinel可以通过向其他sentinel发送命令请求来进行信息交换。注意sentinel之间只建立命令连接,不建立订阅连接,订阅连接存在的目的就是为了sentinel能相互已知,相互已知的多个sentinel只需要用命令来进行通信就够了。

    检测主观下线状态

    默认sentinel会以每秒一次的频率向所有与它创建了命令连接的实例(包括主服务器、从服务器、其他sentinel)发送ping命令,根据返回的命令回复判断实例是否在线。

    返回的ping命令回复有几种形式:

    1、有效回复:如+pong、-loading、-masterdown

    2、无效回复:除了上面三种回复外的其他回复

    sentinel配置文件中的down-after-milliseconds选项指定了sentinel判断实例进入主观下线所需的时间长度。如果一个实例在down-after-milliseconds毫秒内连续向sentinel返回无效回复,那么sentinel会修改这个实例对应的sentinelRedisInstance,在其中的flags标识值中打开SRI_S_DOWN选项,以此表示该实例已经进入主观下线状态。

    一个主服务器被判定为主观下线状态后,对应的实例如下:

    配置文件中的down-after-milliseconds不仅被sentinel用来判断主服务器的主观下线状态,还会被用于判断从服务器和sentinel,这是一个统一的下线标准。

    如果多个sentinel设置的主观下线时长不同,那么时间设置的较少的那个sentinel认为某个服务器下线时,其他sentinel还认为其仍处于在线状态,这种情况下只有超过较大的时间设置,才会让多个sentinel同时判定其下线。

    判定客观下线状态

    对于主服务器来说,当sentinel从其他sentinel那里接受到足够数量的已下线判断后,就会判定主服务器客观下线,并对主服务器执行故障转移操作。

    发出is-master-down-by-addr命令

    sentinel会发出以下命令询问其他sentinel是否同意主服务器下线:

    sentinel is-master-down-by-addr <ip> <port> <current_epoch> <runid>
    

    其中的四个参数分别是主服务器ip、端口、sentinel当前的配置纪元、sentinel的运行ID或者*(如果是ID的话这条命令就同时用于选举领头sentinel,如果是*代表这条命令仅代表检测主服务器的客观下线状态)

    接受is-master-down-by-addr命令

    当sentinel接受到is-master-down-by-addr命令后,会检查对应的主服务器是否已经下线,然后向源sentinel返回一个包含三个参数的Multi Bulk回复:

    <down_state>
    <leader_runid>
    <leader_epoch>
    

    分别代表对主服务器的检查结果(1代表主服务器已下线,0代表主服务器的未下线)、本sentinel的局部领头sentinel的ID(也可能是*,此时这条回复与选举无关)、本sentinel的局部领头sentinel的配置纪元。

    接受is-master-down-by-addr命令的回复

    sentinel在收到其他sentinel的命令回复后,会统计结果,当这一数量达到配置指定的判断主观下线需要的数量时,sentinel会将主服务器实例结构的flags属性的SRI_O_DOWN标识打开,表示主服务器已经进入客观下线状态:

    这个配置在初始化时被载入的sentinel配置文件中,不同的sentinel判断客观下线的条件可能不同,这可能导致对于同一主服务器,不同的sentinel判断结果不同。

    选举领头sentinel

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

    选举的过程如下:

    1、每个发现主服务器进入客观下线状态的sentinel都会要求其他sentinel将自己设置为局部领头sentinel,此时这个sentinel会向另一个sentinel发送sentinel is-master-down-by-addr命令,runid为*,代表要求目标sentinel将自己设置为局部领头。

    2、设置局部领头的规则为先到先得,最先向目标sentinel发送要求设置的源sentinel会成为目标sentinel的局部领头sentinel,之后收到的所有要求设置的命令都会被拒绝。

    目标sentinel收到sentinel is-master-down-by-addr命令后,会回复一个Multi Bulk回复:

    <down_state>
    <leader_runid>
    <leader_epoch>
    

    此时后两个参数就代表目标sentinel的局部领头运行ID、配置纪元。

    3、当sentinel接受到回复后会检查配置纪元和自己的配置纪元是否相同(配置纪元其实就是一个计数器,每次进行领头选举后这个值都会增1,在一个配置纪元中所有sentinel都有一次将某个sentinel设置为局部领头sentinel的机会,局部领头一旦设置,在这个配置纪元中就不能再更改),如果相同就继续检查其中的ID和自己的是否相同,如果相同代表自己就是目标sentinel的局部领头。

    4、如果某个sentinel被半数以上的sentinel设置成了局部领头,那么这个sentinel就会成为领头sentinel。如果在给定时限内没有一个sentinel被选举为领头,那么各个sentinel将在一段时间后重新选举,直到选出为止。

    故障转移

    选好了领头sentinel就会进行故障转移,具体步骤如下:

    1、在已下线服务器属下的所有从服务器中,挑选出一个从服务器,选择的标准如下:

    (1)删除从服务器列表中处于下线和断线的从服务器(保证从服务器可用)

    (2)删除列表中最近5秒没有回复过领头sentinel的info命令的从服务器(保证从服务器最近成功通信)

    (3)删除所有与已下线主服务器连接断开超过down-after-milliseconds*10毫秒的从服务器(保证没有过早的断开连接)

    (4)优先选择优先级高的从服务器

    (5)优先选择复制偏移量高的从服务器

    (6)优先选择运行ID小的从服务器

    2、对该从服务器发送以下命令:

    slaveof no one
    

    将这个从服务器转换成主服务器。领头sentinel发送该命令后会以每秒一次的频率向被升级的服务器发送info命令,观察命令回复中的role信息,当role从slave变为master时就代表升级成功了。

    3、领头sentinel向已下线主服务器属下所有从服务器去复制新的主服务器,这个操作可以通过发送slave命令来实现。

    4、在领头sentinel的已下线主服务器实例结构中更新它的状态,把它变成从服务器,待它重新上线后给它发送slave命令,让它复制新的主服务器。

    总结

    CAP原理

    CAP原理是设计和部署分布式应用时存在三个核心系统需求:consistent(一致性)、availability(可用性)、partition tolerance(分区容忍性)。一致性不满足就是多个分布式节点的数据不一致,一个节点的修改操作无法同步到另一个节点。当网络分区(网络断开,分布式节点之间失去联系)发生时,一致性很难满足。除非牺牲可用性,就是暂停节点的服务,当网络恢复时重启。所以,网络分区发生时(集群中出现不能相互通信的两部分,即不满足分区容忍性),一致性和可用性难以同时满足。

    大多数网站的架构都是AP,放弃对一致性的严格要求。

    增量同步和快照同步

    redis的主从数据是异步同步的,所以并不满足一致性的要求,在主从网络断开的条件下,主节点依然可以正常对外提供修改服务,满足可用性。

    主动同步(严格意义上讲分为主从同步和从从同步)主要分为增量同步和快照同步两种。

    增量同步会将那些修改性指令存在本地的内存缓冲区中,然后异步的将其同步到从节点,从节点一边执行同步指令一边向主节点反馈偏移量。因为内存缓冲区是有限的,所以主库不可能装入太多指令,实际上这个缓冲区是一个环形数组,如果满了就会覆盖开始的内容。如果网络长时间断开就有可能缓冲区的指令被覆盖掉,此时就需要使用快照同步。

    快照同步将主库中的内存数据全部装入磁盘中,然后将其传送到从节点,从节点拿到快照文件后清空内存然后执行全量加载。在执行快照同步时,主库的修改指令还在不断的装入buffer中,如果buffer太小或者同步时间过长都会导致buffer的数据再次被覆盖,于是又要进行快照同步,陷入死循环,所以要合理设置buffer的值以避免。

    当一个新节点加入集群时先来一次快照同步,再进行增量同步。

    redis2.8版本中快照同步改为了无盘复制,无需写入磁盘,直接通过套接字将内容发给从节点,从节点取出写入磁盘,然后一次性完成加载。

    wait指令是强行手动同步,可以设置从库个数和最多等待时间,允许无限等待。

    抵抗节点故障:sentinel

    sentinel的作用:监控从节点的健康(判断是否满足可用性)、进行主从切换、查询主节点的地址。

    它负责监控主从节点的健康,当主节点挂掉时,自动选择一个最优的从节点切换为主节点。

    客户端连接集群时首先连接sentinel,然后由其来查询主节点的地址,完成交互。客户端连接时首先查主库地址,如果发现主库地址和内存中的不一样就认为主库地址改变了,客户端会断开连接与主库建立新的连接。

    sentinel也可以完成主动进行主从切换,主从切换后,主库降级为从库,修改性指令在从库执行会抛出异常,然后客户端会切换连接。

    在主从互换时,可能会因为网络延迟导致同步消息丢失,sentinel可以设置必须至少有x个节点正常复制,否则就失去可用性,还可以设置正常复制的标准,就是每隔x秒收到从节点的反馈就代表合格。

  • 相关阅读:
    【Java 学习笔记】 问题列表
    【Weka】Weka聚类
    【MFC 学习笔记】MFC BUG
    【Java 学习笔记】 变量转换
    【MFC 学习笔记】CheckListBox
    Http Handler 介绍
    C# HttpWebRequest可断点上传,下载文件;SOCKET多线程多管道可断点传送大文件
    SQL Server 2005 常用数据类型详解
    浅谈OCR之Tesseract
    [理财产品] 招行新的日日金8158
  • 原文地址:https://www.cnblogs.com/yinyunmoyi/p/11523922.html
Copyright © 2011-2022 走看看