本文转载自微信公众号《SH的全栈笔记》,作者SH。转载本文请联系SH全栈笔记公众号。本篇博客会简单介绍Redis的Sentinel相关原理,也会在最后的文章中给出一个“硬核”的实战教程,让大家在了解原理后实际体验整个过程。上一篇讲了Redis的主从复制,以及相关的原理和缺点。具体建议可以参考我之前的文章Redis主从复制。总的来说,要满足Redis在真正复杂的生产环境中的高可用,仅仅使用主从复制显然是不够的。比如当主节点宕机,进行主从切换时,我们需要手动做failover。同时,在流量方面,主从架构只能通过增加从节点来扩展读请求,而受限于主单节点的资源限制,无法扩展“写能力”。这就是我们需要引入Sentinel的原因。Sentinel功能概述Sentinel的通用功能如下图所示。SentinelSentinel是Redis的高可用解决方案之一。它也是一个分布式架构,包括“多个”Sentinel节点和“多个”Redis节点。而每个Sentinel节点都会监控Redis节点和其余的Sentinel节点。当它发现某个节点不可达时,如果它是主节点,它会与其余的Sentinel节点协商。当大多数Sentinel节点认为master不可达时,会选择一个Sentinel节点对master进行故障转移,并将相关变化通知给Redis的调用者。与“Master-Slave”下的手动故障转移相比,Sentinel的故障转移是全自动的,无需人工干预。Sentinel本身就有666的高可用,那我怎么知道需要部署多少个Sentinel节点才能满足自己的高可用呢?因为Sentinel本身也是分布式的,所以也需要部署多个实例来保证自身集群的高可用,但是这个数量是有限的。最低要求,最低为“3”。我去,你说3是3?我今天刚部署了2个,别开玩笑。。。我说了,你为什么非要3个。。。因为Sentinels需要“大部分”的Sentinels来执行failover如果你们都同意就好了,而且有效如果你只有两个Sentinel实例就好了,就像这样。如果sentinel所在的机器由于机房断电、光纤被挖等极端情况挂掉了,那么另一个sentinel发现master故障后想要进行failover,但是无法得到任何“其他”哨兵”。节点”的协议,此时可以进行“永不”故障转移,那么Sentinel就成了一个摆设?所以我们至少需要3个节点才能保证Sentinel集群本身的高可用。当然这3个Sentinel节点是绝对推荐的要部署在“不同”的机器上,如果所有的Sentinel节点都部署在同一台机器上,那么当这台机器挂了,整个Sentinel就不复存在了redis-sentinel-successquorum&majority?大哥,这个要在生产环境,大部分数字都太敷衍了,就不能专业点吗?上面说的sentinel大多涉及两个参数,一个叫quorum,如果Sentinel集群中有quorum哨兵认为masterdown了,才算是“客观上”master宕机了,另外一个叫majority……等等等等,不是已经有quorum了么,还需要这个majority,能不能等我说完说话...法定人数有权利t没说,它的作用是判断master是否宕机,只是一个“判断”的功能。而在我们的实际制作中,也不仅仅是“判断”master挂了。难道我们不需要执行“故障转移”才能使集群正常工作吗?同理,当sentinel集群开始failover时,如果有大多数sentinel同意failover,才能选择一个sentinel节点执行failover操作。主观宕机&客观宕机你刚才提到“客观宕机”了吗?笑,有主观宕机这回事吗?在Sentinel中,一个节点的宕机分为两种:SubjectiveDown,简称“sdown”,主观上认为master宕机ObjectiveDown,简称“odown”,客观上认为master宕机当一个Sentinelnode跟它监控的Redis节点A通信,发现连接不上,那么sentinel节点就会“主观上”认为Redis数据节点A宕机了,为什么会“主观”呢?首先要知道什么是主观的,没有经过分析计算,得出结论、决策和行为反应,暂时无法与其他不同意见的对象仔细讨论,这叫做主观。?简单来说,因为有可能“只有”当前的Sentinel节点与这个A节点的网络通信出现问题,其余的Sentinel节点仍然可以与A正常通信。sentinel-sdown这就是我们需要引入“odown”的原因。当大于或等于“法定人数”的Sentinel节点认为某个节点宕机时,我们会“客观地”认为该节点宕机。当Sentinel集群客观认为master宕机时,会从所有Sentinel节点中选出一个Sentinel节点最终执行master的故障转移。那么这个“故障转移”到底是做什么的呢?我们通过一张图来看一下。通知被调用的clientmaster发生了变化,并通知其余原来的slave节点复制Sentinel选出的新master节点。如果此时原来的master恢复了,Sentinel也会让它复制新的master节点。成为新的从节点。硬核教程硬核教程旨在让你以最快的方式在本地体验Redis主从架构和Sentinel集群的搭建,体验整个故障转移过程。前提条件已安装docker已安装docker-compose准备一个compose文件,首先需要准备一个目录,然后分别创建两个子目录。如下。$tree..├──redis│└──docker-compose.yml└──sentinel├──docker-compose.yml├──sentinel1.conf├──sentinel2.conf└──sentinel3.conf2directories,5filesbuildRedis主从服务器redis目录下的docker-compose.yml内容如下。版本:'3'服务:master:image:rediscontainer_name:redis-masterports:-6380:6379slave1:image:rediscontainer_name:redis-slave-1ports:-6381:6379command:redis-server--slaveofredis-master6379slave2:image:rediscontainer_name:redis-slave-2ports:-6382:6379command:redis-server--slaveofredis-master6379上面的命令,简单说明slaveof就是让两个slave节点复制container_name为redis-master的节点,从而形成一个简单的master-slave3个节点的架构然后使用命令行进入当前目录,只需要输入命令docker-composeup,剩下的交给docker-compose,它会启动我们需要的所有节点。此时,我们还需要获取刚刚启动的master节点的IP。简要步骤如下:1、通过dockerps找到对应master节点的containerIDps$dockerpsCONTAINERIDIMAGECOMMANDCREATEDSTATUSPORTSNAMES9f682c199e9bredis"docker-entrypoint.s..."3secondsagoUp2seconds0.0.0.0:6381->6379/tcpredis-slave-12572ab587558redis"docker-entrypoint.s..."3secondsagoUp2seconds0.0.0.0:6382->6379/tcpredis-slave-2f70a9d9809bcredis"docker-entrypoint.s..."3secondsagoUp2seconds0.0.0.0:63790->/tcpredis-master也是f70a9d9809bc.2、通过dockerinspectf70a9d9809bc,获取对应容器的IP,在NetworkSettings->Networks->IPAddress字段。然后记录这个值,这里我的值为172.28.0.3。构建Sentinel集群的Sentinel目录下的docker-compose.yml内容如下。版本:'3'服务:sentinel1:image:rediscontainer_name:redis-sentinel-1ports:-26379:26379command:redis-sentinel/usr/local/etc/redis/sentinel.confvolumes:-./sentinel1.conf:/usr/local/etc/redis/sentinel.confsentinel2:image:rediscontainer_name:redis-sentinel-2ports:-26380:26379command:redis-sentinel/usr/local/etc/redis/sentinel.confvolumes:-./sentinel2.conf:/usr/local/etc/redis/sentinel.confsentinel3:image:rediscontainer_name:redis-sentinel-3ports:-26381:26379command:redis-sentinel/usr/local/etc/redis/sentinel.confvolumes:-./sentinel3.conf:/usr/local/etc/redis/sentinel.confnetworks:default:external:name:redis_default这里也解释一下命令redis-sentinel。该命令使redis以哨兵模式启动,本质上是一个运行在特殊模式下的redis服务器。与redis-server不同的是它们加载的是不同的命令表,而sentinel不能进行redis特有的各种set-get操作。创建三个相同的文件,分别命名为sentinel1.conf、sentinel2.conf和sentinel3.conf。其内容如下:port26379dir"/tmp"sentineldeny-scripts-reconfigyessentinelmonitormymaster172.28.0.363792sentinelconfig-epochmymaster1sentinelleader-epochmymaster1可以看到在我们的sentinel配置文件中sentinelmonitormymaster172.28.0.363792表示让它监听mymaster的名字注意这里的IP必须是自己主节点的IP,最后2个就是我们前面说的quorum。然后在命令行进入名为sentinel的目录,点击docker-composeup。至此,Sentinel集群启动。手动模拟master挂掉然后我们需要手动模拟master挂掉来验证我们搭建的Sentinel集群是否可以正常进行failover。从命令行输入名为redis的目录,然后键入以下命令。docker-composepausemaster此时会暂停master容器的运行,让我们等待“10秒”,然后我们就可以看到sentinel输出如下日志。redis-sentinel-2|1:X07Dec202001:58:05.459#+sdownmastermymaster172.28.0.36379.....redis-sentinel-1|1:X07Dec202001:58:06.932#+switch-mastermymaster172.28.0.36379172.28.0.26379dede,你上传一堆日志文件干嘛?凑字数?你这种鬼能听得懂吗??确实,只是一行一行的看日志文件,即使我两周后再看一遍,我还是一头雾水。日志文件完整的描述了整个Sentinel集群从故障转移开始到最后执行的所有细节,但是这里不方便大家直接放出来。所以为了让大家更直观的了解流程,我简单的把流程抽象成一张图。图片结合log应该更容易让大家看懂。我也把sentinel-process中关键步骤的相关解释放到了图中。最后的结果就是master从我们原来的172.28.0.3切换到了172.28.0.2,也就是原来的slave节点之一。这个时候我们也可以连接到容器172.28.0.2,通过命令查看它的当前情况。role:masterconnected_slaves:1slave0:ip=172.28.0.4,port=6379,state=online,offset=18952,lag=0master_replid:f0bf5d1c843ec3ab005c5ac2b864f7ffdc6a8217master_replid2:72c43e1f9c05d4b08bea6bf9b2549997587e261cmaster_repl_offset:18952second_repl_offset:16351repl_backlog_active:1repl_backlog_size:1048576repl_backlog_first_byte_offset:1repl_backlog_histlen:18952可以看到,现在的172.28.0.2这个节点的角色变成了“master”,并且只有一个slave节点连接到它,因为当前“原来的master”还没有启动,总共只有两个存活的实例。重启原master接下来我们模拟重启原master,看看会发生什么。或者通过命令行进入名为redis的本地目录,使用docker-composeunpausemaster模拟原master故障恢复后上线。同样,我们连接到原来的master机器。$dockerexec-itf70a9d9809bc1e924a5be0135888067ad3eb16552f9eaf82495e4c956b456cd9/bin/sh;exit#redis-cli127.0.0.1:6379>inforeplication#Replicationrole:slavemaster_host:172.28.0.2master_port:6379master_link_status:up......master断线重连之后,角色也成为新master(即节点172.28.0.2)的slave。那么我们也可以通过查看新主节点的复制情况来作证。#Replicationrole:masterconnected_slaves:2slave0:ip=172.28.0.4,port=6379,state=online,offset=179800,lag=0slave1:ip=172.28.0.3,port=6379,state=online,offset=179800,lag=1......原master短线重连后,其“connected_slaves”变为2,而“原master”172.28.0.3被明确标记为slave1,这也符合开篇所述的原理和图。
