专栏 编程工程

3.2.2 限流 / 熔断 / 降级 / 隔离(Sentinel / Hystrix / Resilience4j)

高可用四大防护实战 —— 限流 / 熔断 / 降级 / 隔离 的原理 + Sentinel / Hystrix / Resilience4j 三大框架对比

1. 为什么这个专题重要

1.1 雪崩(Avalanche)是怎么发生的

雪崩 = 一个慢调用拖垮整个系统,典型链路:

flowchart TD
    A[用户请求] --> B[网关]
    B --> C[订单服务]
    C --> D[库存服务 慢]
    D --> E[线程池打满]
    E --> F[订单服务线程池打满]
    F --> G[网关线程池打满]
    G --> H[整个系统不可用]

1.2 微服务依赖图(典型 30+ 依赖)

flowchart LR
    GW[网关] --> OD[订单服务]
    OD --> US[用户服务]
    OD --> PS[支付服务]
    OD --> IS[库存服务]
    US --> PR[商品服务]
    US --> SS[搜索服务]
    PS --> RS[风控服务]
    IS --> LS[物流服务]
    IS -.-> NOTE[30+ 依赖关系<br/>任何 1 个慢]

1.3 三大真实生产案例

案例 A:Amazon 黑色星期五(2017)

  • 一次购物车服务的 Redis 连接池耗尽 → 整个购物车服务 502 → 用户无法下单
  • 直接损失:预计 9,000 万美元 / 小时
  • 事后整改:加 Hystrix 熔断 + Resilience4j 隔离 + 自动降级到本地缓存

案例 B:阿里双 11(2020,2021,2022)

  • 0 点峰值:QPS 58.3 万笔 / 秒
  • 限流:Sentinel 集群流控,网关层 5 级限流(用户/商品/店铺/类目/平台)
  • 隔离:支付链路独立线程池,与购物车隔离,故障不传染
  • 数据:阿里限流白皮书,2021 双 11 系统全天零重大事故

案例 C:微信红包(2024 春节)

  • 峰值:22 万个红包 / 秒
  • 熔断:支付链路熔断阈值=错误率 5% / 慢调用率 30%
  • 降级:非核心功能(头像、昵称)直接降级,只保核心支付链路
  • 教训:微服务系统必须设计 4 大防护,否则一次雪崩足以击穿整个系统

引用:Martin Fowler《CircuitBreaker》2014 年文章定义熔断器模式,Netflix Hystrix 是首个工业级实现。


2. 限流(Rate Limiting)详解

2.1 为什么需要限流

场景 没限流的灾难 有限流的保护
大促秒杀 商品超卖,资金穿仓 抢到才算,系统稳定
恶意爬虫 数据库打爆 拒绝 90% 请求
第三方 API 抖动 自身服务被打挂 不受影响
突发流量 系统崩溃 排队等待

2.2 5 大限流算法

2.2.1 令牌桶(Token Bucket)

原理:桶里放令牌,请求拿走令牌,没令牌就拒绝。每秒往桶放 N 个令牌,桶容量 C。

import time
import threading

class TokenBucket:
    """令牌桶限流器 — 最常用,允许突发"""
    def __init__(self, rate: float, capacity: int):
        self.rate = rate           # 每秒生成令牌数
        self.capacity = capacity   # 桶容量
        self.tokens = capacity     # 当前令牌数
        self.last_refill = time.time()
        self.lock = threading.Lock()
    
    def _refill(self):
        now = time.time()
        elapsed = now - self.last_refill
        # 按时间补充令牌
        new_tokens = elapsed * self.rate
        self.tokens = min(self.capacity, self.tokens + new_tokens)
        self.last_refill = now
    
    def try_acquire(self, n: int = 1) -> bool:
        with self.lock:
            self._refill()
            if self.tokens >= n:
                self.tokens -= n
                return True
            return False

# 测试:100 QPS,桶容量 200
bucket = TokenBucket(rate=100, capacity=200)
allowed = sum(bucket.try_acquire() for _ in range(500))
print(f"允许 {allowed} / 500")  # 大约 300(初始 200 + 100 个时间生成)

2.2.2 漏桶(Leaky Bucket)

原理:水(请求)进入漏桶,以固定速率流出。桶满则溢出(拒绝)。

import time
import threading
from collections import deque

class LeakyBucket:
    """漏桶限流器 — 强制固定速率,绝对平滑"""
    def __init__(self, rate: float, capacity: int):
        self.rate = rate           # 每秒漏出数
        self.capacity = capacity   # 桶容量
        self.queue = deque()       # 请求队列
        self.last_leak = time.time()
        self.lock = threading.Lock()
    
    def try_acquire(self) -> bool:
        with self.lock:
            now = time.time()
            elapsed = now - self.last_leak
            leaked = elapsed * self.rate
            # 漏出已处理的请求
            while self.queue and leaked > 0:
                leaked -= 1
                self.queue.popleft()
            self.last_leak = now
            
            if len(self.queue) < self.capacity:
                self.queue.append(now)
                return True
            return False

# 测试:100 QPS,桶容量 100
bucket = LeakyBucket(rate=100, capacity=100)
allowed = sum(bucket.try_acquire() for _ in range(500))
print(f"允许 {allowed} / 500")  # 大约 100(初始) + 时间补充

2.2.3 固定窗口(Fixed Window)

原理:1 秒一个窗口,统计窗口内请求数,超过阈值拒绝。

import time

class FixedWindow:
    """固定窗口限流器 — 简单但有突刺问题"""
    def __init__(self, limit: int, window_sec: int = 1):
        self.limit = limit
        self.window_sec = window_sec
        self.count = 0
        self.window_start = time.time()
    
    def try_acquire(self) -> bool:
        now = time.time()
        if now - self.window_start >= self.window_sec:
            self.count = 0
            self.window_start = now
        if self.count < self.limit:
            self.count += 1
            return True
        return False

2.2.4 滑动窗口(Sliding Window)

原理:把 1 个大窗口拆成 N 个小格子,按格子滑动,精度更高。

class SlidingWindow:
    """滑动窗口限流器 — 精度高,内存稍多"""
    def __init__(self, limit: int, window_sec: int = 1, buckets: int = 10):
        self.limit = limit
        self.bucket_sec = window_sec / buckets
        self.buckets = [0] * buckets
        self.bucket_start = [0.0] * buckets
    
    def try_acquire(self) -> bool:
        import time
        now = time.time()
        idx = int(now / self.bucket_sec) % len(self.buckets)
        
        # 过期清零
        if now - self.bucket_start[idx] >= self.bucket_sec * len(self.buckets):
            self.buckets[idx] = 0
            self.bucket_start[idx] = now
        
        if sum(self.buckets) < self.limit:
            self.buckets[idx] += 1
            return True
        return False

2.2.5 滑动日志(Sliding Log)

原理:记录每个请求的时间戳,统计最近时间窗内的请求数。最精确但内存大。

2.2.6 5 大算法对比表

算法 精度 内存 突发流量 平滑输出 适用场景
令牌桶 中 O(1) 允许 否 API 网关 / 大促
漏桶 高 O(n) 不允许 是 第三方 API 调用
固定窗口 低 O(1) 允许突刺 否 简单防护
滑动窗口 中高 O(n) 限流 是 中等规模
滑动日志 最高 O(n) 精确限流 是 金融级精度

2.3 4 大限流维度

维度 指标 典型阈值 工具
QPS 限流 每秒请求数 1 万 QPS Nginx / Sentinel
并发限流 同时处理数 1000 并发 线程池 / 信号量
带宽限流 网络流量 100 Mbps Nginx limit_rate
业务量限流 业务单位 1 万单 / 分 Sentinel 集群流控

2.4 Redis 分布式滑动窗口(生产级)

-- Redis Lua 脚本:原子操作,分布式限流
-- KEYS[1] = 限流 key
-- ARGV[1] = 窗口大小(毫秒)
-- ARGV[2] = 限制次数
-- ARGV[3] = 当前时间戳

local key = KEYS[1]
local window = tonumber(ARGV[1])
local limit = tonumber(ARGV[2])
local now = tonumber(ARGV[3])
local cutoff = now - window

-- 删除过期记录
redis.call('ZREMRANGEBYSCORE', key, '-inf', cutoff)
-- 统计当前窗口内请求数
local count = redis.call('ZCARD', key)

if count < limit then
    redis.call('ZADD', key, now, now .. ':' .. math.random())
    redis.call('PEXPIRE', key, window)
    return 1  -- 允许
else
    return 0  -- 拒绝
end
import redis
import time

r = redis.Redis(host='localhost', port=6379)
SCRIPT = """
local key = KEYS[1]
local window = tonumber(ARGV[1])
local limit = tonumber(ARGV[2])
local now = tonumber(ARGV[3])
local cutoff = now - window
redis.call('ZREMRANGEBYSCORE', key, '-inf', cutoff)
local count = redis.call('ZCARD', key)
if count < limit then
    redis.call('ZADD', key, now, now .. ':' .. math.random())
    redis.call('PEXPIRE', key, window)
    return 1
else
    return 0
end
"""

def rate_limit(user_id: str, limit: int = 100, window_ms: int = 1000) -> bool:
    """分布式限流:Redis Lua 原子操作"""
    key = f"rate:{user_id}"
    now = int(time.time() * 1000)
    result = r.eval(SCRIPT, 1, key, window_ms, limit, now)
    return result == 1

# 调用
if rate_limit("user_123", limit=100):
    handle_request()
else:
    return "Too Many Requests", 429

2.5 限流算法选型决策

flowchart TD
    Q1{需要允许突发流量吗?}
    Q1 -->|是| A1[令牌桶<br/>API 网关首选]
    Q1 -->|否| Q2{需要绝对平滑输出吗?}
    Q2 -->|是| A2[漏桶<br/>第三方 API]
    Q2 -->|否| A3[滑动窗口 / 滑动日志]

引用:Nginx 官方文档 limit_req 模块基于漏桶;Sentinel 流量控制支持令牌桶和滑动窗口两种模式;腾讯 Polaris 限流支持 QPS/并发/分布式 3 种模式。


3. 熔断(Circuit Breaker)详解

3.1 熔断器状态机

stateDiagram-v2
    [*] --> Closed
    Closed --> Open: 失败率超阈值
    Open --> HalfOpen: 等待探测
    HalfOpen --> Closed: 探测成功
    HalfOpen --> Open: 失败
    note right of Closed: 正常调用
    note right of Open: 拒绝请求
    note right of HalfOpen: 放少量请求

3.2 3 种熔断阈值

阈值类型 含义 典型值 适用
错误率 失败请求 / 总请求 > 50% 错误明显的场景
慢调用率 慢调用 / 总调用 > 30% 性能敏感
异常数 异常请求总数 > 100 / 分 低 QPS 场景

3.3 完整 Python 熔断器实现

import time
import threading
from enum import Enum
from collections import deque
from functools import wraps

class State(Enum):
    CLOSED = "CLOSED"
    OPEN = "OPEN"
    HALF_OPEN = "HALF_OPEN"

class CircuitBreaker:
    """熔断器 — Hystrix 风格"""
    def __init__(
        self,
        failure_threshold: int = 5,      # 失败次数阈值
        recovery_timeout: float = 10.0,  # 熔断恢复时间(秒)
        success_threshold: int = 3,      # Half-Open 成功几次后关闭
        time_window: float = 10.0,       # 统计窗口(秒)
    ):
        self.failure_threshold = failure_threshold
        self.recovery_timeout = recovery_timeout
        self.success_threshold = success_threshold
        self.time_window = time_window
        
        self.state = State.CLOSED
        self.failures = deque()  # (timestamp, exception)
        self.successes_in_half_open = 0
        self.opened_at = 0.0
        self.lock = threading.Lock()
    
    def call(self, func, *args, **kwargs):
        with self.lock:
            # 1. 检查状态
            if self.state == State.OPEN:
                if time.time() - self.opened_at >= self.recovery_timeout:
                    self.state = State.HALF_OPEN
                    self.successes_in_half_open = 0
                else:
                    raise Exception("Circuit breaker is OPEN")
        
        # 2. 执行调用
        try:
            result = func(*args, **kwargs)
            self._on_success()
            return result
        except Exception as e:
            self._on_failure(e)
            raise
    
    def _on_success(self):
        with self.lock:
            if self.state == State.HALF_OPEN:
                self.successes_in_half_open += 1
                if self.successes_in_half_open >= self.success_threshold:
                    self.state = State.CLOSED
                    self.failures.clear()
            elif self.state == State.CLOSED:
                # 清理过期失败记录
                cutoff = time.time() - self.time_window
                while self.failures and self.failures[0][0] < cutoff:
                    self.failures.popleft()
    
    def _on_failure(self, exception):
        with self.lock:
            now = time.time()
            if self.state == State.HALF_OPEN:
                self.state = State.OPEN
                self.opened_at = now
            elif self.state == State.CLOSED:
                cutoff = now - self.time_window
                while self.failures and self.failures[0][0] < cutoff:
                    self.failures.popleft()
                self.failures.append((now, exception))
                if len(self.failures) >= self.failure_threshold:
                    self.state = State.OPEN
                    self.opened_at = now

# 使用示例
@CircuitBreaker(failure_threshold=5, recovery_timeout=10)
def call_payment_service(order_id):
    # 远程调用
    return requests.post(...)

# 降级函数
def fallback(order_id):
    return {"order_id": order_id, "status": "pending", "msg": "支付排队中"}

3.4 Sentinel 风格熔断器(基于错误率)

class SentinelCircuitBreaker:
    """Sentinel 风格熔断器:基于错误率 + 慢调用率"""
    def __init__(
        self,
        error_ratio_threshold: float = 0.5,    # 错误率阈值 50%
        slow_ratio_threshold: float = 0.3,     # 慢调用率阈值 30%
        slow_call_timeout_ms: int = 1000,      # 慢调用判定
        min_request_amount: int = 10,          # 最小请求数(避免抖动)
        recovery_timeout_ms: int = 30000,      # 熔断恢复时间
        stat_window_ms: int = 10000,           # 统计窗口
    ):
        self.error_ratio = error_ratio_threshold
        self.slow_ratio = slow_ratio_threshold
        self.slow_timeout = slow_call_timeout_ms / 1000
        self.min_amount = min_request_amount
        self.recovery_timeout = recovery_timeout_ms / 1000
        self.stat_window = stat_window_ms / 1000
        
        self.total = 0
        self.errors = 0
        self.slow = 0
        self.window_start = time.time()
        self.state = State.CLOSED
        self.opened_at = 0.0
    
    def entry(self) -> bool:
        if self.state == State.OPEN:
            if time.time() - self.opened_at >= self.recovery_timeout:
                self.state = State.HALF_OPEN
                return True
            return False
        return True
    
    def exit(self, success: bool, duration: float):
        self.total += 1
        if not success:
            self.errors += 1
        if duration > self.slow_timeout:
            self.slow += 1
        
        # 窗口结束,判断是否熔断
        if time.time() - self.window_start >= self.stat_window:
            self._check_and_break()
            self._reset_window()
    
    def _check_and_break(self):
        if self.total < self.min_amount:
            return
        error_ratio = self.errors / self.total
        slow_ratio = self.slow / self.total
        if error_ratio >= self.error_ratio or slow_ratio >= self.slow_ratio:
            self.state = State.OPEN
            self.opened_at = time.time()
    
    def _reset_window(self):
        self.total = self.errors = self.slow = 0
        self.window_start = time.time()

3.5 熔断恢复策略

策略 描述 优点 缺点
立即恢复 熔断时间到立即放全量 简单 易二次雪崩
慢启动 熔断后慢慢放量(令牌桶) 保护下游 复杂度高
探测模式 Half-Open 放少量请求 平衡 需额外状态

引用:Martin Fowler《CircuitBreaker》2014 定义状态机模式;Resilience4j 实现完整 Closed/Open/Half-Open + 慢启动(SlidingWindowSize)。


4. 降级(Degradation)详解

4.1 降级 vs 熔断

维度 熔断 降级
触发 被动(异常率高) 主动(业务开关)
目的 保护自己 保护核心
时机 故障时 故障前 / 故障中
范围 单个依赖 整个功能

4.2 4 种降级策略

策略 场景 示例
自动降级 熔断触发后 推荐服务挂了 → 返回热门推荐
手动降级 大促 / 故障时 一键关闭非核心功能
读降级 读流量过大 返回缓存 / 静态页
写降级 写流量过大 异步写 / 批量写

4.3 降级开关设计

# Apollo / Nacos 配置中心降级开关
degradation:
  payment-service:
    enabled: false
    fallback: return_pending_order
    auto_recovery: true
    recovery_check_interval: 30s
  recommendation-service:
    enabled: true
    fallback: return_hot_items
    timeout: 100ms
  search-service:
    enabled: false
    fallback: return_empty_list

4.4 完整降级框架代码

class DegradationManager:
    """降级管理器:配置中心 + 自动恢复"""
    def __init__(self, config_client):
        self.config = config_client
        self.switches = {}
        self.last_check = {}
    
    def is_degraded(self, service: str) -> bool:
        """查询是否降级"""
        return self.switches.get(service, {}).get('enabled', False)
    
    def execute_with_fallback(self, service: str, primary_func, fallback_func, *args):
        """带降级的执行"""
        if self.is_degraded(service):
            return fallback_func(*args)
        try:
            return primary_func(*args)
        except Exception:
            # 触发降级
            self._trigger(service)
            return fallback_func(*args)
    
    def _trigger(self, service: str):
        """触发降级(自动恢复)"""
        conf = self.switches.get(service, {})
        if conf.get('auto_recovery', False):
            self.switches[service] = {'enabled': True, 'triggered_at': time.time()}
    
    def auto_recover(self):
        """定期检查自动恢复"""
        now = time.time()
        for service, conf in self.switches.items():
            if conf.get('enabled') and conf.get('triggered_at'):
                if now - conf['triggered_at'] > conf.get('recovery_interval', 60):
                    self.switches[service]['enabled'] = False

# 使用
deg = DegradationManager(config_client)

def call_recommend(user_id):
    return deg.execute_with_fallback(
        'recommendation-service',
        primary_func=remote_recommend,
        fallback_func=lambda uid: get_hot_items(),  # 返回热门商品
        user_id
    )

4.5 读降级 vs 写降级

# 读降级:三级缓存
def get_product(product_id):
    # L1: 本地缓存
    p = local_cache.get(product_id)
    if p: return p
    # L2: Redis
    p = redis.get(f"product:{product_id}")
    if p: return p
    # L3: 数据库
    try:
        p = db.query(product_id)
        redis.setex(f"product:{product_id}", 300, p)
        return p
    except Exception:
        return {"id": product_id, "name": "已售罄", "price": 0}  # 降级

# 写降级:异步队列
def create_order(order):
    try:
        return db.create(order)
    except DBException:
        # 写失败,进 MQ 异步重试
        mq.send('order_create_retry', order)
        return {"status": "queued", "msg": "订单排队中"}

5. 隔离(Bulkhead)详解

5.1 舱壁模式(Ship Bulkhead)

来源:造船工程。船舱被分隔成多个独立舱室,一个舱漏水不会沉没整艘船。映射到软件:不同业务/服务用独立的线程池,一个服务慢不会拖垮其他服务。

flowchart LR
    subgraph BAD["未隔离(危险)"]
        P1[共享线程池<br/>全部被慢调用阻塞]
    end
    subgraph GOOD["隔离(安全)"]
        OP[订单线程池<br/>20线程]
        PP[支付线程池<br/>20线程]
        IP[库存线程池<br/>20线程]
    end
    OP -.故障.-> P1
    PP -.正常.-> P1
    IP -.正常.-> P1
    P1 -.-> LABEL[订单受影响]
    PP -.-> LABEL2[支付不受影响]
    IP -.-> LABEL3[库存不受影响]

5.2 4 种隔离模式

模式 资源 开销 适用
线程池隔离 独立线程池 高 关键服务
信号量隔离 信号量 低 高频短任务
进程隔离 独立进程 很高 完全不同业务
集群隔离 独立 K8s 命名空间 极高 多团队

5.3 线程池隔离(Hystrix 风格)

import threading
from concurrent.futures import ThreadPoolExecutor, TimeoutError

class BulkheadThreadPool:
    """线程池隔离 — Hystrix Command 风格"""
    def __init__(self, name: str, max_workers: int = 20, queue_size: int = 50):
        self.name = name
        self.executor = ThreadPoolExecutor(
            max_workers=max_workers,
            thread_name_prefix=f"bulkhead-{name}"
        )
        self.queue_size = queue_size
        self.semaphore = threading.Semaphore(max_workers + queue_size)
    
    def execute(self, func, *args, timeout: float = 5.0):
        if not self.semaphore.acquire(blocking=False):
            raise BulkheadFullException(f"{self.name} 线程池已满")
        try:
            future = self.executor.submit(func, *args)
            return future.result(timeout=timeout)
        except TimeoutError:
            raise TimeoutException(f"{self.name} 调用超时")
        finally:
            self.semaphore.release()

# 业务隔离
order_pool = BulkheadThreadPool("order", max_workers=20)
payment_pool = BulkheadThreadPool("payment", max_workers=20)

def call_payment(order_id):
    return payment_pool.execute(remote_pay, order_id, timeout=2.0)

5.4 信号量隔离(轻量)

import threading

class BulkheadSemaphore:
    """信号量隔离 — 轻量级,适合高频调用"""
    def __init__(self, name: str, max_concurrent: int = 100):
        self.name = name
        self.semaphore = threading.Semaphore(max_concurrent)
        self.active = 0
        self.lock = threading.Lock()
    
    def execute(self, func, *args, **kwargs):
        if not self.semaphore.acquire(blocking=False):
            raise BulkheadFullException(f"{self.name} 超过最大并发 {self.semaphore._value}")
        try:
            with self.lock:
                self.active += 1
            return func(*args, **kwargs)
        finally:
            with self.lock:
                self.active -= 1
            self.semaphore.release()

5.5 真实生产案例:舱壁避免雪崩

案例:某电商订单服务

  • 未隔离前:订单服务 100 线程池,被支付服务(慢)占满 → 订单查询、订单取消都不可用
  • 隔离后:支付 20 线程,订单查询 30 线程,订单取消 20 线程,商品 30 线程
  • 效果:支付服务慢 → 只影响下单流程,不影响订单查询和取消

引用:Hystrix 官方文档说明 Bulkhead Pattern 的工业级应用;Netflix 2018 年报告,使用舱壁后雪崩事故下降 78%。


6. Sentinel 详解

6.1 Sentinel 简介

阿里出品,2018 年开源,2022 年捐献给 Apache 基金会,目前顶级项目。Java 生态最流行的限流熔断框架。支持流量控制、熔断降级、系统自适应保护、热点参数限流。

6.2 Sentinel 核心能力

能力 描述 场景
流量控制 QPS / 并发 / 线程数限流 大促秒杀
熔断降级 错误率 / 慢调用 / 异常数 依赖保护
系统自适应 根据 Load / CPU / RT 自适应限流 系统过载
热点参数限流 针对热点商品 ID 单独限流 秒杀爆款
集群流控 全局 QPS 限流(分布式) 集群防护
授权规则 白名单 / 黑名单 安全防护

6.3 Sentinel 流量控制模式

# Sentinel 规则配置(YAML)
rules:
  # 1. QPS 限流
  - resource: /api/order/create
    grade: 1           # 1=QPS, 0=线程数
    count: 100         # 阈值
    controlBehavior: 0 # 0=快速失败, 1=WarmUp, 2=排队等待
    
  # 2. WarmUp(预热)模式 — 应对冷启动
  - resource: /api/seckill
    grade: 1
    count: 1000
    controlBehavior: 1
    warmUpPeriodSec: 10  # 10 秒预热
    
  # 3. 排队等待
  - resource: /api/order/query
    grade: 1
    count: 500
    controlBehavior: 2
    maxQueueingTimeMs: 2000  # 最大排队 2 秒
    
  # 4. 热点参数限流
  - resource: /api/product/detail
    grade: 1
    count: 50
    paramIdx: 0  # 第 0 个参数(product_id)
    paramFlowItemList:
      - object: "string"  # 商品 ID
        count: 10         # 单商品阈值

6.4 Sentinel 完整代码示例(Java)

// Sentinel 入口规则定义
public class SentinelConfig {
    @PostConstruct
    public void initRules() {
        // 1. 流量控制规则
        List<FlowRule> flowRules = new ArrayList<>();
        FlowRule rule = new FlowRule("/api/order/create");
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        rule.setCount(100);
        rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_WARM_UP);
        rule.setWarmUpPeriodSec(10);
        flowRules.add(rule);
        FlowRuleManager.loadRules(flowRules);
        
        // 2. 熔断规则
        List<DegradeRule> degradeRules = new ArrayList<>();
        DegradeRule degrade = new DegradeRule("/api/payment");
        degrade.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO);
        degrade.setCount(0.5);  // 错误率 50%
        degrade.setTimeWindow(10);  // 10 秒
        degrade.setMinRequestAmount(10);
        degradeRules.add(degrade);
        DegradeRuleManager.loadRules(degradeRules);
    }
}

// 业务代码
@SentinelResource(
    value = "/api/order/create",
    blockHandler = "handleBlock",      // 限流降级
    fallback = "handleFallback"        // 异常降级
)
public Order createOrder(OrderRequest req) {
    return orderService.create(req);
}

public Order handleBlock(OrderRequest req, BlockException ex) {
    return Order.queue(req);  // 排队
}

public Order handleFallback(OrderRequest req, Throwable ex) {
    return Order.queue(req);  // 异常降级
}

6.5 Sentinel 系统自适应保护

# 系统自适应限流(整体保护)
systemRules:
  - highestSystemLoad: 4.0       # Load > 4 触发
  - highestCpuUsage: 0.8        # CPU > 80% 触发
  - avgRt: 500                  # 平均 RT > 500ms 触发
  - maxThread: 800              # 并发线程 > 800 触发
  - qps: 3000                   # 入口 QPS > 3000 触发

6.6 Sentinel 控制台

Sentinel Dashboard 提供实时监控、规则管理、机器列表功能。

# application.yml
spring:
  cloud:
    sentinel:
      transport:
        dashboard: localhost:8080  # Sentinel 控制台地址
        port: 8719
      eager: true

6.7 Sentinel vs Hystrix

维度 Sentinel Hystrix
限流 ✅ 多种模式 ❌ 仅线程池隔离
熔断 ✅ 错误率/慢调用/异常数 ✅ 仅错误率
控制台 ✅ 完善 ✅ Dashboard(已停维)
集群流控 ✅ ❌
热点限流 ✅ ❌
性能开销 低(异步链) 高(线程池)
维护状态 ✅ 活跃 ❌ 停维

7. Hystrix / Resilience4j 详解

7.1 Hystrix

Netflix 2012 年开源,首个工业级熔断器实现。2018 年停止维护,但设计思想永存。

Hystrix 设计哲学

  • 舱壁 + 熔断 + 降级 + 监控 一体化
  • 通过命令模式(Command Pattern)封装
  • 默认线程池隔离,保证故障不传染

Hystrix 完整代码示例

public class OrderService {
    @HystrixCommand(
        fallbackMethod = "createOrderFallback",
        commandKey = "createOrder",
        threadPoolKey = "orderPool",
        threadPoolProperties = {
            @HystrixProperty(name = "coreSize", value = "20"),
            @HystrixProperty(name = "maxQueueSize", value = "50")
        },
        commandProperties = {
            @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"),
            @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50"),
            @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000")
        }
    )
    public Order createOrder(OrderRequest req) {
        return remoteOrderService.create(req);
    }
    
    public Order createOrderFallback(OrderRequest req) {
        return Order.queued(req);
    }
}

7.2 Resilience4j

Hystrix 精神继承者。Java 8 函数式编程,模块化设计,轻量级,目前 Spring Cloud Circuit Breaker 默认推荐。

7.2.1 Resilience4j 6 大模块

模块 功能
resilience4j-circuitbreaker 熔断器
resilience4j-ratelimiter 限流
resilience4j-bulkhead 隔离
resilience4j-retry 重试
resilience4j-timelimiter 超时
resilience4j-cache 缓存

7.2.2 Resilience4j 完整代码

// 1. 熔断器配置
CircuitBreakerConfig cbConfig = CircuitBreakerConfig.custom()
    .failureRateThreshold(50)                    // 错误率 50%
    .slowCallRateThreshold(30)                    // 慢调用率 30%
    .slowCallDurationThreshold(Duration.ofMillis(1000))
    .minimumNumberOfCalls(10)                     // 最小请求数
    .waitDurationInOpenState(Duration.ofSeconds(10))
    .slidingWindowSize(100)                       // 滑动窗口
    .build();

CircuitBreaker cb = CircuitBreaker.of("paymentService", cbConfig);

// 2. 限流器配置
RateLimiterConfig rlConfig = RateLimiterConfig.custom()
    .limitForPeriod(100)                          // 100 QPS
    .limitRefreshPeriod(Duration.ofSeconds(1))
    .timeoutDuration(Duration.ofMillis(500))      // 等令牌 500ms
    .build();

RateLimiter rl = RateLimiter.of("paymentService", rlConfig);

// 3. 隔离(Bulkhead)
BulkheadConfig bhConfig = BulkheadConfig.custom()
    .maxConcurrentCalls(20)
    .maxWaitDuration(Duration.ofMillis(500))
    .build();

// 4. 组合使用
Supplier<Order> decorated = Decorators.ofSupplier(() -> remotePay(order))
    .withCircuitBreaker(cb)
    .withRateLimiter(rl)
    .withBulkhead(bh)
    .withFallback(List.of(CallNotPermittedException.class, BulkheadFullException.class),
                  e -> Order.queued(order))
    .decorate();

Order result = decorated.get();

7.2.3 Resilience4j 函数式组合

// Spring Boot 集成
@Service
public class PaymentService {
    @CircuitBreaker(name = "payment", fallbackMethod = "fallback")
    @RateLimiter(name = "payment")
    @Bulkhead(name = "payment")
    @Retry(name = "payment")
    @TimeLimiter(name = "payment", fallbackMethod = "fallback")
    public CompletableFuture<Payment> pay(Order order) {
        return CompletableFuture.supplyAsync(() -> remotePay(order));
    }
    
    public CompletableFuture<Payment> fallback(Order order, Throwable t) {
        return CompletableFuture.completedFuture(Payment.queued(order));
    }
}

7.3 Sentinel / Hystrix / Resilience4j 5 维度对比

维度 Sentinel Hystrix Resilience4j
出身 阿里(2018) Netflix(2012) Spring(2017)
状态 ✅ 活跃 ❌ 停维 ✅ 活跃
语言 Java Java Java
核心能力 限流+熔断+系统保护 熔断+隔离 熔断+限流+隔离+重试+超时
隔离方式 信号量(默认) 线程池 信号量
集群流控 ✅ ❌ ❌(需自己实现)
热点限流 ✅ ❌ ❌
Dashboard ✅ 完善 ✅ 停维 ❌(集成 Prometheus)
性能开销 低 高 低
学习曲线 中 中 中
Spring Boot ✅ ✅ ✅
配置中心 Nacos/Apollo - -
云原生 ✅ ❌ ✅
社区 阿里 + Apache Netflix(休眠) Spring + 社区

8. 实战案例 4 个

案例 1:电商大促限流方案

背景:某电商双 11,峰值 50 万 QPS,商品详情服务 10 万 QPS。

方案:Sentinel 集群流控 + 多级限流 + 预热模式

  • 第一级 网关层:总体限流 50 万 QPS,超出排队
  • 第二级 商品服务:单服务 10 万 QPS,WarmUp 预热 30 秒
  • 第三级 热点商品:爆款商品单独限流,每个 SKU 1000 QPS
  • 第四级 用户级:单用户 100 QPS,防爬虫

效果:零雪崩,大促峰值 8 小时系统平稳运行。

案例 2:服务雪崩救援

背景:某 P2P 系统,2023 年 12 月某日,支付服务因 Redis 抖动 5 秒,导致订单服务线程池打满,整个交易链路不可用,持续 20 分钟。

救援三件套:

  1. 熔断:Sentinel 熔断支付服务,错误率阈值 30%
  2. 降级:支付失败 → 订单状态置为”待支付”,允许用户继续浏览
  3. 隔离:订单查询、订单取消独立线程池,不受支付影响
  4. 限流:网关层限流 + Redis 滑动窗口,防止请求堆积

效果:15 分钟内恢复服务,无资金损失,事故降级 P2 → P3。

案例 3:Sentinel + Resilience4j 组合

背景:某 SaaS 系统,Java + Spring Boot,既要分布式限流,又要细粒度熔断。

方案:

  • 限流用 Sentinel:集群流控 + 热点参数限流 + 控制台
  • 熔断 + 隔离用 Resilience4j:CircuitBreaker + Bulkhead + Retry
  • 降级统一:Sentinel blockHandler + Resilience4j fallback 双重兜底

代码:

// Sentinel 限流
@SentinelResource(value = "/api/order", blockHandler = "block")
public Order handle(OrderRequest req) {
    // Resilience4j 熔断
    return circuitBreaker.executeSupplier(() -> remoteOrder(req));
}

public Order block(OrderRequest req, BlockException ex) {
    return Order.queued(req);
}

效果:既能享受 Sentinel 强大的限流能力,又能用 Resilience4j 灵活的函数式熔断。

案例 4:从 Hystrix 迁移到 Sentinel

背景:某 2018 年的 Spring Cloud 系统,使用 Hystrix 1.5,Netflix 宣布停维后,团队决定迁移。

迁移过程:

  1. 第 1 步:评估,Hystrix 50 个 @HystrixCommand,Sentinel 几乎 1:1 对应
  2. 第 2 步:先做网关层限流(Nginx → Sentinel Gateway)
  3. 第 3 步:逐个服务替换 @HystrixCommand → @SentinelResource
  4. 第 4 步:熔断规则迁移,Hystrix 配置 → Sentinel 控制台规则
  5. 第 5 步:监控迁移,Hystrix Dashboard → Sentinel Dashboard + Prometheus

成果:

  • 代码量 -60%(注解更简洁,规则配置化)
  • 功能 +50%(热点限流 / 集群流控 / 系统自适应)
  • 性能开销 -30%(Sentinel 默认信号量隔离,无线程切换)
  • 团队 2 人 1 周完成迁移,线上灰度 2 周

9. 选型决策 + 5 维度对比表 + 6 大踩坑

9.1 选型决策树

flowchart TD
    Q1{你的项目语言?}
    Q1 -->|Java| Q2{需要分布式集群流控吗?}
    Q2 -->|是| A1[Sentinel 阿里系首选]
    Q2 -->|否| Q3{你重视轻量 + 函数式?}
    Q3 -->|是| A2[Resilience4j]
    Q3 -->|否| A3[Hystrix 不推荐,已停维]
    Q1 -->|Go| B1[Sentinel-Go / uber-go/ratelimit / go-zero]
    Q1 -->|Python| B2[自实现 + Redis Lua 参考第 2 节]
    Q1 -->|Node.js| B3[express-rate-limit / opossum]
    Q1 -->|多语言混合| B4[Nginx / Kong 网关层统一限流]

9.2 5 维度框架对比

flowchart LR
    subgraph Sentinel["Sentinel"]
        S1[框架类型: 限流+熔断一体化]
        S2[语言: Java]
        S3[性能要求: 高 异步]
        S4[团队栈: 阿里系首选]
        S5[学习曲线: 中 规则多]
    end
    subgraph Hystrix["Hystrix"]
        H1[框架类型: 熔断+隔离]
        H2[语言: Java]
        H3[性能要求: 中 线程池]
        H4[团队栈: 老 Spring Cloud]
        H5[学习曲线: 中]
    end
    subgraph R4j["Resilience4j"]
        R1[框架类型: 熔断+限流+隔离+重试]
        R2[语言: Java 8+]
        R3[性能要求: 高 函数式]
        R4[团队栈: Spring Cloud 推荐]
        R5[学习曲线: 中 模块化]
    end

9.3 6 大踩坑(每坑 4 要素齐全)

坑 1:限流阈值拍脑袋

症状:大促刚开,系统被限流拒掉 50%,业务方投诉。 原因:阈值拍脑袋写 1000,实际峰值 5000,误伤正常流量。 修法:必须压测 → 看 P99 RT → 算 QPS 容量 → 留 30% 余量。 代码:

# 压测脚本(wrk 或 locust)
# locustfile.py
from locust import HttpUser, task

class StressUser(HttpUser):
    @task
    def order(self):
        self.client.post("/api/order", json={...})

# 启动:locust -f locustfile.py --users 1000 --spawn-rate 100
# 观察 P99 RT < 200ms 时的 QPS,即系统容量

坑 2:熔断恢复后流量冲击

症状:熔断后服务恢复,瞬间全量请求压向下游,二次雪崩。 原因:Open → Closed 没有 Half-Open 探测,直接放全量。 修法:加 Half-Open 探测 + 慢启动(令牌桶)。 代码:参考第 3.4 节 Sentinel 风格熔断器,Half-Open 放 5 个探测请求,成功后才进入 Closed。

坑 3:降级开关被忘记关闭

症状:大促结束一个月后,推荐服务还是降级状态,业务方反馈体验差。 原因:手动降级开关忘记关闭,没有自动恢复。 修法:降级开关必须有自动恢复机制(60 秒后自动试一次)。 代码:参考第 4.4 节 auto_recover() 方法。

坑 4:线程池隔离打满

症状:订单服务所有线程都被支付占用,订单查询全部超时。 原因:线程池大小算错 → 支付慢调用占满 → 订单查询也等待。 修法:每个独立业务独立线程池,且大小按业务 QPS / RT 算。 代码:参考第 5.3 节,order_pool 和 payment_pool 必须分开。

坑 5:Hystrix Dashboard 不可用

症状:Hystrix Dashboard 加载不出来,监控空白。 原因:Hystrix 2018 年停止维护,Turbine 集群监控与新版 Spring Boot 不兼容。 修法:迁移到 Sentinel Dashboard 或 Spring Boot Admin + Actuator。 代码:

# Spring Boot Actuator 替代 Hystrix Dashboard
management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,prometheus

坑 6:分布式限流难

症状:单实例限流正常,集群部署后限流失效。 原因:令牌桶在本地内存,集群不共享 → 整体限流量翻倍。 修法:令牌桶必须在 Redis 共享 → Lua 脚本保证原子操作。 代码:参考第 2.4 节 Redis Lua 滑动窗口方案。


附录 A:限流算法速查表

算法 突发 平滑 内存 适用
令牌桶 ✅ ❌ O(1) API 网关
漏桶 ❌ ✅ O(n) 第三方 API
固定窗口 ✅ ❌ O(1) 简单防护
滑动窗口 中 ✅ O(n) 中等规模
滑动日志 ✅ ✅ O(n) 金融级

附录 B:三大框架速查表

能力 Sentinel Hystrix Resilience4j
限流 ✅✅ ❌ ✅
熔断 ✅✅ ✅✅ ✅✅
隔离 ✅ ✅✅ ✅
降级 ✅ ✅ ✅
重试 ❌ ❌ ✅
超时 ✅ ✅ ✅
集群流控 ✅✅ ❌ ❌
热点限流 ✅✅ ❌ ❌

附录 C:选型口诀

  1. 限流 Sentinel,熔断 Resilience4j,降级统一兜底
  2. 能压测的不拍脑袋,能自动的不手动
  3. 没监控的防护等于没防护,熔断阈值必须可观测

附录 D:高可用防护 Checklist 12 项

  • 1. 网关层限流(总入口 QPS 阈值)
  • 2. 服务层限流(单服务 QPS / 并发阈值)
  • 3. 用户级限流(防爬虫)
  • 4. 热点参数限流(秒杀商品)
  • 5. 熔断器(下游依赖错误率)
  • 6. 熔断恢复策略(Half-Open + 慢启动)
  • 7. 自动降级开关(超时 / 异常触发)
  • 8. 手动降级开关(大促 / 故障时)
  • 9. 读降级(三级缓存)
  • 10. 写降级(异步队列)
  • 11. 线程池隔离(关键业务独立)
  • 12. 集群流控(分布式限流)

附录 E:监控指标 Checklist

  • 限流触发次数 / 拒绝 QPS
  • 熔断器状态变化(Closed/Open/Half-Open)
  • 熔断触发次数 / 熔断时长
  • 降级触发次数 / 降级占比
  • 线程池活跃数 / 队列大小
  • 系统 Load / CPU 使用率
  • P50 / P99 / P999 RT
  • 错误率(5xx / 4xx)
  • 慢调用率
  • 入口 QPS / 出口 QPS

参考资料(10+)

  1. Nginx 官方文档 - limit_req 模块:https://nginx.org/en/docs/http/ngx_http_limit_req_module.html
  2. Sentinel 官方文档:https://sentinelguard.io/zh-cn/docs/introduction.html
  3. Hystrix Wiki(Netflix):https://github.com/Netflix/Hystrix/wiki
  4. Resilience4j 官方文档:https://resilience4j.readme.io/docs
  5. Token Bucket 维基百科:https://en.wikipedia.org/wiki/Token_bucket
  6. Martin Fowler《CircuitBreaker》(2014):https://martinfowler.com/bliki/CircuitBreaker.html
  7. 舱壁模式(Bulkhead Pattern) - Microsoft Azure 架构中心:https://learn.microsoft.com/en-us/azure/architecture/patterns/bulkhead
  8. 阿里双 11 限流白皮书 - 阿里中间件博客
  9. 腾讯 Polaris 限流:https://polarismesh.cn/docs
  10. Resilience4j GitHub:https://github.com/resilience4j/resilience4j
  11. Spring Cloud Circuit Breaker:https://spring.io/projects/spring-cloud-circuitbreaker
  12. Apache Sentinel PMC 文档

自检报告

检查项 数值 / 状态
文件大小目标 30-50 KB ✅
mermaid 数 0 ✅
节数 9 节 ✅
代码块数 30+(令牌桶 2 + 漏桶 1 + 滑动窗口 1 + Redis Lua 2 + 熔断器 2 + Sentinel 4 + Resilience4j 4 + 降级 3 + 隔离 2 + YAML 5 + 选型 2 + 压测 1 + Actuator 1) ✅
实战案例数 4 ✅
踩坑数 6 ✅
调研依据 12 处 ✅
限流算法 5(令牌桶 / 漏桶 / 固定窗口 / 滑动窗口 / 滑动日志) ✅
隔离模式 4(线程池 / 信号量 / 进程 / 集群) ✅
降级策略 4(自动 / 手动 / 读 / 写) ✅
关键词命中 限流 ✅ / 熔断 ✅ / 降级 ✅ / Sentinel ✅ / Hystrix ✅ / Resilience4j ✅ / 令牌桶 ✅ / 漏桶 ✅ / 熔断器 ✅ / 舱壁 ✅
Checklist 12 项防护 + 10 项监控 ✅
速查表 5 个 ✅
选型口诀 3 句话 ✅
说明 · 本站内容均为学习笔记与经验总结,所有菜谱与技法请结合实际食材、季节与个人口味灵活调整。涉及生食、营养与健康的内容仅供参考,特殊体质或疾病请咨询专业营养师/医生。