Redis高可用架构:哨兵的原理与实战
本文将介绍哨兵基于Redis主从复制。其主要任务是解决主节点故障恢复的自动化问题,进一步提高系统的高可用性。
文章首先会介绍哨兵的角色和架构;然后描述哨兵系统的搭建方法以及通过客户端访问哨兵系统的方法;然后简要说明了哨兵实施的基本原则;最后对哨兵的做法提出一些建议。 (注:文章内容基于Redis 3.0版本) 哨兵的角色与架构哨兵的角色
在介绍哨兵之前,先从宏观的角度回顾一下Redis高可用相关的技术。
包括:持久化、复制、哨兵和集群。它们的主要功能和解决的问题有:
- 持久化:持久化是最简单的高可用方法(有时甚至不被归类为高可用资产),它的主要功能是数据备份,即存储数据写入硬盘,保证数据不因退出进程而丢失。
- 复制:复制是高可用Redis的基础。 哨兵和集群都基于复制实现了高可用性。 Replication主要进行多机数据备份以及读操作的负载均衡和简单错误恢复。缺点:故障恢复无法自动化;写操作无法负载均衡;一台机器的存储容量有限。
- 哨兵:基于复制,哨兵实现了自动纠错。缺点:写操作无法负载均衡;一台机器的存储容量有限。
- 集群:通过集群,Redis解决了写操作无法负载均衡以及存储容量受单机限制的问题,实现了比较完整的高可用解决方案。
我们来谈谈哨兵。 Redis Sentinel 或 Redis 哨兵 是在 Redis 2.8 版本中引入的。 哨兵的主要特点是主节点的自动切换。
以下是来自 Redis 官方文档中对 哨兵 功能的描述:
- 监控: 哨兵 会不断检查主节点和从节点是否正常工作。
- 自动切换:当主节点无法正常工作时,哨兵将开始自动切换。它将把故障主节点的一个子节点升级为新的主节点,并让另一个子节点复制新的主节点。
- 配置提供者:初始化时,客户端通过连接哨兵获取当前Redis服务的主节点地址。
- 注意:哨兵可以将切换结果发送给客户端。
其中,监控和自动切换功能可以让哨兵及时发现主节点故障并完成切换;而配置提供者和通知功能必须体现在与客户的交互中。
这里对本文中“客户端”这个词的使用进行解释:在上一篇文章中,只要是通过API访问Redis服务器,就会被称为客户端,包括rediscli、 Java Jedis 客户端等待。
为了方便区分和解释,本文中的客户端不包含redis-cli,但它比redis-cli更复杂。
redis-cli使用了Redis提供的核心接口,客户端封装了这些接口和功能,以充分利用哨兵的配置提供者和通知功能。 ? 哨兵节点组成的节点是特殊的Redis节点,不存储数据。
本部分将介绍一个简单的哨兵系统,包含1个主节点、2个从节点和3个哨兵节点。
方便起见: 所有这些节点都安装在一台计算机上(LAN IP:192.168.92.128),端口号不同;节点的配置尽可能简化。 部署主从节点
哨兵系统主从节点与普通主从节点配置相同,不需要任何额外配置。
以下是主节点(端口=6379)和两个从节点(端口=6380/6381)的配置文件。配置比较简单,不再详细描述。
#redis-6379.confport 6379daemonize yeslogfile "6379.log"dbfilename "dump-6379.rdb"#redis-6380.confport 6380daemonize yeslogfile "6380.log"dbfilename "dump-6380.rdb"slaveof 192.168.92.128 6379#redis-6381.confport 6381daemonize yeslogfile "6381.log"dbfilename "dump-6381.rdb"slaveof 192.168.92.128 6379
配置完成后,依次启动主节点和从节点:
redis-server redis-6379.confredis-server redis-6380.confredis-server redis-6381.conf
节点启动后,连接主节点,检查主从状态是否正常,如下图:
部署哨兵的节点
哨兵的节点基本上是一个特殊的Redis节点。三个哨兵节点的配置几乎相同。主要区别在于端口号(26379/26380/26381)。
下面以节点26379为例介绍节点配置和启动方法。配置部分尽量简单,更多的配置稍后会介绍。
#sentinel-26379.confport 26379daemonize yeslogfile "26379.log"sentinel monitor mymaster 192.168.92.128 6379 2
其中,配置哨兵监控mymaster 192.168.92.128 6379的意义2:节点哨兵监控master节点192.168.92.128:6379。
主节点的名称是mymaster,最后2的含义与主节点故障的判定有关:至少2个哨兵节点必须同意判定主节点故障并执行故障转移。
哨兵 节点有两种启动方式,两者的功能完全相同:
redis-sentinel sentinel-26379.confredis-server sentinel-26379.conf --sentinel
按照上述方式配置并启动后,整个 哨兵 系统启动,您可以通过以下方式连接到 哨兵 节点: redis-cli 用于验证。
如下图所示: 可以看到,已经有26379个哨兵节点在监听主节点mymaster(即192.168.92.128:6379),并发现了它的2个子节点和另外2个哨兵节点。
此时查看哨兵节点的配置文件,会发现一些变化,以26379为例:
其中,dir仅显式声明了数据和日志所在的目录(仅日志在哨兵的背景下); known-slave和known-guard表示哨兵已经发现slave节点和其他哨兵s。
Epoch 参数与配置纪元相关(配置纪元是一个计数器,从 0 开始,每次选举 Leader 哨兵 都会+1;选举 Leader 哨兵 是切换阶段的操作,后面会讲到原理部分)。 自动切换显示
在哨兵的四个角色中,配置和通知提供者需要客户配合。本文将在下一章详细介绍客户端如何访问哨兵系统。
本节将演示哨兵的监控能力以及主节点故障时的自动故障转移。
(1)首先使用Kill命令杀掉master节点:
(2)如果立即在哨兵节点上使用info Sentinel命令查看,会发现master节点并没有切换,因为哨兵发现主节点故障转移需要一些时间。
(3)一定时间后,在哨兵节点上重新运行info Sentinel,发现主节点已经切换到节点6380。
但同时可以发现哨兵节点认为新的主节点仍然有2个子节点。这是因为哨兵将节点6379设置为从节点,同时将6380切换为主节点。
虽然子节点6379死亡,但由于哨兵客观上不会终止该子节点(其含义将在原理部分介绍),因此该子节点被认为一直存在。
当节点6379重新启动时,它将自动成为节点6380的子节点。请检查以下内容。
(4)重启节点6379:可以看到节点6379已经成为节点6380的从节点。
(5)自动切换阶段,哨兵和主从节点的配置文件将被更新被覆盖。
对于主节点和从节点来说,主要变化是slaveof配置:新的主节点没有slaveof配置,其从节点是新主节点的从节点。
对于哨兵节点来说,除了主从节点信息发生变化外,时代也会发生变化。如下图所示,所有与纪元相关的参数都是+1。
总结
在搭建哨兵系统的过程中有几点需要考虑:
- 哨兵系统中的主从节点与普通的主从节点没有什么区别。错误检测和传输由哨兵控制并完成。
- 哨兵 节点基本上都是 Redis 节点。
- 每个哨兵节点只需要配置一个监控主节点即可自动发现其他哨兵节点和从节点。
- 在哨兵节点启动阶段和自动故障转移阶段之间,每个节点的配置文件都会被覆盖(configuration overwrite)。
- 本章示例中,一个哨兵仅控制一个主节点;事实上,一个哨兵可以控制多个主节点,这可以通过配置多个控制监视器来实现。
上一节展示了哨兵的两个主要角色:控制和自动切换。本节结合客户端来展示哨兵的另外两个功能:配置和通知提供者。 示例代码
在介绍客户端原理之前,我们先以Java Jedis客户端为例来展示一下如何使用: 下面的代码可以连接到我们刚刚搭建的哨兵系统,并进行各种读写操作操作(代码中仅展示了如何连接哨兵,未考虑异常处理、关闭资源等)。
public static void testSentinel() throws Exception { String masterName = "mymaster"; Set<String> sentinels = new HashSet<>(); sentinels.add("192.168.92.128:26379"); sentinels.add("192.168.92.128:26380"); sentinels.add("192.168.92.128:26381"); JedisSentinelPool pool = new JedisSentinelPool(masterName, sentinels); //初始化过程做了很多工作 Jedis jedis = pool.getResource(); jedis.set("key1", "value1"); pool.close();}
党纲绝地党为哨兵提供了良好的支持。如上面的代码所示,我们只需要向Jedis提供哨兵和masterName节点的集合,就可以构造一个JedisSentinelPool对象。
然后您可以像常规 Redis 连接池一样使用它:通过 pool.getResource() 获取连接并执行某些命令。
整个过程中,我们的代码不需要显式指定主节点地址来连接主节点;代码中没有自动故障转移实现,因此可以在哨兵完成故障转移后自动故障转移到master。节点。
之所以可以做到这一点是因为 JedisSentinelPool 构造函数中已经完成了正确的工作;主要包括以下两点:
哨兵节点遍历及masternode信息获取: 哨兵节点遍历,通过哨兵 + masterName节点之一获取masternode信息。
该功能是通过调用哨兵节点sentinelget-master-addr-by-name命令来执行的。命令示例如下:
一旦获取到主节点信息,就停止遍历(所以一般遍历到第一个哨兵节点就停止循环)。
添加对哨兵的控制: 这样客户端可以在发生切换时收到哨兵的通知并完成主节点切换。
特殊的方法是:利用Redis提供的发布订阅功能,为每个哨兵节点开启一个单独的线程,订阅哨兵节点的channel + switch-master,收到后重新初始化连接池一个消息。 总结
介绍党的原则可以让我们加深对哨兵职能的理解。
配置提供者:客户端可以通过哨兵 + masterName节点获取主节点的信息,其中哨兵扮演配置提供者的角色。
要知道哨兵只是一个配置提供者,而不是调解者。两者的区别在于:
- 如果是配置提供者,当客户端通过哨兵获取masternode信息时,会直接与masternode建立连接,后续请求(如set/get)将直接发送。到主节点。
- 如果是中介,每个客户端请求都会发送给哨兵,哨兵会通过主节点处理该请求。
举个例子,以更好地理解哨兵作为配置提供者而不是代理人的角色。在之前安装的哨兵系统中,修改哨兵节点的配置文件如下:
sentinel monitor mymaster 192.168.92.128 6379 2改为sentinel monitor mymaster 127.0.0.1 6379 2
然后在局域网内的另一台电脑上运行前述客户端代码,会发现客户端无法连接到主节点。
这是因为哨兵作为配置提供者,客户端通过它查询主节点地址127.0.0.1:6379。客户端会与127.0.0.1:6379建立Redis连接,当然无法连接。如果哨兵是特工,这个问题就不会存在。
注意:自动切换完成后,哨兵节点会将新主节点的信息发送给客户端,以便客户端及时更换主节点。 哨兵实现的基本原理
前面介绍了哨兵安装和使用的基本方法。本节介绍哨兵实施的基本原则。 哨兵节点支持的命令
哨兵节点是运行在特殊模式下的Redis节点,其支持的命令与普通Redis节点不同。
在运维中,我们可以使用这些命令来查询或更改哨兵系统;更重要的是,如果哨兵节点之间没有通信,哨兵系统就无法实现故障检测和故障转移等各种功能。
大部分通信是通过哨兵节点支持的命令完成的。 哨兵节点支持的主要命令如下所示。
基本查询
这些命令可用于查询拓扑、节点信息、配置信息等。系统哨兵:
- 信息监督者: 获取所有监控主节点的基本信息。
- 主监管者: 获取所有受监管主节点的详细信息。
- sentinel master mymaster: 获取有关 Sentinel 主节点 mymaster 的详细信息。
- sentinel Slaves mymaster: 获取监控的主节点mymaster的从节点详细信息。
- sentinel哨兵mymaster:获取哨兵控制的主节点mymaster的节点详细信息。
- sentinel get-master-addr-by-name mymaster: 获取前面介绍的 Sentinel 主节点 mymaster 的地址信息。
- sentinel is-master-down-by-addr: 哨兵节点可以使用此命令询问主节点是否离线,以评估其是否客观离线。
添加/删除master节点监控
sentinel监控 mymaster2 192.168.92.128 16379 2:与部署哨兵节点时配置文件中的sentinel监控功能完全相同,不再详细介绍。
sentinel remove mymaster2:取消监控当前的哨兵主节点mymaster2。
强制故障转移
哨兵故障转移 mymaster:即使当前主节点运行完好,此命令也可以强制自动故障转移到 mymaster。
例如,如果当前主节点所在的设备即将被废弃,可以通过auto-failover命令提前进行自动故障转移。 基本原则
对于哨兵的原则,理解以下概念至关重要。
计划任务
每个 哨兵 节点维护 3 个计划任务。定时任务的功能如下:
- 通过向主从节点发送info命令获取最新的主从结构。
- 通过发布和订阅功能获取有关其他哨兵中心的信息。
- 通过 ping 其他节点来执行心跳检测,看看它们是否离线。
主观下线
在定时心跳检测任务中,如果其他节点在一定时间内没有响应,则哨兵节点主观下线。
顾名思义,主观离线是指哨兵节点做出“主观”的离线判断;主观离线的反面是客观离线。
离线目标
当哨兵节点主观关闭某个Master节点时,它会通过sentinelis-master-down-by-addr命令向其他哨兵节点询问该Master节点的状态。
如果主节点判断下线的哨兵s数量达到一定值,主节点就会客观下线。
需要注意的是,离线镜头是主节点独有的概念;如果从节点和哨兵节点出现故障且哨兵主观下线,则不会进行后续的客观下线操作和自动故障转移。
选择哨兵主节点
当判断主节点客观离线时,各个哨兵节点会协商选择哨兵主节点,主节点对其进行切换操作。
所有监控该主节点的哨兵s都可以被选举为leader,选举使用的算法是Raft算法。
Raft算法的基本思想是先来先服务:即在一轮选举中,哨兵A向B发送领导申请。如果B不同意其他哨兵的意见,他将同意让A担任领导人。
具体的选举过程这里就不详细介绍了。总的来说,哨兵的选拔过程非常快。谁先完成线下目标,一般就会成为领导者。
故障转移
当选的领导者哨兵发起故障转移操作,大致可以分为3步:
- 从从节点中选择新的主节点: 选择原则 是的,首先过滤不健康的从节点,则选择优先级最高的从节点(由优先级slave指定)。如果无法区分优先级,则选择复制偏移量最大的子节点;如果仍然无法区分,则选择runid最小的子节点。
- 更新主从状态: 使用slaveof no one命令将选定的从节点更改为主节点;并使用slaveof命令使其他节点成为从节点。
- 将离线的主节点(即6379)设置为新主节点的子节点。 当6379重新上线后,它将成为新主节点的子节点。
使用上面的关键概念,你就可以基本了解哨兵的行事方式了。为了更清楚的说明,下图显示了领先的哨兵节点的日志,包括从节点启动到自动切换完成的时间。
哨兵配置及实用建议哨兵配置
下面介绍与哨兵相关的几种配置。
主控{masterName} {masterIp} {masterPort}{quorum}
主控是哨兵的主要配置。之前的一篇文章讨论了哨兵枢纽的部署。其中:masterName指定主节点的名称,masterIp和masterPort指定主节点的地址,quorum是决定主节点是否客观下线的哨兵s阈值数量。
当判定主节点下线的哨兵数量达到法定人数时,主节点将客观下线。建议值为哨兵s数量的一半加1。
sentinel down-after-milliseconds {masterName} {time}
sentinel down-after-milliseconds与主观下线判断有关:哨兵使用ping命令执行其他节点上的心跳检测。
如果其他节点在down-after-milliseconds配置的时间后没有响应,哨兵会主观地切断它们。该配置对主节点、从节点和离线哨兵节点的主观判断有效。
down-after-milliseconds默认值为30000,即30s;可根据不同的网络环境和应用需求进行定制。
的值越大,主观离线判断就越宽松。优点是误判的可能性小。缺点是错误检测和故障转移的时间会增加,客户端的等待时间也会增加。
例如,如果应用程序的可用性要求较高,可以相应减小该值,以便在发生错误时尽快完成下载;如果网络环境比较差,可以相应提高阈值,避免频繁误判。
sentinel parallel-syncs {masterName} {number}
sentinel parallel-syncs 与自动故障转移后的从复制相关:它指定每次向新的 master 发起复制操作的从节点数量。
例如,假设主节点切换完成后,有3个从节点想要开始复制到新的主节点;如果parallel-syncs=1,子节点将开始逐个镜像;如果parallel-syncs=3,则3个子节点将开始一起复制。
并行同步值越大,从节点完成复制的速度越快,但对主节点的网络负载和硬盘负载的压力也越大;必须根据实际情况来设置。
例如,如果主节点负载较轻,从节点对服务可用性要求较高,则可以相应增大并行同步值。并行同步默认值为1。
自动切换超时时间{masterName} {time}
自动切换超时时间与自动切换超时判断有关,但该参数不用于时限判断不是自动切换的整个阶段,而是其几个子阶段的时间限制。 。
例如主节点晋升为从节点的时间超过时限,或者从节点开始向新主节点进行复制操作(没有时间复制数据)的时间超过时限,会导致自动切换限制失效。
默认切换超时值为180000,即180s;如果时间到期,下次该值将是原来值的两倍。
除了上述参数之外,还有一些其他的参数,比如安全验证相关的参数,这里就不介绍了。实用建议
哨兵节点数量必须大于1。一方面增加了哨兵节点的冗余度,避免哨兵本人成为高可用瓶颈;另一方面减少线下误判。而且,这些不同的哨兵节点应该安装在不同的物理机上。
哨兵节点的数量必须是奇数,以便哨兵更容易通过投票做出“决定”:领导人选举的决定、线下客观决策的决定等。
各个哨兵节点的配置必须一致,包括硬件、参数等;此外,所有节点必须使用ntp或类似服务来确保时间准确一致。
哨兵 的配置提供程序和通知客户端功能需要客户端支持实现,例如前面提到的 Jedis;如果开发人员使用的库没有提供足够的支持,开发人员可能必须自己实现。
当哨兵系统节点安装在Docker(或其他可以进行端口映射的软件)中时,需要特别注意端口映射可能会导致哨兵系统出现故障。
因为哨兵的工作依赖于与其他节点的通信,而Docker的端口映射可能会导致哨兵无法连接其他节点。
例如,哨兵依靠他们向外界发布的IP和端口来发现对方。如果 Docker 中安装了某个特定的 哨兵 A,并进行了端口映射,则其他 哨兵 无法通过 A 上报的端口连接到 A。 总结
本文首先介绍 哨兵 的作用: 监控、交换、配置和通知提供者;然后讲述如何安装哨兵系统以及如何通过客户端访问哨兵系统;然后简要说明了哨兵实施的基本原则;最后对哨兵的做法提出一些建议。
在主从复制的基础上,哨兵引入了自动主节点故障转移,进一步提高了Redis的高可用性。
但是哨兵的错误也很明显:哨兵无法进行从节点自动切换。在读写解耦的场景下,一个从节点的故障会导致读服务不可用,需要我们对从节点进行额外的监控和故障转移操作。 。
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。