redis sentinel哨兵机制的源码分析
来源:互联网 发布:开源数据展示平台 编辑:程序博客网 时间:2024/06/04 18:33
sentinel是redis的高可用性解决方案:由一个或多个sentinel实例组成sentinel系统监视多个master以及master的slave,
并在被监视的master进入下线状态时,自动将下线master的某个slave升级为master,然后新的master代替下线的master处
理命令请求。
一、下线状态监测
(1)、主观下线监测
在默认情况下,sentinel会以每秒一次的频率向所有与它创建了命令连接的实例(master、slave、sentinel)发送ping,
如果实例在down_after_period时间内,没有返回正确回复,标记为SRI_S_DOWN,反之,标记为正常状态。
由定时任务serverCron定期监听所有连接实例
int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) { …… run_with_period(100) { //如果是哨兵服务器,触发sentinelTimer if (server.sentinel_mode) sentinelTimer(); } ……}void sentinelTimer(void) { …… sentinelHandleDictOfRedisInstances(sentinel.masters); ……}//检查所有连接实例void sentinelHandleDictOfRedisInstances(dict *instances) { ……//instances为sentinelState->masters while((de = dictNext(di)) != NULL) { sentinelRedisInstance *ri = dictGetVal(de); sentinelHandleRedisInstance(ri); if (ri->flags & SRI_MASTER) { sentinelHandleDictOfRedisInstances(ri->slaves); sentinelHandleDictOfRedisInstances(ri->sentinels); …… } } } ……}void sentinelHandleRedisInstance(sentinelRedisInstance *ri) { ……//发送ping给这个实例 sentinelSendPeriodicCommands(ri); //检查是否主观下线 sentinelCheckSubjectivelyDown(ri); ……}//主观下线检查void sentinelCheckSubjectivelyDown(sentinelRedisInstance *ri) { mstime_t elapsed = 0; //收到pong后,act_ping_time会被置为0 if (ri->link->act_ping_time) elapsed = mstime() - ri->link->act_ping_time; if (elapsed > ri->down_after_period || (ri->flags & SRI_MASTER &&ri->role_reported == SRI_SLAVE && mstime() - ri->role_reported_time > (ri->down_after_period+SENTINEL_INFO_PERIOD*2))) {//实例之前不是下线状态,更新为下线状态同时更新下线时间 if ((ri->flags & SRI_S_DOWN) == 0) { ri->s_down_since_time = mstime(); ri->flags |= SRI_S_DOWN; } } else {//收到pong,把下线状态删除 if (ri->flags & SRI_S_DOWN) { ri->flags &= ~(SRI_S_DOWN|SRI_SCRIPT_KILL_SENT); } }}
(2)、客观下线监测
当sentinel将master判断为主观下线之后,为了确认master是否真的下线,sentinel向监视master的sentinels询问
下线状态。在sentinel系统中有足够的的sentinel认为master下线了,sentinel将master标记为SRI_O_DOWN。(客观
下线监测只针对maste,不针对slave和sentinelr)
void sentinelHandleRedisInstance(sentinelRedisInstance *ri) { if (ri->flags & SRI_MASTER) { sentinelCheckObjectivelyDown(ri);//客观下线检查 ……//向其他sentinel询问master的下线状态 sentinelAskMasterStateToOtherSentinels(ri,SENTINEL_NO_FLAGS); }}//向其他sentinel询问master的下线状态void sentinelAskMasterStateToOtherSentinels(sentinelRedisInstance *master, int flags) { …… di = dictGetIterator(master->sentinels); while((de = dictNext(di)) != NULL) { sentinelRedisInstance *ri = dictGetVal(de); ……//主观下线才询问其他sentinel if ((master->flags & SRI_S_DOWN) == 0) continue; …… /*failover_state为SENTINEL_FAILOVER_STATE_NONE, 还未进入failover,询问master的下线状态*/ retval = redisAsyncCommand(ri->link->cc, sentinelReceiveIsMasterDownReply, ri, "SENTINEL is-master-down-by-addr %s %s %llu %s", master->addr->ip, port,sentinel.current_epoch, (master->failover_state > SENTINEL_FAILOVER_STATE_NONE) ?sentinel.myid : "*"); if (retval == C_OK) ri->link->pending_commands++; }}//接收询问下线状态的回复void sentinelReceiveIsMasterDownReply(redisAsyncContext *c, void *reply, void *privdata) { …… //reply回复三个参数down_state,leader_runid,leader_epoch。询问状态时,只需要down_state r = reply;//down_state==1表示sentinel认为master下线 if (r->element[0]->integer == 1) { ri->flags |= SRI_MASTER_DOWN; } else { ri->flags &= ~SRI_MASTER_DOWN; } ……}//客观下线检查void sentinelCheckObjectivelyDown(sentinelRedisInstance *master) { unsigned int quorum = 0, odown = 0; if (master->flags & SRI_S_DOWN) { quorum = 1; di = dictGetIterator(master->sentinels); //遍历监视master的sentinel的flag的下线标志 while((de = dictNext(di)) != NULL) { sentinelRedisInstance *ri = dictGetVal(de); if (ri->flags & SRI_MASTER_DOWN) quorum++; }//当下线标志数量大于master->quorum,认定为客观下线 if (quorum >= master->quorum) odown = 1; } if (odown) {//更新master的下线状态 if ((master->flags & SRI_O_DOWN) == 0) { master->flags |= SRI_O_DOWN; master->o_down_since_time = mstime(); } } else { if (master->flags & SRI_O_DOWN) { master->flags &= ~SRI_O_DOWN; } }}
二、故障转移
· (1)、领头的选举
每个sentine都会向其他监视master的sentinel请求,请求成为目标sentinel局部领头sentinel,目标sentinel则会
返回自己设置好的局部领头给源sentinel。sentinel设置局部领头sentinel的规则是先到先得:最先向目标sentinel发
送设置要的源sentinel将会成为目标sentinel的局部领头sentinel。得到半数以上sentinel支持的sentinel将成为领头,
如果没有超过半数,在下个配置纪元再进行领头选举。
目标sentinel接收成为局部领头sentinel的请求,进行领头选举
char *sentinelVoteLeader(sentinelRedisInstance *master, uint64_t req_epoch, char *req_runid, uint64_t *leader_epoch) { ……/*只有当请求纪元大于当前纪元,sentinel才能成为局部领头,并更新当其纪元 让同一纪元的另外的领头请求请求失败*/ if (master->leader_epoch < req_epoch && sentinel.current_epoch <= req_epoch) { sdsfree(master->leader); master->leader = sdsnew(req_runid); master->leader_epoch = sentinel.current_epoch; } …… *leader_epoch = master->leader_epoch; return master->leader ? sdsnew(master->leader) : NULL;}
(2)、故障转移
领头选举完成后,领头sentinel对下线的master执行故障转移操作,该操作包含以下三个步骤:
1)在下线master的从节点中选出一个slave,将其转换成master;
2)让master的slave改为复制新master;
3)将下线master设置为新master的从节点,当其重新上线时,成为新master的slave。
其中新master的选择规则是领头sentinel先删除列表中处于非正常状态的节点根据slave的slave_priority,slave_repl_offset,
runid对列表中剩余的slaves进行排序,选择slave_priority最高;slave_priority相同选择slave_repl_offset最大;slave_repl_offset
相同时选择runid最小的。
sentinelRedisInstance *sentinelSelectSlave(sentinelRedisInstance *master) { sentinelRedisInstance **instance =zmalloc(sizeof(instance[0])*dictSize(master->slaves)); …… di = dictGetIterator(master->slaves); while((de = dictNext(di)) != NULL) { if (slave->flags & (SRI_S_DOWN|SRI_O_DOWN)) continue;//过滤下线的slave if (slave->link->disconnected) continue;//过滤断开连接的slave if (mstime() - slave->link->last_avail_time > SENTINEL_PING_PERIOD*5) continue; //过滤掉SENTINEL_PING_PERIOD*5没有回复头ping命令的slave if (slave->slave_priority == 0) continue; if (mstime() - slave->info_refresh > info_validity_time) continue; //过滤info_validity_time内*5没回复领头info命令的slave if (slave->master_link_down_time > max_master_down_time) continue; //过滤与已下线master断开连接超过max_master_down_time instance[instances++] = slave; } …… qsort(instance,instances,sizeof(sentinelRedisInstance*),compareSlavesForPromotion); ……}//从服务器的排序规则int compareSlavesForPromotion(const void *a, const void *b) { sentinelRedisInstance **sa = (sentinelRedisInstance **)a, **sb = (sentinelRedisInstance **)b; //优先级比较 if ((*sa)->slave_priority != (*sb)->slave_priority) return (*sa)->slave_priority - (*sb)->slave_priority; if ((*sa)->slave_repl_offset > (*sb)->slave_repl_offset) { return -1; //复制偏移量比较 } else if ((*sa)->slave_repl_offset < (*sb)->slave_repl_offset) { return 1; /* b > a */ } sa_runid = (*sa)->runid; sb_runid = (*sb)->runid; if (sa_runid == NULL && sb_runid == NULL) return 0; else if (sa_runid == NULL) return 1; /* a > b */ else if (sb_runid == NULL) return -1; /* a < b */ return strcasecmp(sa_runid, sb_runid);//运行id比较}
故障转移的代码实现过程是一个状态机(sentinelFailoverStateMachine函数)如下图所示:
故障转移状态
SENTINEL_FAILOVER_STATE_NONE 0 /*没有failover在进行*/SENTINEL_FAILOVER_STATE_WAIT_START 1 /* 领头sentinel接手failover*/SENTINEL_FAILOVER_STATE_SELECT_SLAVE 2 /* 选择slave成为master*/SENTINEL_FAILOVER_STATE_SEND_SLAVEOF_NOONE 3 /* 发送slaveof no one给新master */SENTINEL_FAILOVER_STATE_WAIT_PROMOTION 4 /* 等待新master升级完成,超时终止failover*/SENTINEL_FAILOVER_STATE_RECONF_SLAVES 5 /* 新master升级完成后,让slaves复制新master */SENTINEL_FAILOVER_STATE_UPDATE_CONFIG 6 /* 监视新master */开始进行failover,向其它sentinel请求成为其局部领头,sentinelFailoverStateMachine根据不同状态做出相应的处理。
void sentinelHandleRedisInstance(sentinelRedisInstance *ri) { …… if (ri->flags & SRI_MASTER) { if(sentinelStartFailoverIfNeeded(ri)) //请求成为其他sentinel的局部领头 sentinelAskMasterStateToOtherSentinels(ri,SENTINEL_ASK_FORCED); sentinelFailoverStateMachine(ri); }}//判断master是否客观下线,并且master没有进行failoverint sentinelStartFailoverIfNeeded(sentinelRedisInstance *master) { if (!(master->flags & SRI_O_DOWN)) return 0; if (master->flags & SRI_FAILOVER_IN_PROGRESS) return 0; //设置failover_state为SENTINEL_FAILOVER_STATE_WAIT_START sentinelStartFailover(master); return 1;}状态机中的函数汇总
void sentinelStartFailover(sentinelRedisInstance *master);//failover_state=SENTINEL_FAILOVER_STATE_WAIT_START,开始故障转移void sentinelFailoverWaitStart(sentinelRedisInstance *ri); //如果自己是领头sentinel,failover_state = SENTINEL_FAILOVER_STATE_SELECT_SLAVEvoid sentinelFailoverSelectSlave(sentinelRedisInstance *ri);//选择slave成为新master,failover_state= SENTINEL_FAILOVER_STATE_SEND_SLAVEOF_NOONEvoid sentinelFailoverSendSlaveOfNoOne(sentinelRedisInstance *ri);//发送slaveof no one给新master,failover_state=SENTINEL_FAILOVER_STATE_WAIT_PROMOTIONvoid sentinelFailoverWaitPromotion(sentinelRedisInstance *ri);//等待新master角色转换完成,超时终止failover。void sentinelRefreshInstanceInfo(sentinelRedisInstance *ri, const char *info);/*info新master的回复中的role==SRI_MASTER,本地记录的flag==SRI_SLAVE表示新master转换完,failover_state=SENTINEL_FAILOVER_STATE_RECONF_SLAVES*/void sentinelFailoverReconfNextSlave(sentinelRedisInstance *master);//让slaves复制新master,failover_state=SENTINEL_FAILOVER_STATE_UPDATE_CONFIGvoid sentinelFailoverSwitchToPromotedSlave(sentinelRedisInstance *master);//监视新master,failover_state=SENTINEL_FAILOVER_STATE_NONE
- redis sentinel哨兵机制的源码分析
- Redis的哨兵机制 Sentinel(简要)
- redis的哨兵Sentinel
- 搭建redis-sentinel(哨兵机制)集群
- 搭建redis-sentinel(哨兵机制)集群
- 搭建redis-sentinel(哨兵机制)集群
- 搭建redis-sentinel(哨兵机制)集群
- Redis Sentinel 源码分析
- Laravel 5的redis哨兵(sentinel)支持
- Redis的哨兵模式(Sentinel)
- Redis源码剖析和注释(二十四)--- Redis Sentinel实现(哨兵操作的深入剖析)
- 结合redis设计与实现的redis源码学习-21-哨兵(Sentinel.c)
- Redis的哨兵机制
- redis sentinel(哨兵)模式
- Redis:哨兵Sentinel
- redis sentinel(哨兵)
- redis sentinel(哨兵)
- Redis sentinel 哨兵模式
- php xml 转成json
- linux ./configure,make,make install的作用
- apache2.4 以后配置相关
- delete一个指针之后,要记得设置为NULL
- Axure 盗版使用被终结,我们该怎么办
- redis sentinel哨兵机制的源码分析
- 程序员值得一看的博客及网站
- 阿里云登入主页设置
- 3个案例教你用Spark解决Map Reduce问题!
- Linux运维之加密/解密原理、自建CA及搭建基于https的Apache
- Cenots6.4下mysql5.6安装及主从配置
- iOS 去掉粘贴文字中的换行(适配安卓)
- Vue.js中Directive知识
- ANSI C (5) —— 结构、联合、位字段、枚举