目录
- Sentinel简介
- Sentinel服务器的初始化
- 获取主从服务器信息
- 向主从服务器发送接收消息
- 检测主观客观下线状态
- 选举领头Sentinel
- 故障转移
1. Sentinel简介
Sentinel是Redis高可用性的解决方案。一个或者多个Sentinel实力可以组成一个Sentinel系统,用来监视这些主从服务器,当主服务器下线时,还可以故障转移,重新选举。
2. Sentinel服务器的初始化
初始化分成5个步骤
- 初始化服务器
- 将普通的Redis服务器使用的代码替代成Sentinel专用代码
- 初始化Sentinel状态
- 初始化Sentinel的监视主服务器列表
- 创建连向主服务器的网络连接
步骤一:初始化服务器
Sentinel的初始化其实跟普通的redis服务器相似,包括三个步骤:
- 初始化服务器状态结构
- 载入配置选项
- 初始化服务器数据结构
步骤二:使用Sentinel专用代码
普通Redis服务器使用redis.h/REDIS_SERVERPORT常量作为服务器的端口,而Sentinel使用sentinel.c/REDIS_SENTINEL_PORT常量的值作为服务器端口。
#define REDIS_SERVERPORT 6379 #define REDIS_SENTINEL_PORT 26379
- 普通Redis服务器使用redis.c/redisCommandTable作为服务器的命令表:
而Sentinel使用sentinel.c/sentinelcmds作为服务器的命令表
从上面的命令可以看出来,在Sentinel模式下,Reids服务器不能执行SET,DBSIZE等操作,而类似PING,SENTINEL,INFO这些命令就是客户端可以对Sentinel执行的命令。
步骤三:初始化Sentinel状态
Sentinel的状态由 sentinelState结构来表示:
struct sentinelState{
//当前纪元,用于实现故障转移
unit64_t current_epoch;
//保存着被这个Sentinel监视的主服务器
dict *masters;
//是否进入TILT模式
int tilt;
//正在执行的脚本的数量
int running_scripts;
//进入TILT模式的时间
mastime_t tilt_start_time;
//最后一次执行时间处理器的时间
mastime_t previous_time;
//一个FIFO队列,包含了所有需要执行的用户脚本
list *scripts_queue;
}
步骤三:初始化Sentinel状态的masters属性
masters字典记录了所有被Sentinel监视的主服务器的相关信息:
- 字典的键是被监视的主服务器的名字
- 字典的值是被监视主服务器对应的sentinel.c/sentinelRedisInstance结构
typedef struct sentinelRedisinstance{
//标记值,记录了实例的类型,以及该实例的当前状态。
int flags;
//实例名
char *name;
//实例运行ID
char *runid;
//配置纪元,用于故障转移
unit64_t config_enpoch;
//实例的地址
sentinelAddr *addr;
//从服务器
sentinelRedisinstance *slaves;
//实例 无响应多少毫秒被认为主观下线
mstime_t down_after_period;
//判断这个实例为客观下线所需的支持投票数量
int quorum;
//执行故障转移操作时,可以同时对新的主服务器进行同步的从服务器数量
int parallel_syncs;
你
//刷新故障迁移状态的最大时限
mstime_t failover_timeout;
}
实例的addr属性是一个指向sentinelAddr结构的指针,保存着实例的ip地址和端口号。
typedef struct sentinelAddr{
char *ip;
int port;
}
现在初始化的Sentinel状态的redis服务器结构为:
主从服务器的关系:
步骤五:创建连向主服务器的网络连接
初始化Sentinel的最后一步就是创建连向被监视主服务器的网络连接。Sentinel会创建两个连向主服务器的异步网络连接。
- 一个是命令连接,专门向主服务器发送命令,并接受回复。
- 一个是订阅连接,这个连接用于订阅主服务器的sentinel_:hello频道。
3. Sentinel获取主从服务器的信息
Sentinel默认会以每十秒一次的频率,通过命令连接向被监视的服务器发送INFO命令,通过分析INFO命令的回复来获取主服务器的当前信息。
例如发送了INFO命令,从主服务器中获得回复如下:
#Server
···
run_id:32423ku4h23ohsdfsjklh3289fddsjk
···
# Repilication
role:master
···
slaver0:ip=127.0.0.1,prot=11111,state=online,offset=43,lag=0;
slaver1:ip=127.0.0.1,prot=22222,state=online,offset=43,lag=0;
slaver2ip=127.0.0.1,prot=33333,state=online,offset=43,lag=0;
···
# Other sections
因此可以获取到主服务器本身的信息,run_id记录了服务器运行ID,以及role域记录了服务器角色;而且可以获取slave的信息。因此可以根据这些域对Sentinel服务器的状态结构进行更新。
同理,如果发送INFO命令给从服务器,从服务器会回复如下:
#Server
···
run_id:382974oilfhdsakjfhwqowrws3245df
···
#Repilication
role:slave
master_host:127.0.0.1
master_port:6379
master_link_status:up
slave_repl_offset:11887
slave_priority:100
#Other sections
因此,同样可以获取从服务器的很多信息,然后Sentinel服务器对从服务器的实例结构进行更新。
4. 向主从服务器发送接收消息
发送
默认情况下,Sentinel会以每两秒一次的频率向它监视的主从服务器发送以下格式的命令:
PUBLSIT __sentinel__:hello "<s_ip><s_port><s_runid><s__epoch><m_name><m_ip><m_port><m_enpoch"
其中s代表Sentinel信息, m代表主服务器信息
接收
Sentinel会通过订阅连接,并向服务器发送以下命令:
SUBSCRIBE __sentinel__:hello
于是这个Sentinel就能订阅这个频道的消息。
处理消息
Sentinel为主服务器的实例结构sentinelRedisInstance有一个字段sentinels字典,储存着监视着这个主服务器的sentinels。
所以一个Sentinel收到一条消息,它先查看消息发送者,如果是自身,那么将忽略这条消息,如果不是自身,则根据消息对sentinelRedisInstance的sentinels字典进行更新。
建立连接
当Sentinel通过频道信息发现了一个新的Sentinel,它不仅会为新的Sentinel创建相应的数据结构,还会创建一个连向新的Sentinel的命令连接,新的Sentinel也会同样创建一个连向这个Sentinel的连接。所以,监听同一个服务器的几个Sentinel会形成网络。
5. 主观客观下线状态
主观下线 : Sentinel每秒一次向与它创建命令连接的实例发送ping命令,并通过是实例返回的ping回复来判断是否在线。回复有+PONG,-LOADING,-MASTERDOWN和无效回复,如果不是+PONG就可以认为这个这个实例主观下线。
客观下线 :当Sentinel将一个主服务器判断为主观下线之后,为了确认这个主服务器是否真的下线了,它会向同样监视这一个主服务器的其他Sentinel进行询问,看他们是否也认为主服务器已经进入了下线状态,如果数量超过sentinelRedisInstance结构中的quorum参数,那么认为客观下线,于是就进行主服务器的选举和故障转移。
6. 选举领头Sentinel
每个Sentinel都有成为领头的能力,而且每次选举无论是否成功,都会将配置纪元(confuguration epoch)的值自增,它实际上就是一个计数器。
局部领头:当一个Sentinel A向另一个Sentinel B发送请求SENTINEL is-master-down-by-addr + (Sentinel A 的 runid )代表A想成为B的局部领头。
所以这种规则就是先到先得,最早向目标Sentinel发送这个命令的必然成为它的Sentinel,后面的命令都会无效,当它的票数超过半数时,它就成为领头Sentinel,然后对已经下线的主服务器执行故障转移操作。
7. 故障转移
在选举出领头的Sentinel之后,领头Sentinel对已经下线的主服务器执行故障转移操作。步骤为:
在已下线的主服务器属下的所有从服务器里面,挑选一个从服务器,并将其转换为主服务器。(根据从服务器优先级,相同优先级选择复制偏移量较大的从服务器)
让已下线属下的所有从服务器改为复制新的主服务器,并成为新的主服务器的从服务器。
当旧的主服务器重新上线之后,它就会成为新的主服务器的从服务器。