推荐渐进式解析 Redis 源码 - 哨兵 sentinel 上面有讲解的主观下线和客观下线
前言
下面是我摘抄的有关主观下线和客观下线的介绍:
-
主观下线 SDOWN: 如果 监控的服务器节点 在
down-after-milliseconds
设置的毫秒时效内没有响应检测,则会被判定为 主观下线;这个状态适用所有服务器节点 -
客观下线 ODOWN: 主服务器节点在发生故障的时候,
sentinel
会通过is-master-down-by-addr
命令向其他 sentinel 节点询问 该主服务器节点的状态,如果超过quorum
个数的哨兵节点都认为 主服务器节点不可达,则判定为客观下线;这个状态只会针对 主服务器节点(对于从服务器没有客观下线,从下面源码中能找到答案)
那客观下线与主观下线与sentinel
关系其实也是redis
和sentinel
的交互
- 每个
sentinel
以 每秒一次 的频率,向它所知的 主服务器节点 和 从服务器节点 以及其他sentinel
节点 发送PING 命令,检测其状态 - 如果一个 节点 或者 实例 距离 最后一次 有效回复 PING 命令的时间超过
down-after-milliseconds
设定的值,那么这个 节点 或者 实例会被sentinel
标记为SDOWN
主观下线 - 随后正在监控这个 主服务器节点 的所有
sentinel
都要以每秒一次的频率确认这个 主服务器节点 是否真正进入了 主观下线状态,一旦有 足够数量 的sentinel
在指定时间范围内同意这个下线判断,那么这个 服务器就要被标记为ODOWN 客观下线
,然后sentinel
向 客观下线主服务器 的所有 从服务器 发送 INFO 命令的频率,会从 10 秒一次改为 每秒一次 - 当然,如果 中途当没有足够数量的
sentinel
同意下线判断,那这个 客观下线 状态就会被移除,而且当 主服务器重新对 PING进行有效响应的时候,那 主观下线状态就会被移除 - 接上面,如果还是 客观下线状态,就需要进行 主服务器 选举逻辑
sentinelCheckSubjectivelyDown(主观下线)
在
sentinelHandleRedisInstance
方法下有sentinelCheckSubjectivelyDown
方法来进行判定,
主要的流程:如果 监控的服务器节点 在down-after-milliseconds
设置的毫秒时效内没有响应检测,则会被判定为 主观下线;这个状态适用所有服务器节点
/* Is this instance down from our point of view?
从主观的角度看这个实例是否是下线状态*/
void sentinelCheckSubjectivelyDown(sentinelRedisInstance *ri) {
mstime_t elapsed = 0;
// 获取命令响应已经过去的时长
if (ri->link->act_ping_time)
elapsed = mstime() - ri->link->act_ping_time;
// 实例连接断开了
else if (ri->link->disconnected)
elapsed = mstime() - ri->link->last_avail_time;
/* Check if we are in need for a reconnection of one of the
* links, because we are detecting low activity.
*低活跃度重新连接,即 连接超过了 1.5秒,并且之前发送了 PING 命令但是活跃度很低
* 1) Check if the command link seems connected, was connected not less
* than SENTINEL_MIN_LINK_RECONNECT_PERIOD, but still we have a
* pending ping for more than half the timeout.
检查命令链接是否似乎已连接,连接时间不少于 SENTINEL_MIN_LINK_RECONNECT_PERIOD,但我们仍有超过一半超时的待处理 ping*/
if (ri->link->cc &&
(mstime() - ri->link->cc_conn_time) >
SENTINEL_MIN_LINK_RECONNECT_PERIOD &&
ri->link->act_ping_time != 0 && /* There is a pending ping... */
/* The pending ping is delayed, and we did not receive
* error replies as well. */
(mstime() - ri->link->act_ping_time) > (ri->down_after_period/2) &&
(mstime() - ri->link->last_pong_time) > (ri->down_after_period/2))
{
// 断开实例的连接
instanceLinkCloseConnection(ri->link,ri->link->cc);
}
/* 2) Check if the pubsub link seems connected, was connected not less
* than SENTINEL_MIN_LINK_RECONNECT_PERIOD, but still we have no
* activity in the Pub/Sub channel for more than
* SENTINEL_PUBLISH_PERIOD * 3.
*/
// 检查 订阅发布的连接是不是也是出于 低活跃度
if (ri->link->pc &&
(mstime() - ri->link->pc_conn_time) >
SENTINEL_MIN_LINK_RECONNECT_PERIOD &&
(mstime() - ri->link->pc_last_activity) > (SENTINEL_PUBLISH_PERIOD*3))
{ // 断开实例连接
instanceLinkCloseConnection(ri->link,ri->link->pc);
}
/* Update the SDOWN flag. We believe the instance is SDOWN if:
*更新 SDOWN 标志。我们认为实例是 SDOWN 如果
* 1) It is not replying. 它没有回复
* 2) We believe it is a master, it reports to be a slave for enough time
* to meet the down_after_period, plus enough time to get two times
* INFO report from the instance. sentinel 认为是主节点但是 报告是从节点*/
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)))
{
/* Is subjectively down // 设置主观下线标识*/
if ((ri->flags & SRI_S_DOWN) == 0) {
sentinelEvent(LL_WARNING,"+sdown",ri,"%@");
ri->s_down_since_time = mstime();
ri->flags |= SRI_S_DOWN;
}
} else {
/* Is subjectively up // 如果已经是主观下线状态,则取消标识*/
if (ri->flags & SRI_S_DOWN) {
sentinelEvent(LL_WARNING,"-sdown",ri,"%@");
ri->flags &= ~(SRI_S_DOWN|SRI_SCRIPT_KILL_SENT);
}
}
}
sentinelCheckObjectivelyDown(客观下线)
sentinelHandleRedisInstance
方法下的sentinelCheckObjectivelyDown
方法,这里需要注意的是,客观下线是针对主服务器节点才能做的状态标识,从服务器节点就不会有这个状态标识
/* Is this instance down according to the configured quorum?
*
* Note that ODOWN is a weak quorum, it only means that enough Sentinels
* reported in a given time range that the instance was not reachable.
* However messages can be delayed so there are no strong guarantees about
* N instances agreeing at the same time about the down state.
请注意,ODOWN 是一个弱仲裁,它仅表示在给定时间范围内报告了足够多的 Sentinel 报告该实例无法访问。但是消息可能会延迟,因此无法保证 N 个实例同时同意关闭状态*/
void sentinelCheckObjectivelyDown(sentinelRedisInstance *master) {
dictIterator *di;
dictEntry *de;
unsigned int quorum = 0, odown = 0;
// 如果已经被判断为主观下线
if (master->flags & SRI_S_DOWN) {
/* Is down for enough sentinels? */
// 当前 哨兵认为下线,投票1
quorum = 1; /* the current sentinel. */
/* Count all the other sentinels. */
di = dictGetIterator(master->sentinels);
// 遍历所有监控该主节点的所有 sentinel
while((de = dictNext(di)) != NULL) {
sentinelRedisInstance *ri = dictGetVal(de);
// 如果 sentinel 投票主观下线,投票 +1
if (ri->flags & SRI_MASTER_DOWN) quorum++;
}
dictReleaseIterator(di);
// 如果投票数够了,则认为 客观下线
if (quorum >= master->quorum) odown = 1;
}
/* Set the flag accordingly to the outcome. */
// 如果已经判定为 客观下线了
if (odown) {
// 如果主服务器节点还没改变状态,则修改其 客观下线状态
if ((master->flags & SRI_O_DOWN) == 0) {
sentinelEvent(LL_WARNING,"+odown",master,"%@ #quorum %d/%d",
quorum, master->quorum);
master->flags |= SRI_O_DOWN;
master->o_down_since_time = mstime();
}
} else {
// 如果票数不足,原本 客观下线就要改回来为 主观下线
if (master->flags & SRI_O_DOWN) {
sentinelEvent(LL_WARNING,"-odown",master,"%@");
master->flags &= ~SRI_O_DOWN;
}
}
}