技术大佬问我,中间件是如何做到高可用的(上)

284 阅读9分钟

当我们在讨论软件高可用的时候,我们到底是在说什么?

应用具备高可用? 中间件具备高可用? 应用上层的nginx具备高可用? DNS 具备高可用?

连身为一线互联网大厂的阿里都宕机了,所谓的高可用,其实是不可用?

对于高可用,网上有这样两种声音:一种是无用论,一种是神话论

无用论的观点。比如:一线互联网大厂阿里语雀 都宕机8个小时以上了,高可用的3个9都达不到,但还看到他们 到处宣讲自己系统有多么的高可用,所谓的高可用,其实是不可用。

还有一种 神话论的观点。比如:高可用,是解决系统可用性的高级手段和唯一法宝;过分夸大高可用的作用;将其视为解决软件可用性的的万能药;是解决所有可用性问题的唯一因素。

个人观点

阿里语雀宕机的个别事件,并不能代表软件高可用思想是无用的。这种说法,多少有点以偏概全的谬误。

软件高可用是软件三高的特征之一,因其实现的技术较复杂;大学里开设的专业课程又很少会单独讲到这块;另外在实际的业务研发中,又很少的接触和考虑到它,所以在大众面前,显的很神秘;而这份神秘感,在外界不断的强化对软件高标准的追求时,导致在研发人面前,显的很神话(科班不教,工作中又很少接触,涉及到的技术又很晦涩难懂,又是后端技术人在技术层面追求的目标之一 )。并且这种神话还有可能会掩盖软件领域其它技术问题,比如应用的性能,安全性,应用性等。

系统高可用不是万能的,系统高可用是有一定的前提条件的。这些前提条件包括,系统运行所依赖的硬性条件,软件环境,还有人为因素

硬性条件 比如:电力,网络;所在机房在物理地址上的稳定性。地球都毁灭了,还在说系统具备高可用,那就是在扯淡了;除非系统是垮星球部署。一般软件的高可用方案不能解决这些问题。

软件条件 :指系统运行依赖的底层软件,比如操作系统,或者底层驱动,或者三方依赖,又或者部分服务器的磁盘,网络 物理上的损害。软件层面和部分物理机的硬件故障,导致的系统不可用,是一般软件高可用方案擅长解决的问题领域。

人为因素 :删库跑路;人为写的隐藏bug 导致全系统宕机;APP首页打不开等。这些人为因素和系统性bug,导致的全局软件不可用;也不是一般软件高可用方案擅长解决的领域;要防止这些问题的发生,可能更需要借用软件领域的其它“银弹”。比如 code review;服务的降级,限流;线上操作权限限制和规范化管理等。

高可用的衡量指标有哪些呢?

一般用可用性指标来进行衡量,即系统全年 提供服务的时间/一年的时间。比如一年365天,系统正常运行了364天,可用性为 99.72%;
假设系统要达到 3个9的可用性,即99.9%,一年能宕机(不可提供服务)多久了? 8.76个小时
假设系统要达到 4个9的可用性,即99.99%,一年能宕机(不可提供服务)多久了?0.876个小时,52.56分钟

而一般我们说系统要至少达到 4个9才具备高可用。 即一年只能宕机 52.56分钟。太难了,特别是系统还在不断的发展和演进中,还要想达到此目标,那更是难上加难。

中间件高可用的初级玩法是怎样的了?

image.png

冗余: 高可用的基石

软件层面冗余:比如服务冗余,数据冗余;

像redis 有主从节点。从节点是对主节点服务和数据冗余。

kafka的topic 有分区的概念;分区上有主从之分,从分区是对主分区数据的冗余。kafka数据冗余的机制和流程请看《技术大佬问我,kafka是如何做到数据的高可靠的(上)》《技术大佬问我,kafka是如何做到数据的高可靠的(下)》

硬件层面也有冗余:比如电力冗余,网络冗余等;在机房没电的时候,备用的UPS就该上场了。

故障检测

故障检测,是主动发现集群中问题节点的关键。我们都希望集群中的服务节点是健康的,如果有不健康,不能对外提供服务怎么办?最理想的状态,是无缝的用前面提到的 正常的冗余节点 替换那些 不健康节点,以此保证系统具备最高可用性。
而如何发现集群中,某个节点不健康了,一般通过两种方式:

写作 (32).png

像,常用中间件redis,是通过哨兵节点,以一定的检测频率,不断的向其他master和slave节点,发送探活请求,来进行存活检测;此种方式属于被动接受健康检查

kafka 主要是借用三方中间件zk来实现故障检测(最新的版本可以kafka自带选KRAFT机制): kafka broker节点,也是以一定的频率,主动向zk发送心跳请求;kafka 认为: 如果broker不能向zk 发送心跳,那么在zk上注册的临时节点,就会被删除;以此触发对应的watch事件,然后被kafka controller 节点监听到该事件,感知到整个集群中,那些节点是非存活。

故障转移----选主

选主核心思想:候选节点的数据,尽量和主节点保持一致。

kafka的选主思想: kafka的选主,是由controller节点负责的。controller会从ISR分区列表里,顺序选择下一个从分区做为该分区的主分区。(该选主算法非常的简单,之所以这么的简单,是因为 kafka 规定,分区如果是在ISR分区集合里,就是和主分区保持同步的,没有和主分区保持同步,会被踢出ISR分区列表,即我们说的非ISR分区)

redis的选主过程和思想较为的灵活和复杂了:
1) 在进行选主之前,redis需要从众多的哨兵节点中,选举一个哨兵出来,进行选主操作
2) 确定好了选主哨兵后,由该哨兵进行后续的选主工作
3) 哨兵会排除待选节点超过断开连接一定时长的节点(这个能理解,毕竟超过一定时长,该节点大概率会落后主节点数据太多)
4)哨兵会对从节点上配置的优先级进行排序,优先级越高,被选中的权利越大(有点 人为干预选举过程了,比如机器配置好的,优先级配置的高一点,被选中的概率也高一些)
5)优先级相同的情况下,会比较replica offset,replica offset越大的,就越接近主节点的数据,会优先被选择为新主
6) 如果上面的条件都满足的情况下,选择 run id比较小的slave做为新主(较小的"run id"通常表示节点是最近启动的,因此它可能拥有较新的数据,而较大的"run id"则可能表示节点已经运行了较长时间,可能存在较旧的数据。)

通知外部切换 --------外部无缝切换

选取新主后,redis外部切换
redis的外部,主要有两部分,一部分是redis客户端,一部分是其它的slave。
其它的slave感知到新主,可通过redis的pub/sub机制,通知给其它从节点进行新主的切换。
而redis客户端需要感知到新主,不同的redis客户端,底层机制可能不太一样。比如Lettuce、Jedis客户端 定期向Redis服务器发送PING命令以检查连接的健康状态。如果客户端的连接不再有效(比如连接的是原主节点,但该节点已经不再是主节点),客户端库会尝试重新连接到集群,并根据最新的集群拓扑结构来更新连接信息。

如果客户端使用了Redis Sentinel作为监控和管理工具,那么Sentinel客户端会持续监听Redis主节点切换事件。一旦发生主节点切换,Sentinel客户端会收到通知,并可以更新客户端的连接信息以连接到新的主节点。

选取新主后,kafka客户端外部切换:

kafka客户端感知到新主分区变化,主要是kafka 客户端通过和broker集群进行通信时感知到的。

比如kafka客户端的IO线程,会以固定的频率,向集群发送Metadata请求以获取当前的集群元数据,包括主题(topics)、分区(partitions)以及每个分区的leader副本等信息。当主分区发生切换时,集群的元数据会更新,客户端下次发送Metadata请求时会收到更新后的元数据,从而感知到主分区的变化,以此切换底层的链接信息。

总结

  1. 高可用并不是解决软件可用性问题的唯一方案,无需贬低高可用,也无需一味的赞扬高可用。一般软件的高可用,有自己解决的问题领域,这些领域包括,底层操作系统出现故障时,集群中某些节点不能提供服务时,集群中某些物理机的硬件不能正常工作时

  2. 高可用的度量指标,一般用系统在一年时间内,能正常工作的时间比例来进行计算;3个9也是灰常的厉害了。

  3. 软件高可用的一般玩法。冗余,故障检测,故障转移,外部通知和切换。

原创不易,请 点赞,留言,关注,收藏 4暴击^^