深入浅析Redis中的sentinel故障转移


本篇文章带大家了解一下redis中的故障转移(sentinel),希望对大家有所帮助!

深入浅析Redis中的sentinel故障转移

当两台以上的Redis实例形成了主备关系,它们组成的集群就具备了一定的高可用性:当master发生故障的时候,sl*e可以成为新的master对外提供读写服务,这种运营机制成为failover。【相关推荐:Redis视频教程】

那么谁来发现master的故障做failover决策?

一种方式是,保持一个daemo进程,监控着所有的master-sl*e节点,如下图所示:

1.png

一个Redis集群里面有一个master和两个sl*e,这个daemon进程监控着这三个节点。但daemon为单节点,本身可用性无法保证。需要引入多daemon,如下图所示:

2.png

多个daemon解决了可用性问题,但又出现了一致性问题,如何就某个master是否可用达成一致?例如上图两个daemon1和和master网络不通,daemon和master连接畅通,那此时mater节点是否需要failover那?

Redis的sentinel提供了一套多daemon间的交互机制,多个daemon间组成一个集群,成为sentinel集群,daemon节点也称为sentinel节点。如下图所示:

3.png

这些节点相互间通信、选举、协商,在master节点的故障发现failover决策上表现出一致性。

sentinel集群监视任意多个master以及master下的sl*e,自动将下线的master从其下的某个sl*e升级为新的master代替继续处理命令请求。

启动并初始化Sentinel

启动一个Sentinel可以使用命令:

./redis-sentinel ../sentinel.conf

或者命令:

./redis-server ../sentinel.conf --sentinel

当一个Sentinel启动时,它需要执行以下步骤:

初始化服务器

Sentinel本质上是运行在特殊模式下的Redis服务器,它和普通的Redis服务器执行的工作不同,初始化过程也不完全相同。如普通的Redis服务器初始化会载入RDB或者AOF文件来恢复数据,而Sentinel启动时不会载入,因为Sentinel并不使用数据库。

将普通Redis服务器使用的代码替换成Sentinel专用代码

将一部分普通Redis服务器使用的代码替换成Sentinel专用代码。如普通Redis服务器使用server.c/redisCommandTable作为服务器的命令表:

truct redisCommand redisCommandTable[] = {
    {"module",moduleCommand,-2,"as",0,NULL,0,0,0,0,0},
    {"get",getCommand,2,"rF",0,NULL,1,1,1,0,0},
    {"set",setCommand,-3,"wm",0,NULL,1,1,1,0,0},
    {"setnx",setnxCommand,3,"wmF",0,NULL,1,1,1,0,0},
    {"setex",setexCommand,4,"wm",0,NULL,1,1,1,0,0},
    {"psetex",psetexCommand,4,"wm",0,NULL,1,1,1,0,0},
    {"append",appendCommand,3,"wm",0,NULL,1,1,1,0,0},
    .....
    {"del",delCommand,-2,"w",0,NULL,1,-1,1,0,0},
    {"unlink",unlinkCommand,-2,"wF",0,NULL,1,-1,1,0,0},
    {"exists",existsCommand,-2,"rF",0,NULL,1,-1,1,0,0},
    {"setbit",setbitCommand,4,"wm",0,NULL,1,1,1,0,0},
    {"getbit",getbitCommand,3,"rF",0,NULL,1,1,1,0,0},
    {"bitfield",bitfieldCommand,-2,"wm",0,NULL,1,1,1,0,0},
    {"setrange",setrangeCommand,4,"wm",0,NULL,1,1,1,0,0},
    {"getrange",getrangeCommand,4,"r",0,NULL,1,1,1,0,0},
    {"substr",getrangeCommand,4,"r",0,NULL,1,1,1,0,0},
    {"incr",incrCommand,2,"wmF",0,NULL,1,1,1,0,0},
    {"decr",decrCommand,2,"wmF",0,NULL,1,1,1,0,0},
    {"mget",mgetCommand,-2,"rF",0,NULL,1,-1,1,0,0},
    {"rpush",rpushCommand,-3,"wmF",0,NULL,1,1,1,0,0},
    {"lpush",lpushCommand,-3,"wmF",0,NULL,1,1,1,0,0}
    ......
    }

Sentinel使用sentinel.c/sentinelcmds作为服务器列表,如下所示:

struct redisCommand sentinelcmds[] = {
    {"ping",pingCommand,1,"",0,NULL,0,0,0,0,0},
    {"sentinel",sentinelCommand,-2,"",0,NULL,0,0,0,0,0},
    {"subscribe",subscribeCommand,-2,"",0,NULL,0,0,0,0,0},
    {"unsubscribe",unsubscribeCommand,-1,"",0,NULL,0,0,0,0,0},
    {"psubscribe",psubscribeCommand,-2,"",0,NULL,0,0,0,0,0},
    {"punsubscribe",punsubscribeCommand,-1,"",0,NULL,0,0,0,0,0},
    {"publish",sentinelPublishCommand,3,"",0,NULL,0,0,0,0,0},
    {"info",sentinelInfoCommand,-1,"",0,NULL,0,0,0,0,0},
    {"role",sentinelRoleCommand,1,"l",0,NULL,0,0,0,0,0},
    {"client",clientCommand,-2,"rs",0,NULL,0,0,0,0,0},
    {"shutdown",shutdownCommand,-1,"",0,NULL,0,0,0,0,0},
    {"auth",authCommand,2,"sltF",0,NULL,0,0,0,0,0}
}

初始化Sentinel状态

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

struct sentinelState {
 
    char myid[CONFIG_RUN_ID_SIZE+1]; /* This sentinel ID. */
    
    //当前纪元,用于实现故障转移
    uint64_t current_epoch;         /* Current epoch. */
    
    //监视的主服务器
    //字典的键是主服务器的名字
    //字典的值则是一个指向sentinelRedisInstances结构的指针
    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. */
    
    char *announce_ip;  /* IP addr that is gossiped to other sentinels if
                           not NULL. */
    int announce_port;  /* Port that is gossiped to other sentinels if
                           non zero. */
    unsigned long simfailure_flags; /* Failures simulation. */
    int deny_scripts_reconfig; /* Allow SENTINEL SET ... to change script
                                  paths at runtime? */
}

根据给定的配置文件,初始化Sentinel的监视主服务器列表

对Sentinel状态的初始化将引发对masters字典的初始化,而master字典的初始化是根据被载入的Sentinel配置文件来进行的。

字典的key是监视主服务器的名字,字典的值则是被监控主服务器对应的sentinel.c/sentinelRedisInstance结构。

sentinelRedisInstance结构部分属性如下:

typedef struct sentinelRedisInstance {
    //标识值,记录了实例的类型,以及该实例的当前状态
    int flags;      /* See SRI_... defines */
    
    //实例的名字
    //主服务器的名字由用户在配置文件中设置
    //从服务器以及Sentinel的名字由Sentinel自动设置
    //格式为ip:port,例如“127.0.0.1:26379”
    char *name;     /* Master name from the point of view of this sentinel. */
    
    //实例运行的ID
    char *runid;    /* Run ID of this instance, or unique ID if is a Sentinel.*/
    
    //配置纪元,用于实现故障转移
    uint64_t config_epoch;  /* Configuration epoch. */
    
    //实例的地址
    sentinelAddr *addr; /* Master host. */
    
    //sentinel down-after-milliseconds选项设定的值
    //实例无响应多少毫秒之后才会被判断为主观下线(subjectively down)
    mstime_t down_after_period; /* Consider it down after that period. */
    
    //sentinel monitor <master-name> <ip> <redis-port> <quorum>选项中的quorum
    //判断这个实例为客观下线(objective down)所需的支持投票的数量
    unsigned int quorum;/* Number of sentinels that need to agree on failure. */  
    //sentinel parallel-syncs <master-name> <numreplicas> 选项的numreplicas值
    //在执行故障转移操作时,可以同时对新的主服务器进行同步的从服务器数量
    int parallel_syncs; /* How many sl*es to reconfigure at same time. */
    
    //sentinel failover-timeout <master-name> <milliseconds>选项的值
    //刷新故障迁移状态的最大时限
    mstime_t failover_timeout;      /* Max time to refresh failover state. */
}

例如启动Sentinel时,配置了如下的配置文件:

# sentinel monitor <master-name> <ip> <redis-port> <quorum>
sentinel monitor master1 127.0.0.1 6379 2

# sentinel down-after-milliseconds <master-name> <milliseconds>
sentinel down-after-milliseconds master1 30000

# sentinel parallel-syncs <master-name> <numreplicas>
sentinel parallel-syncs master1 1

# sentinel failover-timeout <master-name> <milliseconds>
sentinel failover-timeout master1 900000

则Sentinel则会为主服务器master1创建如下图所示的实例结构:

4.png

Sentinel状态以及masters字典的机构如下:

5.png

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

创建连向被监视主服务器的网络连接,Sentinel将成为主服务器的客户端,向主服务器发送命令并从命令回复获取信息。

Sentinel会创建两个连向主服务器的异步网络连接:

  • 命令连接,用于向主服务器发送命令并接收命令回复
  • 订阅连接,订阅主服务器的_sentinel_:hello频道

6.png

Sentinel发送信息和获取信息

  • Sentinel默认会以每十秒一次的频率,通过命令连接向被监视的master和sl*e发送INFO命令

    通过master的回复可获取master本身信息,包括run_id域记录的服务器运行ID,以及role域记录的服务器角色。另外还会获取到master下的所有的从服务器信息,包括sl*e的ip地址和port端口号。Sentinel无需用户提供从服务器的地址信息,由master返回的sl*e的ip地址和port端口号,可以自动发现sl*e。

    当Sentinel发现master有新的sl*e出现时,Sentinel会为这个新的sl*e创建相应的实例外,Sentinel还会创建到sl*e的命令连接和订阅连接。

    根据sl*e的INFO命令的回复,Sentinel会提取如下信息:

    1.sl*e的运行ID run_id

    2.sl*e的角色role

    3.master的ip地址和port端口

    4.master和sl*e的连接状态master_link_status

    5.sl*e的优先级sl*e_priority

    6.sl*e的复制偏移量sl*e_repl_offset

  • Sentinel在默认情况下会以每两秒一次的频率,通过命令连接向所有被监视的master和sl*e的_sentinel_:hello频道发送一条信息

    发送以下格式的命令:

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

以上命令相关参数意义:

参数 意义
s_ip Sentinel的ip地址
s_port Sentinel的端口号
s_runid Sentinel的运行ID
s_runid Sentinel的运行ID
m_name 主服务器的名字
m_ip 主服务器的IP地址
m_port 主服务器的端口号
m_epoch 主服务器当前的配置纪元
  • Sentinel与master或者sl*e建立订阅连接之后,Sentinel就会通过订阅连接发送对_sentinel_:hello频道的订阅,订阅会持续到Sentinel与服务器的连接断开为止

命令如下所示:

SUBSCRIBE sentinel:hello

SONIFY.io SONIFY.io

设计和开发音频优先的产品和数据驱动的解决方案

SONIFY.io 83 查看详情 SONIFY.io

7.png

如上图所示,对于每个与Sentinel连接的服务器 ,Sentinel既可以通过命令连接向服务器频道_sentinel_:hello频道发送信息,又通过订阅连接从服务器的_sentinel_:hello频道接收信息。

  • sentinel间会相互感知,新加入的sentinel会向master的_sentinel_:hello频道发布一条消息,包括自己的消息,其它该频道订阅者sentinel会发现新的sentinel。随后新的sentinel和其它sentinel会创建长连接。

相互连接的各个Sentinel可以进行信息交换。Sentinel为master创建的实例结构中的sentinels字典保存了除Sentinel本身之外,所有同样监视这个主服务器的其它Sentinel信息。

前面也讲到sentinel会为sl*e创建实例(在master实例的sl*es字典中)。现在我们也知道通过sentinel相互信息交换,也创建了其它sentinel的实例(在master实例的sentinels字典中)。我们将一个sentinel中保存的实例结构大概情况理一下,如下图所示:

8.png

从上图可以看到sl*e和sentinel字典的键由其ip地址和port端口组成,格式为ip:port,其字典的值为其对应的sentinelRedisInstance实例。

master的故障发现

主观不可用

默认情况下Sentinel会以每秒一次的频率向所有与它创建了命令连接的master(包括master、sl*e、其它Sentinel)发送PING命令,并通过实例返回的PING命令回复来判断实例是否在线。

PING命令回复分为下面两种情况:

  • 有效回复:实例返回 +PONG、-LOADING、-MASTERDOWN三种回复的一种

  • 无效回复:除上面有效回复外的其它回复或者在指定时限内没有任何返回

Sentinel配置文件中的设置down-after-milliseconds毫秒时效内(各个sentinel可能配置的不相同),连续向Sentinel返回无效回复,那么sentinel将此实例置为主观下线状态,在sentinel中维护的该实例flags属性中打开SRI_S_DOWN标识,例如master如下所示:

9.png

客观不可用

在sentinel发现主观不可用状态后,它会将“主观不可用状态”发给其它sentinel进行确认,当确认的sentinel节点数>=quorum,则判定该master为客观不可用,随后进入failover流程。

上面说到将主观不可用状态发给其它sentinel使用如下命令:

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

各个参数的意义如下:

  • ip:被sentinel判断为主观下线的主服务器的ip地址
  • port: 被sentinel判断为主观下线的主服务器的port地址
  • current_epoch:sentinel的配置纪元,用于选举领头Sentinel
  • runid:可以为*号或者Sentinel的运行ID,*号代表检测主服务器客观下线状态。Sentinel的运行ID用于选举领头Sentinel

接受到以上命令的sentinel会反回一条包含三个参数的Multi Bulk回复

1)down_state> 目标sentinel对该master检查结果,1:master已下线 2:master未下线

2)leader_runid> 两种情况,*表示仅用于检测master下线状态 ,否则表示局部领头Sentinel的运行ID(选举领头Sentinel)

3)leader_epoch> 当leader_runid为时,leader_epoch始终为0。不为时则表示目标Sentinel的局部领头Sentinel的配置纪元(用于选举领头Sentinel)

其中节点数量限制quorum为sentinel配置文件中配置的

sentinel monitor <master-name> <ip> <redis-port> <quorum>

quorum选项,不同的sentinel配置的可能不相同。

当sentinel认为master为客观下线状态,则会将master属性中的flags的SRI_O_DOWN标识打开,例如master如下图所示:

10.png

选举Sentinel Leader

当一台master宕机时,可能多个sentinel节点同时发现并通过交互确认相互的“主观不可用状态”,同时达到“客观不可用状态”,同时打算发起failover。但最终只能有一个sentinel节点作为failover发起者,那么就需要选举出Sentinel Leader,需要开始一个Sentinel Leader选举过程。

Redis的Sentinel机制采用类似于Raft协议实现这个选举算法:

1.sentinelState的epoch变量类似于raft协议中的term(选举回合)。

2.每一个确认了master“客观不可用”的sentinel节点都会向周围广播自己的参选请求(SENTINEL is-master-down-by-addr ,current_epoch为自己的配置纪元,run_id为自己的运行ID)

3.每一个接收到参选请求的sentinel节点如果还没接收到其它参选请求,它就将本回合的意向置为首个参选sentinel并回复它(先到先得);如果已经在本回合表过意向了,则拒绝其它参选,并将已有意向回复(如上所介绍的三个参数的Multi Bulk回复,down_state为1,leader_runid为首次接收到的发起参选请求的源sentinel的运行ID,leader_epoch为首次接收到的发起参选请求的源sentinel的配置纪元)

4.每个发起参选请求的sentinel节点如果收到超过一半的意向同意某个参选sentinel(可能是自己),则确定该sentinel为leader。如果本回合持续了足够长时间未选出leader,则开启下一个回合

leader sentinel 确定之后,leader sentinel从master所有的sl*e中依据一定规则选取一个作为新的master

故障转移failover

在选举出Sentinel Leader之后,sentinel leader对已下线master执行故障转移:

  • sentinel leader对已下线的master的所有sl*e中,选出一个状态良好、数据完整的sl*e,然后向这个sl*e发送:SL*EOF no one 命令,将这个sl*e转换为master。

    我们来看下新的master是怎么挑选出来的?Sentinel leader会将已下线的所有sl*e保存到一个列表,然后按照以下规则过滤筛选:

  • 优先级最高的sl*e,redis.conf配置中replica-priority选项来标识,默认为100,replica-priority较低的优先级越高。0为特殊优先级,标志为不能升级为master。

  • 如果存在多个优先级相等的sl*e,则会选择复制偏移量(offset)最大的sl*e(数据更加完整)

  • 如果存在多个优先级相等,最大复制偏移量最大的sl*e,则选择运行ID最小的sl*e

选出需要升级为新的master的sl*e后,Sentinel Leader会向这个sl*e发送SL*EOF no one 命令。之后Sentinel会以每秒一次频率(平时是十秒一次)向被升级sl*e发送INFO,当回复的role由sl*e变为master时Sentinel Leader就会知道已升级为master。

  • sentinel leader 向已下线的master属下的sl*e发送SL*EOF命令(SL*EOF ),去复制新的master

  • 将旧的master设置为新的master的sl*e,并继续对其监视,当其重新上线时Sentinel会执行命令让其成为新的master的sl*e。

总结

Sentinel是Redis高可用的解决方案,Sentinel集群的节点数需要>=3.

默认每十秒Sentinel对master和sl*e执行info,用于发现master变更信息,主从关系以及发现新的sl*e节点。

默认每两秒sentinel通过命令连接向所有被监视的master和sl*e的_sentinel_:hello频道发送一条信息,来和其它sentinel交互信息

默认每一秒sentinel向master,sl*e,其它sentinel发送PING命令来判断对方是否下线

Sentinel Leader是按照一定规则选举出来的。

由Sentinel Leader进行故障转移操作,选举出新的master来替代已下线的master。

更多编程相关知识,请访问:编程入门!!

以上就是深入浅析Redis中的sentinel故障转移的详细内容,更多请关注其它相关文章!


# 会以  # 唐山网站推广文案  # 宁夏seo软件排行榜  # 外贸网站建设外包  # 美容seo软文撰写  # 云南seo快排快速入门  # 同城网站推广哪家便宜  # 地产推广网站设计  # 沈阳关键词排名怎么收费  # 南山全网推广营销获客工具  # seo稿件撰写规范  # 则会  # Redis  # 端口号  # 升级为  # 如下图  # 配置文件  # 自己的  # 多个  # 不可用  # 所示  # 故障转移  # sentinel 


相关栏目: 【 Google疑问12 】 【 Facebook疑问10 】 【 优化推广96088 】 【 技术知识133117 】 【 IDC资讯59369 】 【 网络运营7196 】 【 IT资讯61894


相关推荐: 铁路12306怎么申请退票_铁路12306退票申请操作流程  Sublime怎么快速复制文件路径_Sublime右键菜单增强技巧  C++如何实现矩阵乘法_C++二维数组矩阵运算代码示例  Win10如何查看已安装的更新补丁 Win10卸载指定更新教程【教程】  Lar*el 中高效执行多列更新:单次查询实现  Go Template中优雅处理循环最后一项:自定义函数实践  PHP实现等比数列:构建数组元素基于前一个值递增的方法  OTT月报 | 2025年9月智能电视大数据报告  智学网app怎么登录忘记密码_智学网app忘记密码找回与重新登录操作方法  狙击外星人小游戏在线链接_狙击外星人小游戏网页链接  CSS如何在页面中引入重置样式_使用Normalize.css或Reset.css统一浏览器默认样式  AffinityDesigner图层蒙版怎么用_AffinityDesigner图层蒙版设计应用  mysql如何配置从库只读_mysql从库只读设置方法  极兔快递官网查询入口手机版 手机极兔快递登录查询入口官方  《金山词霸》语音翻译方法  汽水音乐网页版登录 汽水音乐网页端官方入口  全球各国上班时间表外贸邮件时间  如何用mysql开发用户注册登录功能_mysql用户注册登录数据库设计  构建可配置的J*aScript加权点击计数器与共享总计功能  夸克浏览器资源嗅探怎么用 夸克浏览器网页资源下载技巧【教程】  微信客户端如何找回密码_微信客户端忘记密码找回方法  使用AI在VS Code中将代码从一种语言翻译成另一种  Python项目中的条件导入:解决跨模块依赖问题  使用Python和NLTK从文本中高效提取名词的实用教程  Mac hosts文件在哪里_Mac修改hosts文件详细教程  word文档行距怎么调?word文档调行距的操作步骤  在J*a中如何实现在线问答与评分系统_问答评分项目开发方法说明  如何在vscode中关闭it环境  192.168.1.1路由器后台入口 192.168.1.1默认登录入口  使用逻辑应用(Logic Apps)自动处理邮件附件中的XML到Excel  《土豆雅思》修改密码方法  发博客与长微博技巧  漫蛙漫画直连入口 _ manwa官方备用入口实时检测  解决CSS布局中意外顶部空白问题的教程  西瓜视频怎么查看访客记录_西瓜视频访客记录查看方法  批改网网页版登录 批改网电脑版学生登录入口  php如何实现多域名共享session_php存储session到redis与跨域读取配置  优酷下载视频的清晰度怎么选_优酷缓存清晰度设置与选择指南  Dash应用多值文本输入处理与类型转换教程  b站网页版入口 哔哩哔哩官方网站直接进入  mysql如何限制远程访问_mysql远程访问限制方法  firefox火狐浏览器最新官网主页_ firefox火狐浏览器平台入口直达官方链接  《美篇》取消会员自动续费方法  口腔诊所管理软件推荐  J*a列表元素格式化输出教程  J*aScript与HTML元素交互:图片点击事件与链接处理教程  在Django中动态检查模型关联:一种灵活的解决方案  漫蛙app官方版手机正版入口-漫蛙漫画manwa在线漫画正版入口  冬季去寒冷地区旅游,以下哪种做法有助于缓解冻伤  海棠阅读网页版_进入海棠网页版在线阅读中心 

 2021-10-28

了解您产品搜索量及市场趋势,制定营销计划

同行竞争及网站分析保障您的广告效果

点击免费数据支持

提交您的需求,1小时内享受我们的专业解答。

运城市盐湖区信雨科技有限公司


运城市盐湖区信雨科技有限公司

运城市盐湖区信雨科技有限公司是一家深耕海外推广领域十年的专业服务商,作为谷歌推广与Facebook广告全球合作伙伴,聚焦外贸企业出海痛点,以数字化营销为核心,提供一站式海外营销解决方案。公司凭借十年行业沉淀与平台官方资源加持,打破传统外贸获客壁垒,助力企业高效开拓全球市场,成为中小企业出海的可靠合作伙伴。

 8156699

 13765294890

 8156699@qq.com

Notice

We and selected third parties use cookies or similar technologies for technical purposes and, with your consent, for other purposes as specified in the cookie policy.
You can consent to the use of such technologies by closing this notice, by interacting with any link or button outside of this notice or by continuing to browse otherwise.