
免费赠送
更多内容关注微信公众号:系统工程实验室
“ 简单来说,高可用是系统提供无故障服务的能力。有太多资料探讨如何构建高可用系统,但多是一些工程实践知识点的堆叠。这篇文章我们从更朴素的视角去透视分布式系统的高可用建设”
01
—
高可用
高可用性(High Availability,HA)的定义是指系统、组件或服务在长时间内能够持续正常运行并提供服务的能力。简单来说就是系统提供无故障服务的能力。业界衡量系统可用性的标准方式是通过“时间维度”,即判断宕机时间,并以此计算出每年系统可用时间达到“几个九”,来判断高可用架构是否健壮。
系统可用性等级越高,每年的系统宕机时间要求越小。以四个九为例,全年系统宕机时间要求不超过52.56分钟。
02
—
高可用的本质
分布式系统的设计本质是在承认"熵增定律"的前提下,通过结构化冗余创造局部熵减。所有技术手段最终服务于一个目标:让系统具备生物体般的自组织能力——既能在稳定态时高效运行,又能在扰动后自主恢复秩序,并随着环境变化持续进化适应性。
任何系统都不是孤立的,其必然存在于一个更广泛的环境中。除了系统自身的机能运转,其必然会于外部环境进行交互,并影响周边环境,或被周边环境影响。因此,系统的稳定运行受到系统自身以及环境的影响。
* 系统整体性:从哲学角度看,一个系统不仅仅是其组成部分的简单集合,而是一个有机整体。高可用性需要从整体性出发,理解各个组件之间的动态关系和相互依赖。
* 有机系统:类似于生态系统,分布式系统需要适应环境变化,具有自我调节和自我修复的能力。在局部不可用时能够快速识别、恢复、隔离保证系统的整体可用。
当我们谈论系统 “高可用” 时,隐式的对系统必然存在不可用的假设。导致系统不可用的客观原因很多,比如硬件故障、网络异常、生产缺陷、突增流量、网络攻击等等,那根因是什么呢?
不确定性-系统的不可用是由于其 “不确定性” 导致。不确定性是世界的基本特征,无论是硬件故障、软件缺陷,还是人为错误,这些都是基于我们无法完全管控系统。分布式系统设计的基石是首先要接受这种观念不确定性。由于不确定性无法消除,我们需要通过某种形式的管控来降低不确定性的发生概率以及问题发生后的快速定位和恢复。高可用性本质上是对不确定性的防御性哲学,即通过冗余、容错、监控等手段,减少不确定性对系统的影响。
03
—
拆解
基于以上的论述,我们重新思考这个问题: 如何构建高可用的分布式系统?既然导致系统不可用的根因是不确定性,且不确定性不能被消除,那么,我们要构建高可用系统需要通过可行的手段“对抗“ 这种不确定性。
冗余:冗余是对抗不确定性的朴素策略,通过增加系统的冗余度来减少单点故障的影响。冗余可以体现在硬件、软件、数据等多个层面,比如硬件冗余、软件冗余、数据冗余等等
分散:分散的本质是去集中化,将系统的功能和负载分散到多个独立的单元中,以减少集中化带来的风险。分散是隔离的前置条件。
容错:系统在出现故障时,通过检测、隔离和处理故障,能够继续正常运行或至少部分运行的能力,确保系统的持续可用性。容错机制是系统自愈能力的一种体现,通过预定的机制,在可控的场景下恢复部分或全部的系统服务能力。
弹性:设计系统具有自动适应变化的能力,使其能够在负载变化或故障发生时自动的、动态的调整系统资源,以保证系统的正常运行。弹性机制体现了系统的动态适应能力,能够随着外部环境,例如负载的变化,动态调整自身资源以适应环境变化。
隔离:通过隔离不同的功能模块和故障域,在系统内部通过边界和独立性来防止故障扩散,控制故障传播范围,降低其对系统的影响。
监控与反馈:通过持续的监控和快速反馈机制,及时发现和定位不确定性引起的系统问题。这要求系统具备一定的可观测能力,使人类能够收集并观测到系统的运行状态。
迭代演进:通过不断的实验和学习来适应不确定性,逐步改进系统的设计。在不确定性引发系统不可用之前主动,通过主动触发并识别潜在风险,主动的调整系统以提升系统可用性。
04
—
工程实践的一些思考
设计到构建高可用分布式系统的工程实践非常之多,本文不对硬件层面进行探讨,主要聚焦的软件层面进行分析,整体上划分为三类:
提前预防:降低由于不确定性导致的问题发生概率
快速恢复:快速发现、定位并恢复
持续治理:通过持续性治理提高系统可用性
系统高可用建设涉及到的工程实践非常多,甚至是每一项工程实践都值得通过一篇文章来展开讨论。以下只是“抛砖引玉”
关于分散
分散的本质是在去中心化,避免集中式模式下局部故障导致的系统整体不可用。通过将不同的组件、功能或服务分散在不同的节点或服务器上,以提高系统的可靠性、可扩展性和容错能力。
负载分散
避免将负载集中至一个节点,通过负载均衡机制将其分不至多个节点之上,从而避免单个节点超过负载阈值导致系统不可用。
数据分散
避免数据的单一存储,通过副本方式将数据分散存储在多个节点,提高数据可用性和降低数据丢失风险。
服务分散
通过微服务架构划分不同的服务边界,尽量降低不同服务单元间的依赖,并将不同服务分散在不同的服务器或容器中,降低服务之间的相互影响。
分散一般与容错机制、负载均衡机制、服务拆分等机制关联,共同作用于系统的高可用建设。
关于弹性
在系统请求量突增情况下,系统依赖的服务器资源可以支持自动伸缩:自动水平伸缩还是垂直伸缩。都依赖于应用的无状态设计。并不是所有的基础设置都支持基于负载进行自动化伸缩。
关于容错
容错是指系统在某个组件发生故障时,仍然能够继续正常运行而不影响用户体验。容错主要关注的是在故障发生的瞬间,系统如何处理并继续提供服务。
限流&降级&熔断
是普遍采用的工程实践,工程落地也比较成熟。限流是对系统进行保护的一种有效的防御策略。不仅体现在入口流量的限流,比如限制某个接口的流量访问上限,或者更细化区分不同的渠道的流量限制,当流量过载则触发限流机制。同样的,当前系统对下游系统的调用在某些场景下依然需要进行主调流量的控制,比如支付场景中调用银行侧的支付接口,一般情况下会有流量限制。限制我方的主调流量有利于保护下游系统,从而间接的保护我方系统。
当发生故障时,降级是确保可用的有效手段。研发需要分析当前系统现状,梳理哪些场景可以进行降级,以及降级的策略是什么。例如:
* 非核心链路的流程直接关闭
* 前端页面降级展示
* 读数据库切换为直接读取临近缓存数据
重试
重试与补偿不同,重试是在请求链路内基于某些特定的异常情况而采取的重新处理的策略,涉及到如下几个方面:
1. 什么场景需要重试:幂等性是重试的前提,不具备幂等性这一前提则系统不允许进行重试。并不是所有的请求异常都需要且可以重试,例如典型的瞬时的网络抖动导致偶发超时可以允许重试,又或者下游接口返回明确的异常码判断是否需要重试。
2. 重试策略是什么:重试可以采取固定时间周期、指数回退、随机时间等重试策略。大多数情况下失败之后立即进行重试依然失败的概率较大,因此,一般会采用指数回退算法进行间隔性重试
隔离
确保系统的某一部分发生故障时,不会影响到其他部分的正常运行。通过有效的隔离策略,系统可以限制故障的传播,保持整体的稳定性和可用性。在不同的维度有不同的工程实践:
系统级的隔离
系统拆分为不同的子系统并独立部署
容器隔离
服务单独部署,采用独立的物理或容器集群,比如在中台化的系统中,对特殊的渠道用户采用物理隔离的方式针对性的部署服务集群
基础设施隔离
不同类型的子系统、组件甚至是服务依赖不同的基础设施,避免基础设施资源瓶颈影响全部服务。比如缓存集群隔离、数据库隔离、数据库读写分离等等。
线程池隔离
不同业务单元原来的线程池进行隔离,避免单个逻辑处理打爆线程池影响其他业务可用性。比如缓存连接池、数据库连接池、应用进程内的线程池等等。
关于监控反馈
监控告警是快速发现、定位问题的重要手段,全方面建设系统的监控告警能力至关重要。通常涉及网络监控、服务器监控、进程监控、端口监控、日志监控、流量监控等,借助于开源或公司内部自研的运维平台可以较容易的进行配置落地。
工程实践中监控告警存在的典型问题有:
1. 监控告警覆盖不全
特别是大量的遗留系统,系统组成及技术栈复杂且历史悠久,系统内部缺乏大量的监控告警配置缺失。
2. 监控误报
误报与系统的工程实现和监控告警策略配置相关,例如:
* 工程实现层面:日志打印不规范,Info/Warn/Error打印区分界限模糊,很多不需要打印Error日志不加区分的打印错误日志,导致告警不准确
* 日志告警策略配置过于简单粗暴,例如“Error日志全告警”,导致告警通知量巨大、告警通知泛滥,需要处理的告警淹没在大量无效告警中,降低值班儿人员的关注度,使得生产问题不容易定位、处理。
关于持续治理
构建高可用系统不是一次性工作,需要团队进行持续建设。因为随着系统迭代以及系统外部环境变化,导致系统不可用的因素也随之变化,只有通过持续性的治理才能确保可用性的持续。基于系统不确定性假设,主动的触发系统故障,比如实践混沌工程、故障注入测试等手段,主动触发发现系统风险因素并迭代优化。
借助CICD基础设施以及严格的生产发布流程是保证系统高可用的重要手段之一,通过灰度发布、平滑上线,降低上线导致的系统不可用问题。
04
—
结语
高可用系统建设是一项系统工程,需要从多个维度去考量。高可用建设往往伴随着成本,大量的高可用建设工程实践可以作为团队的参考输入,团队需要具体情况具体分析,切忌堆叠工程实践,只有在特定的上下文下进行决策才具有实际意义。