Files
autumn-recruitment/05.架构/service-governance/限流熔断降级.md
T

7.3 KiB
Raw Blame History

tags, create time, update time
tags create time update time
arch/microservice
rate-limiting
circuit-breaker
sentinel
resilience4j
token-bucket
leaky-bucket
2026-08-08 18:00 2026-08-08 18:00

限流熔断降级

概述

限流、熔断、降级是微服务稳定性的三道防线。限流保护系统不被过量请求打垮,熔断在下游故障时快速失败避免级联雪崩,降级在服务不可用时返回兜底数据。三者层层递进,构成完整的容错体系。

核心原理

限流算法对比

graph LR
    subgraph Counter["计数器限流"]
        A[请求到达] --> B{本窗口内计数 < 阈值?}
        B -- 是 --> C[允许通过]
        B -- 否 --> D[拒绝]
    end

    subgraph TokenBucket["令牌桶"]
        E[请求到达] --> F{桶中有足够令牌?}
        F -- 是 --> G[消费令牌放行]
        F -- 否 --> H[拒绝/等待]
        I[匀速生成器] -->|"每秒 N 个"| J[(令牌桶)]
    end

    subgraph LeakyBucket["漏桶"]
        K[请求到达] --> L[(漏桶)]
        L --> M[固定速率流出处理]
        L -->|"溢出"| N[拒绝]
    end
算法 核心机制 突发能力 延迟特性 适用场景
滑动窗口计数器 时间窗内计数,超过阈值则拒绝 差(窗口切换时有"双倍突发") 低开销 简单 API QPS 限制
令牌桶 以恒定速率向桶中添加令牌,请求需消耗令牌后才能通过 强(桶满时可突发消费存量令牌) 中 CDN 加速、消息队列限速
漏桶 请求进入桶中,以固定速率流出处理 无(强制匀速) 高(有排队延迟) 需要平滑流量的网关层

滑动窗口的改进版——Leak Bucket vs Token Bucket:

  • 令牌桶适合保护生产者:允许突发流量,但长期受限于平均速率。
  • 漏桶适合保护消费者:无论输入多猛,输出永远匀速,但延迟不确定。

Note

Redisson 的限流器使用令牌桶算法实现 rate(10, RateInterval.SECONDS, RateLimitRate.SEGMENTED),支持分布式环境下跨节点的精确计数。

熔断三态转换

stateDiagram-v2
    [*] --> Closed: 初始状态
    Closed --> Open: 失败率超过阈值 (如 >50%)
    Open --> HalfOpen: 等待探测期超时 (如 30s)
    HalfOpen --> Closed: 探测请求全部成功
    HalfOpen --> Open: 探测请求仍有失败
    Open --> Closed: 直接硬切回 (应急手段)

    state Closed {
        [*] --> Monitoring: 持续统计失败比例
    }
    state Open {
        [*] --> RejectAll: 所有请求直接拒绝
    }
    state HalfOpen {
        [*] --> ProbeLimited: 有限探针进入
    }

各状态的详细行为:

状态 请求处理方式 统计维度 自动恢复?
Closed(关闭) 正常转发 统计最近 N 秒的请求成功/失败率 当失败率低于阈值且连续 M 次成功
Open(打开) 直接拒绝(FastFail),不调用下游 不统计(因为根本不发请求) 等待 ProbeTimeout 进入 HalfOpen
HalfOpen(半开) 发出少量探测请求(通常 1~3 个) 只统计这几次探测结果 全成功 → Closed;任一失败 → Open

半开探测策略:

  • 探测请求数量通常是固定的(如 Sentinel 默认 1 个),而非渐进式增加(像 Hystrix 那样)。
  • 探测期间原有业务请求仍然被熔断拦截,避免探测还没结束就压力暴增。
  • 如果探测成功立即转入 Closed,否则重新进入 Open 并重置倒计时。

Sentinel — 规则推送 + 指标统计

阿里巴巴开源的流量控制组件,核心特性:

  1. 双链路指标采集:滑动时间窗口 + 并发线程数两种维度。
  2. 规则动态推送:支持本地文件、Apollo、Nacos 等多种数据源,规则变更无需重启。
  3. 多级限流粒度:API 级别、方法级别、URL 级别、资源级别。
  4. 系统自适应保护:根据 Load、CPU 使用率、总 RPS 等自动调整限流阈值。
// Sentinel Go SDK - 规则定义
import "github.com/alibaba/sentinel-golang/api"
import "github.com/alibaba/sentinel-golang/core/slot"

rule := &flow.Rule{
    Resource:              "user-query",
    ThresholdType:         flow.QPS,
    Threshold:             100,
    ControlBehavior:       flow.Reject,
}
api.AddFlowRules([]*flow.Rule{rule})

Resilience4j — 函数式 API

与 Spring Cloud CircuitBreaker 深度集成,采用装饰器模式:

// Resilience4j 熔断器 + 重试组合
CircuitBreakerConfig config = CircuitBreakerConfig.custom()
    .failureRateThreshold(50)
    .waitDurationInOpenState(Duration.ofMillis(1000))
    .slidingWindowSize(10)
    .build();

io.github.resilience4j.circuitbreaker.CircuitBreaker cb =
    CircuitBreaker.of("user-service", config);

Retry retry = Retry.of("user-service", RetryConfig.custom()
    .maxAttempts(3)
    .waitDuration(Duration.ofMillis(100))
    .build());

Decorators.ofSupplier(() -> userService.getUser(id))
    .retry(retry)
    .circuitBreaker(cb)
    .fallback(e -> new User(-1, "default"))
    .get();

Tip

面试常考点:Resilience4j 和 Hystrix 的核心区别?Hystrix 基于线程池隔离(重),Resilience4j 基于信号量隔离(轻),且两者都是函数式设计而非继承 Thread。

降级的兜底思路

降级方式 示例 适合场景
返回缓存 用户信息从 DB 查不到时返回 Redis 旧值 对实时性要求不高的读接口
返回默认值 推荐接口超时返回热门商品列表 非核心业务流程
折叠请求 多个相同请求合并为一个真实请求 热点 Key 防护
Mock 数据 开发环境/预发布时直接返回预设 JSON CI/CD 阶段

代码示例

Go 中使用信号量实现简单的并发限流:

type SemaphoreLimit struct {
    sem chan struct{}
}

func NewSemaphoreLimit(maxConcurrent int) *SemaphoreLimit {
    return &SemaphoreLimit{sem: make(chan struct{}, maxConcurrent)}
}

func (s *SemaphoreLimit) Allow() bool {
    select {
    case s.sem <- struct{}{}:
        defer func() { <-s.sem }()
        return true // 放行
    default:
        return false // 拒绝
    }
}

实践场景

秋招高频问题:

  • "Sentinel 的滑动窗口和传统的固定窗口有什么区别?" — 固定窗口在边界处会漏算(上一个窗口的尾巴 + 下一个窗口的头同时超限);Sentinel 将窗口细分为更小的 tick,在每个 tick 内独立计数再聚合,精度由 tickSize 决定。
  • "为什么熔断要从 Open 到 HalfOpen 而不是直接回 Closed?" — HalfOpen 是一个安全阀:如果下游还在恢复中,直接进入 Closed 会让流量瞬间全部灌入,可能导致二次雪崩。有限的探测请求可以验证下游是否真的恢复了。
  • "令牌桶和漏桶选型建议?" — 网关层对外暴露 API 时用令牌桶(允许客户端合理突发);内部服务间调用时用漏桶(保护消费方不被打爆)。

Warning

不要把限流的阈值设得太激进。建议先观察线上 P99/RPS 基线,然后设为基线的 1.5~2 倍作为第一道防线,配合监控告警逐步调优。

关联笔记