【Redis源码】集群之哨兵sentinel初识(十一)

本文涉及的产品
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
简介: 【Redis源码】集群之哨兵sentinel初识(十一)

前言:

说到哨兵之前,可以思考一个问题。为什么有哨兵机制?这一点其实可以从主从复制的优缺点作为考虑。主从复制其实有一个致命的弱点就是它是非高可用的,比如说当主节点出现故障时,那么此时我的写入就会出现问题,切换需要重新配,就无法做到高可用。此时其实就可以用到哨兵进制,哨兵自动发现故障,并通过选举算法重新选举新主完成转移和通知实现高可用。

参考资料:

https://redis.io/topics/sentinel

一篇看着太长分为两篇:

《集群之哨兵sentinel初识》和《集群之哨兵sentinel故障转移》。

(一)理解哨兵模式

基础结构

1.哨兵本身是监听者身份,没有存储功能。在整体体系当中,一个sentinel或者一个群sentinels与redis主从体系是有监听和被监听者关系。

2.哨兵有如下一些功能:监听、通知、自动故障转移、配置提供程序。

1)监听:哨兵会不断检查主服务和从服务是否正常运行。

2)通知:哨兵可以通过API通知到系统管理员或者其他程序,其中一个受监听redis实例出问题。

3)自动转移故障:如果主服务不正常,则哨兵可以启动故障转移,在该从服务升级为主服务。将其该主服务下其他从服务重新配置主服务,并通知使用Redis服务器的应用要使用新地址。

4) 配置提供程序: Sentinel充当客户端服务发现的授权来源, 客户端连接到Sentinels,以询问负责给定服务的当前Redis主服务器的地址。如果发生故障转移,Sentinels将报告新地址。

3.哨兵存在三种交互模式:哨兵与主服务、哨兵与从服务、哨兵与其他哨兵。

4.哨兵体现中有三个角色:master、slave、sentinel。

(二)配置哨兵模

1.1 sentinel.conf配置说明

# 哨兵sentinel监控的redis主节点的 ip port
# sentinel monitor  
sentinel monitor mymaster 127.0.0.163792

# 当在Redis实例中开启了requirepass foobared 授权密码 这样所有连接Redis实例的客户端都要提供密码  
# 设置哨兵sentinel 连接主从的密码 注意必须为主从设置一样的验证密码  
# sentinel auth-pass  
sentinel auth-pass mymaster MySUPER--secret-0123passw0rd  

# 指定多少毫秒之后 主节点没有应答哨兵sentinel 此时 哨兵主观上认为主节点下线 默认30秒  
# sentinel down-after-milliseconds  
sentinel down-after-milliseconds mymaster 30000


# 这个配置项指定了在发生failover主备切换时最多可以有多少个slave同时对新的master进行 同步,  
# 这个数字越小,完成failover所需的时间就越长,  
# 但是如果这个数字越大,就意味着越 多的slave因为replication而不可用。
# 可以通过将这个值设为 1 来保证每次只有一个slave 处于不能处理命令请求的状态。
# sentinel parallel-syncs  
sentinel parallel-syncs mymaster 1


# 故障转移的超时时间 failover-timeout 可以用在以下这些方面:
# 1. 同一个sentinel对同一个master两次failover之间的间隔时间。
# 2. 当一个slave从一个错误的master那里同步数据开始计算时间。直到slave被纠正为向正确的master那里同步数据时。
# 3.当想要取消一个正在进行的failover所需要的时间。
# 4.当进行failover时,配置所有slaves指向新的master所需的最大时间。不过,即使过了这个超时,slaves依然会被正确配置为指向master,
# 但是就不按parallel-syncs所配置的规则来了  
# 默认三分钟  
# sentinel failover-timeout  
sentinel failover-timeout mymaster 180000  


# 哨兵sentinel实例运行的端口 默认26379  
port 26379  


# SCRIPTS EXECUTION  
 
# 配置当某一事件发生时所需要执行的脚本,可以通过脚本来通知管理员,例如当系统运行不正常时发邮件通知相关人员。
# 对于脚本的运行结果有以下规则:
# 若脚本执行后返回1,那么该脚本稍后将会被再次执行,重复次数目前默认为10  
# 若脚本执行后返回2,或者比2更高的一个返回值,脚本将不会重复执行。
# 如果脚本在执行过程中由于收到系统中断信号被终止了,则同返回值为1时的行为相同。
# 一个脚本的最大执行时间为60s,如果超过这个时间,脚本将会被一个SIGKILL信号终止,之后重新执行。
 
# 通知型脚本:当sentinel有任何警告级别的事件发生时(比如说redis实例的主观失效和客观失效等等),将会去调用这个脚本,  
# 这时这个脚本应该通过邮件,SMS等方式去通知系统管理员关于系统不正常运行的信息。调用该脚本时,将传给脚本两个参数,  
# 一个是事件的类型,一个是事件的描述。如果sentinel.conf配置文件中配置了这个脚本路径,那么必须保证这个脚本存在于这个
# 路径,并且是可执行的,否则sentinel无法正常启动成功。
# 通知脚本  
# sentinel notification-script  
sentinel notification-script mymaster /var/redis/notify.sh  
 
# 客户端重新配置主节点参数脚本  
# 当一个master由于failover而发生改变时,这个脚本将会被调用,通知相关的客户端关于master地址已经发生改变的信息。
# 以下参数将会在调用脚本时传给脚本:  
#        
# 目前总是“failover”,  
# 是“leader”或者“observer”中的一个。
# 参数 from-ip, from-port, to-ip, to-port是用来和旧的master和新的master(即旧的slave)通信的  
# 这个脚本应该是通用的,能被多次调用,不是针对性的。
# sentinel client-reconfig-script  
sentinel client-reconfig-script mymaster /var/redis/reconfig.sh

master-name 节点名称,名称只能是[A-z 0-9]以及.-_内的字符。

quorum 当这些quorum个数sentinel哨兵认为master主节点失联,那么这时客观上认为主节点失联了 。

1.2 启动哨兵模式

如果使用的是redis-sentinel可执行文件(或者您具有指向该redis-server可执行文件的名称的符号链接),则可以使用以下命令行运行Sentinel:

redis-sentinel /path/to/sentinel.conf

否则,您可以直接使用redis-server在Sentinel模式下启动的可执行文件:

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

server.c源码中可以体现两种加载模式:

intcheckForSentinelMode(int argc, char **argv) {
   int j;

   if (strstr(argv[0],"redis-sentinel") != NULL) return1;
   for (j = 1; j < argc; j++)
       if (!strcmp(argv[j],"--sentinel")) return1;
   return0;
}

(三)哨兵源码解析

2.1 初始化哨兵

1) server.c 哨兵初始化

intmain(int argc, char **argv) {
   //...省略
   server.sentinel_mode = checkForSentinelMode(argc,argv);  //判断是否为哨兵模式
   
   //...省略
   
   if (server.sentinel_mode) {
       initSentinelConfig(); //加载端口
       initSentinel();       //初始化哨兵模式,包括命令调用和各种数据结构。
   }
   //...省略
   if (!server.sentinel_mode) { //普通模式
      //...省略
   } else {
       /*
       检测哨兵模式是否正常配置
       */

       sentinelIsRunning();  
   }
}

initSentinelConfig函数中为加载哨兵端口,默认端口为26379。initSentinel函数中初始化哨兵模式,包括命令调用和各种数据结构。

initSentinel函数如下:

structredisCommandsentinelcmds[] = {
   {"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}
};

voidinitSentinel(void) {
   unsignedint j;

   /* 初始化哨兵命令 */
   dictEmpty(server.commands,NULL);
   for (j = 0; j < sizeof(sentinelcmds)/sizeof(sentinelcmds[0]); j++) {
       int retval;
       structredisCommand *cmd = sentinelcmds+j;

       retval = dictAdd(server.commands, sdsnew(cmd->name), cmd);
       serverAssert(retval == DICT_OK);
   }

   /* 初始化各种数据结构. */
   sentinel.current_epoch = 0;
   sentinel.masters = dictCreate(&instancesDictType,NULL);
   sentinel.tilt = 0;
   sentinel.tilt_start_time = 0;
   sentinel.previous_time = mstime();
   sentinel.running_scripts = 0;
   sentinel.scripts_queue = listCreate();
   sentinel.announce_ip = NULL;
   sentinel.announce_port = 0;
   sentinel.simfailure_flags = SENTINEL_SIMFAILURE_NONE;
   memset(sentinel.myid,0,sizeof(sentinel.myid));
}

redisCommand结构体数组为哨兵模式支持命令集。哨兵有单独的命令集,只支持ping、sentinel、subscribe、unsubscribe、psubscribe、publish、info、role、client、shutdown命令。

2) server.c 哨兵任务

int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
     //...省略
     run_with_period(100) {
        if (server.sentinel_mode) sentinelTimer();  //哨兵相关模式下运行任务
    }
    //...省略
}

serverCron为initServer函数中的注册时间回调。serverCron中调用sentinelTimer方法中执行哨兵模式中的任务。

包括执行定期操作比如PING、分析主服务和从服务的INFO命令、故障转移等等。

2.2 定期操作

周期执行命令函数sentinelSendPeriodicCommands

voidsentinelSendPeriodicCommands(sentinelRedisInstance *ri) {
   mstime_t now = mstime();
   mstime_t info_period, ping_period;
   int retval;
   //。。。省略

   /* 如果这是一个处于O_DOWN状态的主服务器的从服务器,我们开始发送
       它每秒钟都有信息,而不是通常的哨兵信息期周期 */

   if ((ri->flags & SRI_SLAVE) &&
       ((ri->master->flags & (SRI_O_DOWN|SRI_FAILOVER_IN_PROGRESS)) ||
        (ri->master_link_down_time != 0)))
   {
       info_period = 1000;   //1s
   } else {
       info_period = SENTINEL_INFO_PERIOD; //10s
   }

   /* ping通过 down-after-milliseconds 参数可以配置,默认1秒*/
   ping_period = ri->down_after_period;
   if (ping_period > SENTINEL_PING_PERIOD) ping_period = SENTINEL_PING_PERIOD;

   /* Send INFO to masters and slaves, not sentinels. */
   if ((ri->flags & SRI_SENTINEL) == 0 &&
       (ri->info_refresh == 0 ||
       (now - ri->info_refresh) > info_period))
   {
       retval = redisAsyncCommand(ri->link->cc,
           sentinelInfoReplyCallback, ri, "%s",
           sentinelInstanceMapCommand(ri,"INFO"));
       if (retval == C_OK) ri->link->pending_commands++;
   }

   /* 发送ping */
   if ((now - ri->link->last_pong_time) > ping_period &&
              (now - ri->link->last_ping_time) > ping_period/2) {
       sentinelSendPing(ri);
   }

   /* PUBLISH 发送hello,SENTINEL_PUBLISH_PERIOD为2000则为2s */
   if ((now - ri->last_pub_time) > SENTINEL_PUBLISH_PERIOD) {
       sentinelSendHello(ri);
   }
}

正常情况下每10秒会获取一次info主和从到。每1秒发送ping命令,ping命令down-after-milliseconds可以配置,每2秒广播一次hello msg。

总结:

(1)哨兵没有存储功能。

(2)哨兵服务支持命令:ping、sentinel、subscribe、unsubscribe、psubscribe、publish、info、role、client、shutdown

(3)哨兵提供功能:监听、通知、自动故障转移、配置提供程序。

(4)哨兵有两种启动模式:配置或者命令行参数。

(5)哨兵体现中有三个角色:master、slave、sentinel。

(6)周期函数主从非O_DOWN状态下每10s发送一次info命令到主从。

(7)ping命令默认是1s发送一次,可以通过down-after-milliseconds配置。

(8)每2秒广播一次hello msg。

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
13天前
|
NoSQL Linux Redis
06- 你们使用Redis是单点还是集群 ? 哪种集群 ?
**Redis配置:** 使用哨兵集群,结构为1主2从,加上3个哨兵节点,总计分布在3台Linux服务器上,提供高可用性。
40 0
|
22天前
|
负载均衡 监控 NoSQL
Redis的集群方案有哪些?
Redis集群包括主从复制(基础,手动故障恢复)、哨兵模式(自动高可用)和Redis Cluster(官方分布式解决方案,自动分片和容错)。此外,还有如Codis、Redisson和Twemproxy等第三方工具用于代理和负载均衡。选择方案需考虑应用场景、数据规模和并发需求。
34 2
|
27天前
|
NoSQL Redis
Redis集群(六):集群常用命令及说明
Redis集群(六):集群常用命令及说明
29 0
|
25天前
|
存储 NoSQL 算法
【Redis技术进阶之路】「底层源码解析」揭秘高效存储模型与数据结构底层实现(字典)(二)
【Redis技术进阶之路】「底层源码解析」揭秘高效存储模型与数据结构底层实现(字典)
36 0
|
6天前
|
人工智能 前端开发 Java
Java语言开发的AI智慧导诊系统源码springboot+redis 3D互联网智导诊系统源码
智慧导诊解决盲目就诊问题,减轻分诊工作压力。降低挂错号比例,优化就诊流程,有效提高线上线下医疗机构接诊效率。可通过人体画像选择症状部位,了解对应病症信息和推荐就医科室。
46 10
|
21天前
|
NoSQL Java 测试技术
面试官:如何搭建Redis集群?
**Redis Cluster** 是从 Redis 3.0 开始引入的集群解决方案,它分散数据以减少对单个主节点的依赖,提升读写性能。16384 个槽位分配给节点,客户端通过槽位信息直接路由请求。集群是无代理、去中心化的,多数命令直接由节点处理,保持高性能。通过 `create-cluster` 工具快速搭建集群,但适用于测试环境。在生产环境,需手动配置文件,启动节点,然后使用 `redis-cli --cluster create` 分配槽位和从节点。集群动态添加删除节点、数据重新分片及故障转移涉及复杂操作,包括主从切换和槽位迁移。
31 0
面试官:如何搭建Redis集群?
|
25天前
|
存储 缓存 NoSQL
【Redis深度专题】「核心技术提升」探究Redis服务启动的过程机制的技术原理和流程分析的指南(集群功能分析)(一)
【Redis深度专题】「核心技术提升」探究Redis服务启动的过程机制的技术原理和流程分析的指南(集群功能分析)
66 0
|
1月前
|
NoSQL Redis Docker
使用Docker搭建一个“一主两从”的 Redis 集群(超详细步骤)
使用Docker搭建一个“一主两从”的 Redis 集群(超详细步骤)
61 0
|
1月前
|
存储 NoSQL 网络协议
读懂Redis源码,我总结了这7点心得
读懂Redis源码,我总结了这7点心得
|
1月前
|
存储 监控 NoSQL
Redis 架构深入:主从复制、哨兵到集群
大家好,我是小康,今天我们来聊下 Redis 的几种架构模式,包括主从复制、哨兵和集群模式。
Redis 架构深入:主从复制、哨兵到集群