在前面的一篇,已经说到Sentinel通过INFO命令获得主服务器的信息,然后保存在自己Master属性里(创建SentinelRedisInstance对象),然后主服务器里面的信息还有从服务器的信息,也会通过这些信息与从服务器建立连接,然后也是通过INFO命令获得从服务器的信息,然后保存在主服务器的SentinelRedisInstance对象的slave字典(键名为从服务器的ip:port,值为对应的从服务器的SentinelRedisInstance对象)
下面就来说一下,Sentinel系统(注意这里是整个Sentinel系统,而不是一个Sentinel)是如何与主从服务器进行通信的
Sentinel系统接收来自主服务器和从服务器的频道信息
当Sentinel与一个主服务器或者从服务器建立起命令连接之后,Sentinel就会创建订阅连接,而这个订阅连接其实就是一条订阅命令,让Sentinel订阅服务器的sentinel:hello频道
subscribe _sentinel_:hello
当主服务器或者从服务器往该频道消息里面发送信息时,所有的Sentinel都是可以收到的。
Sentinel系统向主服务器和从服务器发送信息
知道了Sentinel是如何接收主从服务器的消息后,下面就看看Sentinel是怎么给主服务器和从服务器发送信息的
Sentinel其实是通过命令连接去给主从服务器发送消息的,但接收信息的时候是使用订阅连接的
在默认情况下,Sentinel会以每两秒一次的频率,通过命令连接向所有被监视的主服务器和从服务器发送以下格式的命令
publish _sentinel_:hello "......"
其实就是一个发布消息的命令,让主从服务器往_ sentinel _:hello的频道里面发送消息
主要有以下这些信息
参数 | 意义 |
---|---|
s_ip | Sentinel的IP地址 |
s_port | Sentinel的端口号 |
s_runid | Sentinel的运行ID |
s_epoch | Sentinel当前的配置纪元 |
m_name | 主服务器的名字 |
m_ip | 主服务器的ip地址 |
m_port | 主服务器的端口号 |
m_epoch | 主服务器当前的配置纪元 |
注意,这里的m_开头的属性是被Sentinel监视的服务器的属性,一定是主服务器的信息,如果被监视的是主服务器,就是主服务器本身的信息,如果是从服务器就是从服务器正在复制的主服务器的信息。
s_开头的属性就是Sentinel本身的信息。
Sentinel发送与接收信息过程
Sentinel系统通过命令连接,让服务器去发送自己想要的信息,然后Sentinel系统通过订阅连接,获取服务器发送在频道里面的信息,这就是整个发送与接收流程。
但为什么要采用订阅方式去实现呢?接收这些信息有什么用?如何区分这条消息是自己发送的呢?
- Sentinel系统不止只有一个Sentinel服务器,当然每台主服务器也不止有一台Sentinel监视它,Sentinel系统里面的所有监视该主服务器的Sentinel都会去订阅主服务器的sentinel:hello频道,所以只要被监视的服务器往该频道上发送信息,所有监视该主服务器的Sentinel都是可以收到的
- 这些信息会被用于更新其他监视该服务器的Sentinel对被监视服务器的认知,同时也会更新其他监视该服务器的Sentinel对发送信息Sentinel的认知。
- 发送信息里面有一个参数s_runid,Sentinel会通过比较自己的RunId是否匹配,如果匹配,证明是自己发的,那么就不做处理直接丢弃,如果不匹配,证明不是自己发的,接收到信息的其他Sentinel会根据信息中的各个参数,对相应主服务器的实例结构进行更新
更新Sentinel字典
前面提到过,SentinelRedisInstance不仅会保存主服务器信息、从服务器信息,甚至还会保存其余Sentinel信息,主服务器信息有对应的master字典,从服务器信息有对应的slave字典,所以Sentinel信息也有自己的sentinels字典(这个字典与slave字典一样,也是被保存在主服务器实例对象里面的)。
- Sentinels字典的键是Sentinel的名字,格式也是为ip:port(配置文件那里可以规定Sentinel的port)
- Sentinels字典的值则是对应的Sentinel的实例结构
上面提到了,监视同一台服务器的Sentinel都可以收到服务器发送的信息(信息里面有要求发送的Sentinel的信息)。
当一个Sentinel收到了其他Sentinel的信息时(也就是服务器发送信息里面的Sentinel信息,通过匹配Runid来区分是不是自己要求发送的),目标Sentinel(接收到信息的Sentinel)就会从信息中分析并且提取出下面列出的两方面参数
- 与发送方Sentinel有关的参数:源Sentinel的ip地址、端口号、运行ID和配置纪元
- 与被监视的主服务器有关的参数:源Sentinel正在监视的主服务器的名字、ip地址、端口号和配置纪元
目标Sentinel收到主服务器有关的参数,就会去找自己master字典里面对应的主服务器实例(一般来说都会对应的上,因为只有监视同一台主服务器,才会订阅相同服务器的频道),找到主服务器实例之后,就找里面的sentinels字典,根据提出的源Sentinel相关的参数,去看看源Sentinel的实例是否已经存在。
对比完之后就会出现下面两种情况
- 源Sentinel的实例结构已经存在:那么对找到的源Sentinel实例进行信息更新即可
- 源Sentinel的实力结构不存在:那么说明了,这是一台新加入的Sentinel,该新加入的Sentinel与自己监视的主服务器还是同一台,目标Sentinel就会为其创建一个新的实例结构,并且将这个结构加入到Sentinels字典中去。
这也是为什么,在Sentinel的配置文件里面不需要专门去配置其他监视同一台服务器的Sentinel的地址信息,然后就构成了一个Sentinels系统。
就是因为Sentinels可以通过接收消息去发现新加入的Sentinels,也可以通过发送消息,让别的Sentinel发现自己
创建连接向其他Sentinel的命令连接
Sentinels可以通过接收消息去发现新加入的Sentinels,也可以通过发送消息,让别的Sentinel发现自己。
当Sentinel通过频道信息发现一个新的Sentinel时,它不仅会为新的Sentinel在对应的master字典里面的主服务器实例里面的Sentinels字典新建一个Sentinel实例,同时还会创建一个连向新的Sentinel的命令连接,新的Sentinel也同样去发现,然后创建连向这个Sentinel的命令连接
这样导致的最终结果就是,监视同一台服务器的Sentinels会建立起自己的网络连接
Sentinel之间实现互相通信,就可以去判断监视的主从服务器是否下线了(又分为主观下线和客观下线)
注意:Sentinel之间是不会创建订阅连接
Sentinel是为了可以去发现新的Sentinel才需要去与被监视的服务器建立订阅连接,而相互已知的Sentinel只要使用命令连接来进行通信就足够了