• 架构设计系列5:如何设计高可用架构


    #1024程序员节|参与投稿,赢限定勋章和专属大奖#

    概要

    当今的数字时代,高可用架构已经成为了现代应用和服务的基石。无论是企业级应用、云计算平台还是互联网服务,高可用性都是确保系统在面临各种挑战时保持稳定运行的关键要素。

    本文将探讨什么是高可用架构,为什么它如此重要,以及如何提升系统的可用性。
    在这里插入图片描述

    一、什么是高可用架构

    高可用架构是一种设计和架构方法,它旨在确保系统在面对各种故障、异常、硬件或软件问题,以及不可预测的事件下,继续提供稳定的服务。简单来说,高可用就是让我们的系统在任何情况下都尽最大可能能够对外提供服务。

    高可用架构追求提供连续的服务,不受单点故障、服务中断或其他问题的影响。这通常涉及到复制、冗余、负载均衡和自动故障转移等技术手段,以保持系统的稳定性和可用性。


    二、为何高可用架构如此重要

    高可用架构之所以如此重要,有以下几个关键原因:

    1. 用户满意度:用户期望系统7*24可用,如果系统不可用,可能会损害用户体验,并导致用户流失。
    2. 业务连续性:如果系统不可用,将无法为用户提供连续的服务,可能给用户和公司造成业务损失。

    由此可见,高可用架构的目标就是最大限度地减少系统停机时间,并确保用户能够无间断地访问和使用系统。

    那么,一般有哪些因素会影响系统的稳定性呢?

    1. 人为因素:如误操作、配置错误、不合理的变更等
    2. 软件因素:如代码bug、设计漏洞、GC问题、线程池异常、上下游异常等
    3. 硬件因素:如网络故障、存储故障、机器故障等
    4. 安全因素:如安全漏洞、未授权的访问、恶意攻击等
    5. 负载因素:如突发的流量激增、恶意攻击、系统资源不足等

    三、如何提升系统的可用性

    面对诸多影响系统稳定的因素,我们应当如何去保障系统是高可用的呢?

    通常为了保障系统是高可用的,我们需要有一套比较科学的管理方法,要从产品、设计、开发、测试、运维等全方位去考量和设计。

    接下来,我们一起来探讨下具体可以从哪些方面着手提升系统的可用性。

    1. 研发规范层面

    1.1 方案设计和编码规范

    维度核心内容
    设计阶段- 做好规范,采用统一的技术方案模板
    - 做好评审,技术方案一定要进行评审
    编码阶段- 做好规范,采用统一的代码规范、日志规范、单元测试规范
    - 做好评审,核心代码必须做代码Review
    测试阶段- 做好规范,坚守测试准入准出规范、编写测试用例
    - 做好评审,坚持做测试用例评审
    发布阶段- 自动化:GitLab+Jenkins+K8s黄金组合,实现自动化部署,提高交付效率
    - 可灰度:用小比例的一部分流量来验证变更后的内容,减小影响用户群
    - 可回滚:出现问题后,能有有效的回滚机制。
    运维阶段- 可观测:观察变更前后的指标变化,结合告警机制,提前发现问题

    1.2 容量规划和评估

    维度核心内容
    容量评估- 新系统,根据产品和运营对业务体量的初步预估进行评估
    - 旧系统,根据业务场景的历史数据进行评估
    容量规划- 在系统设计之初,就要初步规划好系统大致能抗多少的量级,比如,10万QPS。
    - 特别注意,不同量级对应的系统架构设计完全不一样,需要根据业务实际情况来设计。
    性能压测- 通过性能压测,来确保你的容量规划是准确的。条件允许的话,最好是做全链路压测。
    - 性能压测重点要关注三个指标:QPS、平均RT、TP分位值

    1.3 小结

    系统都是研发人员设计和编码写出来的,因此一个好的研发规范和标准,可以让我们更好的去研发和维护一个高可用的系统。

    2. 应用服务层面

    2.1 无状态设计和负载均衡设计

    维度核心内容
    无状态- 无状态,意味着服务间不需要同步数据,其特点是,使服务更容易做水平扩展和负载均衡。
    负载均衡- 负载均衡的目的是,将任务通过某种策略分配到多个实例上,实现不同实例间的负载平衡。
    - 对于单体架构,一般通过LVS、SLB、Nginx来实现负载均衡。
    - 对于微服务架构,通常微服务框架中提供了服务注册和发现、负载均衡的能力。

    无状态+负载均衡,既可以提高系统的可用性,也可以提高系统的并发能力。

    2.2 弹性伸缩和资源隔离设计

    维度核心内容
    弹性扩缩容设计- 基于容器化(K8s)部署,可非常容易的实现弹性扩缩容。
    N+1设计- 每个组件都应做到没有单点故障,永远不要少于两个,通常为奇数个。
    隔离设计- 避免单一业务占用全部资源,避免业务之间的相互影响。

    2.3 异步解耦和流量削峰设计

    维度核心内容
    异步解耦- 采用异步设计,把同步的流程转换为异步的流程,如异步线程、消息队列等。
    流量削峰- 采用消息队列,当并发较高时,起到削峰的作用。

    2.4 故障和容错设计

    维度核心内容
    开关设计- 确保发布的任何功能,在出现故障时,可通过开关快速禁用,如重构。
    接口设计- 接口层面的超时设置、重试策略、防重设计和幂等设计。
    兜底设计- 产品策略上进行兜底设计。
    - 如:抽奖活动,默认一个兜底商品
    - 如:页面无法访问时,默认展示默认页面
    回滚设计- 确保系统可以向后兼容,如果应用服务上线后出现bug,可以紧急回滚。
    灰度发布- 支持按标签维度进行小流量验证,观察系统日志和业务指标,等运行平稳后再推全量。

    2.5 过载保护设计

    维度核心内容
    超时- 通过设置超时时间,避免因为超时阻塞,导致系统出现雪崩。
    限流- 通过对一个时间窗口内的请求进行限速来保护系统,一旦达到限制速率则可以拒绝服务、排队或等待、降级等处理。
    降级- 保证核心服务,牺牲非核心服务,必要时可进行熔断。
    熔断- 熔断是一种过载自我保护机制,类似现实世界中的‘保险丝’。熔断属于降级的一种方式。

    2.6 小结

    在当前的互联网时代,应用服务基本都是无状态的,因此要实现应用服务的高可用相对会比较简单。

    3. 数据存储层面

    接下来,我们一起来探讨下,具体我们要如何保障数据存储的高可用。

    第一,数据存储高可用本质上如何实现的?

    数据存储高可用本质上是通过数据冗余的方式来实现的。

    简单来说,就是通过将数据复制到多个存储介质里面,来有效的避免数据丢失,同时还可以提高并发能力。

    第二,要保障数据存储的高可用,通常会面临哪些问题?

    1. 数据如何复制?
    2. 各个节点的职责是什么?
    3. 如何应对复制延迟?
    4. 如何应对复制中断?

    第三,常见的解决存储高可用的方案有哪些?

    常见的解决存储高可用的方案有两种:集群存储和分布式存储。业界大多是围绕这些来构建,或者是做相关衍生和扩展。

    3.1 集群存储(集中式存储)

    集中式存储,就是在不同的机器上存放完整的相同数据,把多台机器组合成一个集群,对外提供统一的服务。

    通常我们说的集中式存储,指的就是 一主多备 或者 一主多从 的架构,写数据通过主机,读数据一般通过从机

    架构模式

    架构模式特点
    一主多备- 主备复制,是最常见且最简单的一种存储高可用方案。
    - 特点:主备架构中,主节点支持读写操作,备节点仅起备份作用
    一主多从- 主从复制,同样是最常见且最简单的一种存储高可用方案。
    - 特点:主从架构中,主节点支持读写操作,从节点仅支持读操作,不支持写操作
    主从切换- 在一主多备和一主多从的架构模式下,当主机异常后,通过自动切换机制,将备机或者从机切换为主机

    适合场景
    集群存储,适合数据量规模一般的场景,一般中大型互联网公司,默认都是集群存储的方式。比如 Redis、MySQL 等存储类型。

    3.2 分布式存储

    分布式存储,是指将不同的业务数据,分开存储在不同的地方,分布式中的每一个节点,都可以做集群。

    架构模式

    架构模式特点
    分布式存储- 按照一定的分配算法,将数据分配到不同的节点上进行存储
    - 特点:分布式存储中的节点,往往都采用了集群存储,来保证高可用,并且每个节点都可以处理读写操作

    个人理解

    • Redis 集群模式,按照slot区间进行路由,一定程度上也是分布式存储的体现。
    • MySQL 分库分表,按业务因子进行路由,一定程度上也是分布式存储的体现。

    适合场景

    分布式存储,适合数据量规模巨大的场景,常见的分布式存储比如 Hadoop(HDFS)、HBase、ES 等。

    3.3 小结

    由于数据是有状态的,因此要实现数据存储的高可用,相对应用服务来说,会复杂很多。

    4. 系统运维层面

    4.1 开发阶段

    于系统运维而言,在开发阶段如何提高系统的可用性呢?通常我们可以对如下几个方面进行考量和设计。

    维度核心内容
    安全设计- 目的:避免被外部恶意攻击而造成系统故障(防刷、防黑产、防黑客等)
    - 常见策略
           - 白名单:IP白名单限制
           - 网关:参数签名验签、登录token验证
           - 员工外网访问公司内网:通过VPN访问各种内部系统
           - 内部系统登录安全控制:MFA(多因素认证)
           - 数据层面:敏感字段加密存储
           - 安全产品:高防DDOS,WAF
    实时监控- 目的:对系统的关键指标,进行实时监控和报警,快速感知系统异常
    - 监控指标(Metrics),告诉我们有没有故障。
           - 系统层:CPU、内存、I/O 、网络等信息,一般由运维人员关注
           - 应用层:调用量、接口出错率、响应时间等,一般由开发人员关注
           - 业务层:点击率、转化率、下单率等,一般由产品与运营人员关注
    - 告警要点(Alarm)
           - 实时性:实现秒级监控;
           - 全面性:覆盖所有系统业务;
           - 实用性:预警分为多个级别,监控人员可以方便实用地根据预警严重程度做出精确的决策;
           - 多样性:预警方式提供推拉模式,包括短信,邮件,可视化界面,方便监控人员及时发现问题
    日志管理- 目的:集中管理和分析系统的日志,以便故障排查和性能优化。
    - 日志分析(Log),告诉我们故障的原因。
    性能分析- 目的:深入了解系统的瓶颈和性能问题,并进行优化。
    - 链路追踪(Traces),告诉我们故障在哪里。

    4.2 部署阶段

    于系统运维而言,在部署阶段,我们通常通过容灾设计来提高系统的可用性。

    容灾系统通常分为:数据容灾应用容灾。备份系统通常分为:同城备份异地备份

    维度核心内容
    单机房部署- 针对一个机房内,通过集群或副本的方式,来保障应用或数据的高可用。
    多机房部署- 同样利用集群和副本,来实现机房级别的高可用,任何一个机房的故障,都不影响整体可用性。
    - 由于应用是无状态的,因此应用的多机房部署,比较容易。只需实现服务的注册和发现即可。
    - 由于数据是有状态的,因此数据的多机房部署,比较难搞。其涉及到数据的同步和复制问题。
    - 常见方案:同城双活,两地三中心,异地多活

    一般的业务系统,如果规模不大,通常单机房部署就可以了。当业务发展到一定规模,在条件允许的情况下,可以考虑多机房部署,甚至多区域部署。

    容灾和备份有何区别?

    • 容灾:核心目标是保证业务连续性。 举例:当A发生故障后,业务自动切换到B。
    • 备份:核心目标是保证数据的完整性、安全性。 举例:B仅作为A的备份数据,不对外提供服务,当A故障后,需要大量的人工干预和操作,花费大量的时间才能让B承载业务

    4.3 线上运行阶段

    于系统运维而言,在线上运行阶段,我们通常可以通过如下方式来保障系统的可用性。

    维度核心内容
    故障演练- 对系统进行一些破坏性手段,观察局部故障是否会引起可用性问题。
    - 简单的故障演练,就是模拟机房断电、断网、服务挂掉等场景,然后看我们的整个系统运行是否正常。
    人工巡检- 对线上的服务、中间件进行定期巡检,发现并排除可能的系统风险
    接口拨测- 接口拨测,和巡检类似,就是服务上线后,每隔一个固定时间(比如 5s)调用后端的各种接口,如果接口异常则进行告警。
    - 一般可以结合自动化测试平台进行实现,如果没有,则可以自己写一个接口拨测的服务,定期去调用重要的接口。

    4.4 小结

    提高系统运维效率的关键在于可观测,因此构建可观测性系统,对保障系统的稳定性至关重要。

    5. 故障处理层面

    前面做了这么多保障,但是终究架不住线上的各种异常情况,如果真出现异常,那么该如何将损失降低到最小呢?

    在实际的工作中,发现故障或收到故障告警后,往往存在响应不及时,或不知道如何响应的情况。

    为了提升团队对故障的处理能力,通常我们会制定一个标准化的故障处理流程,使得大家能够及时、快速、有序地对故障进行响应。

    故障处理流程的目标是,将系统中的服务或组件责任到人,当出现故障后,各司其职,快速分析并解决问题,将损失降至最低,避免出现问题后,手忙脚乱导致事态扩大。

    5.1 故障处理流程

    具体见:故障处理流程规范(新)

    5.2 小结

    需特别注意,故障处理流程,越简单越易落地,千万不要搞复杂,否则难以落地。

    四、最后

    对于一个持续迭代、不断演进的系统而言,我们没有办法将故障发生的概率降为0,能做的只有尽可能的预防故障的发生,以及当故障发生后缩短故障的恢复时间。

    当然我们也不用一味的追求可用性,毕竟提升稳定性的同时,维护成本、机器成本等也会跟着上涨,所以需要结合系统的业务要求选择合适的方案,适合的才是最好的。

    最后,一句话总结,高可用就是最大限度地减少系统不可用的时间。


    架构设计系列文章

    1. 架构设计系列:什么是架构设计
    2. 架构设计系列:几个常用的架构设计原则
    3. 架构设计系列:高并发系统的设计目标
    4. 架构设计系列:如何设计可扩展架构
    5. 架构设计系列:如何设计高性能架构
    6. 架构设计系列:如何设计高可用架构
    7. 架构设计系列:如何应对软件变化
    8. 架构设计系列:常用设计模式的实践

  • 相关阅读:
    编程笔记 Golang基础 018 常量与变量
    【C语言进阶】简易通讯录的实现
    如何理解联合关系抽取模型GPLinker?
    arcgis 计算某点到其他城市的距离,含要素转点(以北京市到各个地级市的距离为例)
    机器学习VS深度学习
    ESP32学习笔记(50)——搭建环境、编译烧写(Windows+Espressif-IDE)
    软件开发方法
    国考省考行测:问题型材料主旨分析,有问题有对策,主旨是对策,有问题无对策,要合理引申对策
    智能家居现状分析及未来展望
    鲲鹏开发者峰会2022丨冲量携手鲲鹏,共赢数字新时代
  • 原文地址:https://blog.csdn.net/icansoicrazy/article/details/133977354