Redis高可用解决方案:有一个或者多个哨兵组成哨兵系统,监控服务系统,当主服务器下线,自动将其下的从服务器升级为主服务器(这里感觉哨兵就是一个管理者,但是权限不高)
一 启动并初始化Sentinel
1.初始化服务器:Sentinel本质还是一个redis服务器,但是由于不适用数控功能,所以Sentinel的启动就不需要加载持久化数据了,简化得多,比如数据库创建,事务,脚本等等功能不再使用,哨兵更注重的是订阅功能,详细请看下表:
2.使用Sentinel专用代码:将redis服务器启动的代码替换为Sentinel专用代码,切换使用的端口,切换命令表
3.初始化Sentinel状态:sentinelState结构(称为Sentinel状态),该结构保存了服务器中所有和Sentinel相关的状态,服务器的一般状态仍然使用redisServer结构保存,
struct sentineState{
//当前纪元,用于实现故障转移
uint64_t current_epoch;
//保存了所有被这个sentinel 监视的主服务器
//字典的键 是主服务器的名字
//字典的值 则是一个指向sentinelRedisInstance结构的指针
dict *masters;
//是否进入了 TILT模式
int tilt;
//目前 正在执行的脚本的数量
int running_scripts;
//进入 TILT模式的时间
mstime_t tilt_start_time;
//最后异常执行处理器的时间
mstime_t previous_time;
//一个FIFO队列,包含了所有需要执行的用户脚本
list *scripts_queue
}sentinel;
4.初始化Sentinel中的masters属性:它是一个字典,记录了所有被监视的主服务器信息,键是服务器名字,值是sentinelRedisInstance实例
typedef struct sentinelRedisInstance{
//标识值,记录了实例的类型,以及该实例的当前状态
int flags;
//实例的名字
//主服务器的名字 由用户在配置文件中设置
//从服务器以及 Sentinel 的名字由 Sentinel 自动设置
//格式为ip:port 例如“127.0.0.1:26379”
char *name;
//实例的运行ID
char *runid;
//配置纪元,用于实现故障转移
uint64_t config_epoch;
// 实例的地址
sentinelAddr *addr;
// SENTINEL down-after-milliseconds 选项设定的值
//实例无响应多少毫秒之后才会被判断为 主管下线
metime_t down_after_period;
//判断这个实例 为客观下线,所需的支持投票数量
int quorum;
//在执行故障转移操作时,可以同时对新的主服务器进行同步的从服务器数量
int parallel)syncs;
//刷新故障迁移状态的最大时限
mstime_t failover_timeout;
//...
}sentinelRedisInstance;
在masters字典初始化的过程中,会根据载入的Sentinel配置文件来进行,
所以sentinel管理的 主从服务器的添加,就是在配置文件中直接添加。
5.创建练习主服务器的网络连接:Sentinel作为主服务器的客户端,它可以向主服务器发送命令,sentinel有两个连接,一个是命令连接,另外一个是订阅连接
二 获取服务器信息
1.获取master信息
Sentinel默认是十秒一次发送INFO命令,通过分析回复获得主服务器的当前信息,可以获得主的运行id,服务器角色,还有其下面的从服务器信息,ip,port等。例如下方:
Sentinel对master的实例结构进行更新,根据返回的slave信息,更新,如果发现有salve是sentinel没有保存的,那么就是一个新的从服务器,需要建立连接(还是两个),实例同样使用sentinelRedisInstance,但是标志位属性进行了区分:master和slave。
master在sentinel的保存情况:
master的RedisInstance对象中的name值是根据 sentinel-config配置文件中的值设置的,而它的slave对象的name值直接根据Ip:port自动生成的。
2.获取slave信息
当sentinel发现新的slave的时候,会与slave建立连接,与sentinelRedisInstance实例,建立命令连接之后,Sentinel默认会以10s的频率发送INFO命令,获得下面的回复:
上面的信息包括:ip port role,run_id,master_linke_status,还有salve_priority。以及salve_repl_offset复制偏移量
三 sentinel与master和slave的交互
1.sentinel发送信息
Sentinel会以2s/次的频率,通过命令向所有被监视的master和slave发送一下格式的命令:
它向服务器的 __sentinel__:hello 频道发送了一条信息,s_开头的是sentinel本身的信息,m_开头的是master的信息。
2.sentinel接收信息
当订阅连接建立之后,sentinel会向server发送:SUBSCRIBE __sentinel__:hello,对__sentinel__:hello频道的订阅会一直持续到sentinel与服务器的连接断开为止。
所以sentinel通过命令连接向服务器的__sentinel__:hello频道发送信息,又通过订阅连接从服务器的__sentinel__:hello接收信息。
这也做的目的是:进行sentinel之间的通信,因为一个服务器可能不止一个监视器,当server通过订阅连接返回的时候,返回的是所以连接路线,所以监视它的sentinel都会收到信息,如此就进行了sentinel之间的通信。
1.更新sentinels字典:它保存了其他sentinel监视这个master的信息。如ip,端口,运行id等。保存位置位于sentinelRedisInstance结构的sentinels属性中,它也是一个dict结构,而sentinel的保存,同样使用sentinelRedisInstance结构来保存,标识位为:SRI_SENTINEL。 所以,sentinel在发送信息的时候,会连带发送自己的信息,这样的目的就是暴露自己。
2.创建连向其他Sentinel的命令连接:注意只有命令连接,因为订阅连接目的就是用来发现sentinel的。
四 下线状态
1.主观:每秒一次向所有命令连接发送ping,sentinel根据配置文件中的down-after-milliseconds时间来判断server是否下线,如果下线就会修改SentinelRedisInstance的flags属性值,修改为:SRI_S_DOWN标识。
主观下线时长的范围:down-after-milliseconds的值,不仅用来标识sentinel的监听是否下线,同时还标识主从之间是否下线,注意多个sentinel设置的下线时间可能不同,所以判断一个server是否真正下线,还需要进行投票选择。
2.客观:当本哨兵检测主服务器下线,会询问同时检测这个master的其他snetinel,是否判断下线,当sentinel从其他sentinel收到足够数量的down之后,sentinel就会客观的认为master下线,之后就会进行故障转移操作。
询问其他Sentinel,某master是否下线的命令格式:is-master-down-by-addr ip port current_epoch runid
当sentinel收到上方的命令,会提取出ip和port,分析该master是否下线,然后回复三个参数:
down_state: 返回对master的检查结果,1表示下线,0表示正常
leader_runid: *符号 用来检查master状态,如果是局部领头sentinel的运行id,用来选举leader
leader_epoch:局部领头sentinel的配置纪元,用于选举leader。如果runid为*,则epoch为0
五 选举领头Sentinel
主客观下线之后,监视它的哨兵会协商,选举出领头哨兵,让它对下线master执行故障转移。
1.所以监视master的sentinel都有资格
2.每次进行leader选举之后,无论选举是否成功,纪元epoch都会+1
3.在一个配置纪元里面,所有sentinel都有一次将某个sentinel设置为局部领头sentinel的机会。设置后不能更改。
4.每个发现master客观下线的sentinel都会要求其他sentinel将自己设置为局部leader
5.当源sentinel向目标sentinel发送上方is-master-down-by-addr命令的时候,runid参数不是*,而是源sentinel的id,则要求目标sentinel将自己的局部leader设置为源sentinel。
6.sentinel设置局部leader的原则是先到先得,之后再发送设置请求的将被拒绝。
7.目标sentinel收到上方命令后,将回复命令,也是上方三个参数:目标sentinel的局部leader id和配置epoch纪元。
8.源sentinel收到目标sentinel的回复之后,比较返回参数的epoch和自己的epoch是否相同,相同在比较runId是否相同,如果相同,则表明自己被设置为局部Leader,当自己的支持者超过半数,就会成为leader。
9.当规定时间内,没有产生Leader,则重新进行选举。
六 故障转移
1.选出新的master:挑选出一个slave,向这个slave发送SLAVEOF no one命令,如何挑选这个slave呢?
leader sentinel会把下线的master的所有slave保存到一个列表里面,然后按照下面的规则进行过滤:
1.删除列表中所有下线or短线的server
2.删除列表中最近5s没有回复过leader的Info命令的slave
3.删除列表中slave与下线master断开时间超过down-after-millisecods*10毫秒的salve,保证留下的slave的数据要最新。
过滤之后,leader根据slave的优先级进行排序,选出优先级最高的slave。如果优先级并列的很多,再根据复制偏移量进行排序。如果还有多个,再进行runId进行排序,选择id最小的
priority-->offset-->runId
2.修改slave的复制目标:leader让下线master下的所有slave去复制新master,leader向slave发送SLAVEOF+master ip:port命令完成。
3.将old master 变为slave:leader同样向slave发送SLAVEOF命令。
总结
图片源自:https://blog.csdn.net/JeremyJiaming/article/details/100117013
参考:《redis设计与实现》 读书笔记