文章

高可用系统设计核心指南

高可用系统设计核心指南

本文旨在全面介绍高可用系统设计的核心概念、原则与实践方法,内容涵盖高可用的定义、导致系统不可用的常见原因,以及提升系统可用性的多种策略,如超时重试、冗余设计、限流、熔断等。

一、什么是高可用?

高可用 (High Availability) 描述的是一个系统在绝大部分时间都处于可用状态,能够持续提供服务。一个高可用系统即使在面临硬件故障、软件升级或流量冲击时,其核心服务依然在线。

通常,我们使用 “N个9” 来衡量系统的可用性。例如,“5个9” (99.999%) 意味着系统在一年内的不可用时间不超过 5.26 分钟。

此外,可用性也可以通过 成功请求率 来衡量: 可用性 = (总请求数 - 失败请求数) / 总请求数 × 100%

二、导致系统不可用的常见原因

  • 硬件故障:如服务器宕机、磁盘损坏、网络设备故障。
  • 软件缺陷:如代码中的 Bug、内存泄漏、循环依赖等问题导致程序崩溃。
  • 流量激增:突发的高并发请求超出系统处理能力,导致服务宕机或部分功能不可用。
  • 依赖服务故障:核心依赖(如数据库、缓存、第三方API)变得不可用。
  • 安全攻击:如 DDoS 攻击导致带宽耗尽或服务器资源枯竭。
  • 运维和发布问题:错误的配置、有问题的发布版本等。
  • 自然灾害或人为破坏:如机房断电、火灾等。

三、提升系统可用性的核心策略

1. 保证代码质量与严格测试

高质量的代码是系统稳定性的基石。Code Review 是保证代码质量的有效手段。此外,可以借助以下工具提升代码质量:

  • SonarQube:静态代码分析工具,帮助发现 Bug 和安全漏洞。
  • Alibaba Arthas:Java 在线诊断工具,用于排查线上问题。
  • IDE 自带的代码分析工具:如 IntelliJ IDEA 的代码检查功能。

2. 异步调用

对于非核心、耗时较长的操作,可以采用异步处理。这不仅能削峰填谷,还能降低系统耦合度。

  • 实现方式:通过在程序中创建新线程或使用线程池,或引入消息队列(如 RabbitMQ, Kafka)。
  • 业务流程调整:使用异步后,需要调整业务流程。例如,用户提交订单后立即返回“处理中”状态,待订单处理完成后再通过短信或邮件通知用户。

3. 使用缓存

在并发量较高的场景下,使用缓存可以极大减轻数据库的压力。将热点数据存储在内存中(如 Redis, Memcached),可以实现毫秒级响应,显著提升系统性能。

4. 熔断机制

熔断机制是应对依赖服务故障的有效手段。当某个依赖服务调用失败次数达到阈值时,熔断器会“断开”,后续请求将直接失败并快速返回,避免资源被长时间占用。

  • 常用框架
    • Resilience4j:轻量级容错组件,是 Spring Cloud 官方推荐的 Hystrix替代品。
    • Alibaba Sentinel:功能强大的流量控制和熔断降级框架。

5. 灰度发布

灰度发布(又称金丝雀发布)是一种增量发布策略。它将服务器集群分成若干部分,每次只发布一小部分,并持续观察。如果出现问题,只需回滚已发布的部分,从而将风险控制在最小范围。

6. 其他策略

  • 硬件升级:为核心应用服务和配置性能更优的硬件。
  • 监控与报警:对系统资源(CPU、内存、磁盘)和应用指标(QPS、响应时间)进行实时监控,并设置报警阈值。
  • 数据备份与回滚:定期备份数据,并制定紧急回滚预案。

四、超时与重试机制

1. 超时机制

超时机制指当一个请求在指定时间内未完成时,自动取消该请求并抛出异常。这可以防止慢请求堆积,耗尽系统资源。

  • 连接超时 (ConnectTimeout):建立连接的最长等待时间。
  • 读取超时 (ReadTimeout):等待服务端处理并返回数据的最长等待时间。

如何设置超时时间? 没有银弹。超时时间需要根据业务场景和性能要求进行调整。一个普适的建议是:

  • 读取超时1500ms 左右。对于延迟敏感的系统可适当缩短,反之可适当延长。
  • 连接超时1000ms ~ 5000ms

为了灵活性,可以将超时时间配置在配置中心,实现动态调整。

2. 重试机制

重试通常与超时配合使用,用于应对瞬时或偶然性故障。

  • 重试次数:不宜过多,通常建议 3次
  • 重试间隔:建议采用递增的重试间隔(如 1s, 2s, 3s),避免在系统繁忙时继续加重其压力。
  • 幂等性:重试机制必须确保操作的幂等性,即多次执行同一次请求应产生相同的结果。例如,支付操作需要通过业务逻辑(如判断订单状态)来防止重复扣款。

五、冗余与故障转移

冗余设计是实现高可用的核心思想,即“没有备份,就没有高可用”。

  • 服务冗余:通过集群部署,将同一服务部署多份。
  • 数据冗余:通过数据备份或主从复制,将同一份数据存储多份。

冗余设计的不同级别

  • 高可用集群 (HA Cluster):在同一机房内部署多个服务实例。
  • 同城灾备:在同城不同机房部署服务,备用机房平时不处理业务流量。
  • 异地灾备:在不同城市(远距离)的机房部署服务,用于应对区域性灾难。
  • 同城多活:在同城不同机房部署服务,且所有机房都同时处理业务流量。
  • 异地多活:在异地不同机房部署服务,所有机房都对外提供服务,是最高级别的可用性保障。

故障转移 (Failover)

仅有冗余是不够的,必须配合 自动故障转移。当主节点或服务发生故障时,系统应能自动、快速地将流量切换到备用节点,整个过程无需人工干预。

  • 示例1:Redis Sentinel:哨兵模式下,Sentinel 会监控 Master 节点。当 Master 宕机时,它会自动将一个 Slave 节点提升为新的 Master。
  • 示例2:Nginx + Keepalived:Keepalived 通过虚拟 IP (VIP) 实现 Nginx 的高可用。当主 Nginx 服务器宕机时,Keepalived 会自动将 VIP 漂移到备用服务器。

六、限流 (Rate Limiting)

限流旨在通过限制请求速率来保护系统,防止因瞬时流量高峰而被压垮。

常见限流算法

  1. 固定窗口计数器:在固定的时间窗口内限制请求数。存在临界点问题,可能导致两倍于阈值的流量涌入。
  2. 滑动窗口计数器:将时间窗口细分为多个小格子,统计更为平滑和精确,解决了固定窗口的临界问题。
  3. 漏桶算法 (Leaky Bucket):以固定速率处理请求,多余的请求被放入队列或直接丢弃。能够平滑流量,但无法应对突发流量。
  4. 令牌桶算法 (Token Bucket):以固定速率向桶中放入令牌,请求必须获取令牌才能被处理。这种算法既能限制平均速率,又能允许一定程度的突发流量。

限流工具与方案

  • 单机限流
    • Google Guava RateLimiter:基于令牌桶算法,简单易用,支持平滑突发和预热限流。
    • Bucket4j:功能更全面的限流库,支持多种算法和监控集成。
    • Resilience4j:提供限流、熔断等多种容错功能,生态完善。
  • 分布式限流
    • 中间件:使用 Redis + Lua 脚本 实现,可以精确控制全局速率。
    • 网关层:在 API 网关(如 Spring Cloud Gateway, Kong)上配置限流策略,通常也需要借助 Redis 等中间件。
    • 流量治理平台:如 Alibaba Sentinel,提供可视化的控制台和强大的分布式限流能力。
本文由作者按照 CC BY 4.0 进行授权