Skip to content
Gains Summary
Main Navigation 首页 / Home
C++ 编程 / C++ Programming
系统与高性能 / Systems & Performance
Web 开发 / Web Development
人工智能 / Artificial Intelligence
工业软件 / Industrial Software
其他内容 / Other Topics
C++ 编程 / C++系统与性能 / SystemsWeb 开发 / Web人工智能 / AI工业软件 / Industrial

外观

Sidebar Navigation

← Web 开发 / Web Development

中间件 / Middleware

1. Web 中间件全景 / The Web Middleware Landscape

2. 中间件——流量洪峰下的系统保护 / Middleware for Protecting Systems Under Traffic Spikes

cache layer

1. 缓存架构全景 / Cache Architecture Overview

2. Redis 深入——为什么你的缓存总是出问题 / Redis Internals and Cache Failure Modes

3. Redis 数据结构场景应用——什么时候用什么 / Choosing Redis Data Structures for Real Applications

4. 缓存策略——库存变了,缓存怎么处理 / Cache Strategies and Inventory Consistency

5. Redis 缓存三剑客——穿透/击穿/雪崩 + 一致性策略 / Redis Cache Penetration, Breakdown, Avalanche, and Consistency

6. Redis 分布式锁——从 SETNX 到 Redisson / Redis Distributed Locks from SETNX to Redisson

7. Redis Cluster 与 Sentinel 高可用架构 / Redis Cluster and Sentinel High-Availability Architecture

8. Redis 高级特性——Stream / PubSub / Module / LLM 应用

9. Redis 架构深度分析——为什么 Redis 能这么快 / Redis Architecture and the Sources of Its Performance

10. Redis 全景——为什么你的系统需要一个缓存层 / The Redis Landscape and Why Systems Need a Cache Layer

message queue

1. 消息队列全景——为什么你的系统需要一个中间人 / Message Queue Overview and Why Your System Needs a Middleman

2. Kafka 核心——为什么你的消息总是"丢"了 / Kafka Fundamentals and Message Delivery Semantics

3. 消息队列高级——死信队列、延迟消息、消息积压 / Advanced Messaging with Dead Letters, Delays, and Backlogs

4. Kafka Streams 与 Connect —— 让数据自己流动起来 / Kafka Streams and Connect for Streaming Data Pipelines

5. RabbitMQ 深度解析 —— 灵活路由与消息可靠性 / RabbitMQ Deep Dive into Flexible Routing and Reliability

6. 消息队列对比——为什么最终选了 Kafka / Comparing Message Queues and Choosing Kafka

search engine

1. 搜索引擎知识体系 / Search Engine Knowledge System

2. Elasticsearch——为什么 Like 查询总是那么慢 / Elasticsearch for Full-Text Search at Scale

3. Elasticsearch 查询 DSL 深入——为什么你的搜索总是不准 / Elasticsearch Query DSL Deep Dive

4. Elasticsearch 集群规划与运维——为什么你的集群总是"黄" / Elasticsearch Cluster Planning and Operations

5. Meilisearch 与轻量搜索替代方案——当 ES 太重时 / Meilisearch and Lightweight Search Alternatives

infrastructure

1. 基础设施组件全景 / Infrastructure Components Landscape

2. Nginx 与反向代理 / Nginx and Reverse Proxy

3. 服务发现 / Service Discovery

4. 配置中心 / Configuration Center

本页目录

中间件——流量洪峰下的系统保护 / Middleware for Protecting Systems Under Traffic Spikes ​

📅 创建时间:2026-05-08 🏷️ 标签:#API网关 #限流 #熔断 #令牌桶 #链路追踪 #服务发现 📚 前置知识:[[00-backend-overview]] [[01-redis-deep]](Redis 限流) 📚 相关知识:[[06-network]](TCP 连接数) [[08-concurrency]](线程池)


场景:双十一零点,你的系统被自己的流量打垮了 ​

┌─────────────────────────────────────────────────────────────┐
│                                                             │
│  双十一零点,10 万用户同时涌进。                          │
│                                                             │
│  用户体验:                                               │
│  前端页面:正常                                          │
│  API 网关:正常                                          │
│  用户服务:正常                                          │
│  商品服务:正常                                          │
│  订单服务:❌ 超时                                        │
│                                                             │
│  运营后台:                                             │
│  发现所有服务正常,只有订单服务超时                        │
│                                                             │
│  为什么?                                                │
│  → 10 万请求同时涌入订单服务                             │
│  → 订单服务线程池耗尽                                    │
│  → 后续请求全部排队 → 超时                                │
│                                                             │
│  雪上加霜:                                             │
│  → 超时的请求没有快速失败                                │
│  → 它们还占用着线程资源(等待)                          │
│  → 订单服务越来越慢 → 最终崩溃                          │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

这一章,我们理解如何用中间件保护系统:限流(控制流量)、熔断(快速失败)、网关(统一入口)、链路追踪(问题定位)。


第1节:限流——为什么你需要"拒绝服务" ​

问题抽象 ​

餐厅有 20 张桌子,门口排了 100 人。

方案 A(无限制):100 人全部进去 → 挤爆 → 所有人体验极差
方案 B(限流):门口只放 20 人 → 20 人正常用餐 → 剩下 80 人排队

但方案 B 也有问题:排队的 80 人不知道要等多久。
→ 需要告诉用户"你是第几号,预计等待 X 分钟"
1
2
3
4
5

推演:限流的三个维度 ​

┌─────────────────────────────────────────────────────────────┐
│              限流的三个维度                                │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1. 按谁限流:                                            │
│  • 用户维度:每个用户每分钟最多 100 请求                   │
│  • IP 维度:每个 IP 每分钟最多 200 请求                   │
│  • 接口维度:/order 每秒最多 1000 请求                    │
│  • 全局维度:整个系统每秒最多 10 万请求                   │
│                                                             │
│  2. 按什么限流:                                          │
│  • QPS(每秒请求数)                                      │
│  • 并发数(同时处理的请求数)                              │
│  • 连接数(TCP 连接数)                                    │
│                                                             │
│  3. 限流后怎么办:                                        │
│  • 直接拒绝(返回 429 Too Many Requests)                  │
│  • 排队等待(返回"排队中,预计 X 分钟")                  │
│  • 降级处理(返回默认响应,不调下游服务)                  │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

第2节:限流算法——从计数器到令牌桶 ​

算法一:固定窗口计数器(最简单,有缺陷) ​

┌─────────────────────────────────────────────────────────────┐
│              固定窗口计数器                                 │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  窗口:1 分钟                                             │
│  限制:100 次/分钟                                        │
│                                                             │
│  0:00-1:00: 100 次请求(已满)                          │
│  1:00-1:01: 又来了 100 次(窗口重置,重新计数)          │
│                                                             │
│  问题:临界突变                                            │
│  0:59: 来了一波 100 次请求                               │
│  1:00: 窗口重置,新的一波 100 次请求                     │
│  → 1 秒内处理了 200 个请求(超出预期!)                  │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

算法二:滑动窗口(更精确,但实现复杂) ​

┌─────────────────────────────────────────────────────────────┐
│              滑动窗口算法                                   │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  将 1 分钟分成 6 个 10 秒窗口                             │
│                                                             │
│  当前时刻 1:05,过去 6 个窗口的请求数:                  │
│  [1:00] [1:01] [1:02] [1:03] [1:04] [1:05]             │
│     10      20      15      18      12      10           │
│  总计:85 < 100,允许通过                                  │
│                                                             │
│  当前时刻 1:10,过去 6 个窗口的请求数:                  │
│  [1:05] [1:06] [1:07] [1:08] [1:09] [1:10]             │
│     15      20      20      20      20      15           │
│  总计:110 > 100,限流                                    │
│                                                             │
│  优点:没有临界突变                                       │
│  缺点:需要维护 6 个窗口的状态                             │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

算法三:漏桶(让流量平滑) ​

┌─────────────────────────────────────────────────────────────┐
│              漏桶算法                                       │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│       ┌──────────────────────────────┐                    │
│       │                              │                    │
│       │           桶                │                    │
│       │  [请求][请求][请求]...       │                    │
│       │         ↓ 漏出(固定速率)  │                    │
│       └──────────────────────────────┘                    │
│                    ↓                                       │
│               消费者(固定处理速度)                       │
│                                                             │
│  特点:                                                    │
│  • 无论上游来多少请求,下游都以固定速率处理               │
│  • 桶满了,请求被丢弃                                     │
│  • 流量平滑,但突发请求无法加速处理                        │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

算法四:令牌桶(推荐,同时处理突发和限流) ​

┌─────────────────────────────────────────────────────────────┐
│              令牌桶算法(推荐)                            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│       ┌──────────────────────────────┐                    │
│       │                              │                    │
│       │           桶                │ ← 容量 = burst    │
│       │         (令牌)              │   burst = 允许的突发量 │
│       │       以固定速率放入         │   令牌放入速率 = qps  │
│       └──────────────────────────────┘                    │
│                    ↓                                       │
│       请求来了 → 必须拿到令牌才能处理                     │
│       拿到令牌 → 处理请求 → 令牌消耗                     │
│       没令牌   → 请求被限流                               │
│                                                             │
│  举例:                                                   │
│  桶容量 = 100(burst)                                    │
│  令牌放入速率 = 10/秒                                     │
│                                                             │
│  平时:每秒处理 10 个请求                                 │
│  突发:一次性可以处理 100 个请求(桶里有 100 个令牌)     │
│  突发后:需要等令牌慢慢补充                               │
│                                                             │
│  优点:                                                  │
│  ✅ 允许一定的突发流量(用户体验更好)                     │
│  ✅ 平滑限流(令牌补充是均匀的)                         │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

令牌桶 Python 实现 ​

python
import time
import threading
import math

class TokenBucket:
    def __init__(self, qps=100, burst=200):
        self.qps = qps
        self.burst = burst
        self.tokens = burst  # 初始令牌数
        self.last_update = time.time()
        self.lock = threading.Lock()

    def acquire(self, tokens=1, blocking=False):
        with self.lock:
            now = time.time()
            # 补充令牌
            elapsed = now - self.last_update
            self.tokens = min(
                self.burst,
                self.tokens + elapsed * self.qps
            )
            self.last_update = now

            if self.tokens >= tokens:
                self.tokens -= tokens
                return True  # 获取到令牌
            else:
                if blocking:
                    wait_time = (tokens - self.tokens) / self.qps
                    time.sleep(wait_time)
                    self.tokens = min(self.burst, self.tokens + wait_time * self.qps)
                    return True
                return False  # 令牌不足

# 使用
limiter = TokenBucket(qps=100, burst=200)

@app.route("/order")
def create_order():
    if not limiter.acquire():
        return jsonify({"error": "Too Many Requests"}), 429

    # 处理订单...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

限流实战:Sentinel(Spring Cloud Alibaba) ​

java
// Sentinel:阿里的限流降级框架(比 Hystrix 更轻量)
@Configuration
public class SentinelConfig {

    @Bean
    public SentinelResourceHolder sentinelResourceHolder() {
        return new SentinelResourceHolder();
    }
}

// 使用注解:限流处理
@SentinelResource(value = "createOrder",
    blockHandler = "createOrderBlockHandler",
    fallback = "createOrderFallback")
public Result createOrder(OrderDTO dto) {
    // 业务逻辑
    return orderService.create(dto);
}

// 限流后的处理(快速失败)
public Result createOrderBlockHandler(OrderDTO dto, BlockException e) {
    return Result.fail(429, "系统繁忙,请稍后再试");
}

// 降级后的处理(服务不可用时的兜底)
public Result createOrderFallback(OrderDTO dto, Throwable e) {
    return Result.fail(503, "服务降级,请稍后再试");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

第3节:熔断——如何让故障不蔓延 ​

场景:订单服务依赖库存服务,但库存服务挂了 ​

┌─────────────────────────────────────────────────────────────┐
│                                                             │
│  订单服务 ──▶ 库存服务                                    │
│                                                             │
│  库存服务挂了:                                           │
│  → 订单服务调用库存服务 → 超时                            │
│  → 订单服务线程池等待 → 积压                              │
│  → 订单服务越来越慢                                       │
│  → 订单服务也挂了!                                       │
│                                                             │
│  连锁反应:一个服务挂了 → 拖垮了另一个服务 → 整个系统崩溃  │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13

推演:熔断器的三个状态 ​

┌─────────────────────────────────────────────────────────────┐
│              熔断器三状态                                   │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  状态 1:Closed(关闭)                                   │
│  ┌──────────────────────────────────────────────────────┐ │
│  │ 正常:请求通过 → 统计成功/失败                        │ │
│  │ 失败率 < 阈值 → 继续关闭                              │ │
│  │ 失败率 > 阈值 → 跳闸!进入 Open                      │ │
│  └──────────────────────────────────────────────────────┘ │
│                                                             │
│  状态 2:Open(打开)                                     │
│  ┌──────────────────────────────────────────────────────┐ │
│  │ 熔断:所有请求直接失败(快速失败)                    │ │
│  │ 不调用下游服务 → 保护下游服务                        │ │
│  │ 等待一段时间 → 进入 Half-Open                        │ │
│  └──────────────────────────────────────────────────────┘ │
│                                                             │
│  状态 3:Half-Open(半开)                                │
│  ┌──────────────────────────────────────────────────────┐ │
│  │ 试探:放行一个请求                                    │ │
│  │ 成功 → 关闭熔断器,恢复正常                           │ │
│  │ 失败 → 重新打开熔断器                                 │ │
│  └──────────────────────────────────────────────────────┘ │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

熔断 vs 限流的区别 ​

┌─────────────────────────────────────────────────────────────┐
│              熔断 vs 限流的区别                             │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  限流:保护系统不被流量打垮                                │
│  → 流量是正常的,但系统处理不过来                          │
│  → 拒绝部分请求,但不是服务不可用                          │
│                                                             │
│  熔断:保护系统不被下游故障拖垮                            │
│  → 流量正常,但下游服务有问题                              │
│  → 主动拒绝请求,让下游服务喘口气                          │
│                                                             │
│  两者配合:                                               │
│  限流(流量控制) + 熔断(故障隔离)                      │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

第4节:API 网关——统一的流量入口 ​

场景:多个服务,怎么统一鉴权/限流/路由? ​

没有网关:
用户 → 用户服务(自己鉴权)
用户 → 商品服务(自己鉴权)
用户 → 订单服务(自己鉴权)

每个服务都要写一套鉴权/限流/路由代码。
新加一个服务 → 要复制粘贴 3 份。

有网关:
用户 → API 网关(统一处理)→ 各服务
1
2
3
4
5
6
7
8
9
10

网关的核心职责 ​

┌─────────────────────────────────────────────────────────────┐
│                 API 网关职责                               │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1. 路由转发                                              │
│  /api/users/** → 用户服务                                  │
│  /api/products/** → 商品服务                              │
│  /api/orders/** → 订单服务                                │
│                                                             │
│  2. 统一鉴权                                              │
│  → 验证 JWT Token                                          │
│  → 验证 API Key                                            │
│  → 验证 IP 黑名单                                          │
│                                                             │
│  3. 统一限流                                              │
│  → 按用户限流                                             │
│  → 按接口限流                                             │
│  → 按 IP 限流                                             │
│                                                             │
│  4. 协议转换                                              │
│  → HTTP → Dubbo / gRPC(内部通信)                         │
│  → REST → GraphQL                                          │
│                                                             │
│  5. 统一日志/监控                                         │
│  → 请求日志                                               │
│  → 调用链追踪(OpenTelemetry)                              │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

Kong 网关实战配置 ​

yaml
# Kong 配置:通过声明式 YAML 部署
services:
  - name: order-service
    url: http://order-service:8080
    routes:
      - name: order-route
        paths:
          - /api/orders
        methods:
          - GET
          - POST
    plugins:
      # JWT 鉴权
      - name: jwt
        config:
          key_claim_name: kid
      # 限流(按用户,每分钟 100 次)
      - name: rate-limiting
        config:
          minute: 100
          policy: redis
          redis_host: redis
          hide_client_headers: false
      # CORS
      - name: cors

consumers:
  - username: user_1001
    jwt_secrets:
      - secret: my-secret-key
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

第5节:链路追踪——为什么问题总是难定位 ​

场景:订单接口超时,怎么快速定位是哪个环节慢? ​

┌─────────────────────────────────────────────────────────────┐
│                                                             │
│  订单接口耗时 5 秒。                                      │
│                                                             │
│  是 MySQL 慢?                                           │
│  是 Redis 慢?                                           │
│  是库存服务慢?                                         │
│  是支付服务慢?                                         │
│                                                             │
│  没有任何追踪工具 → 一个个排查 → 花了 2 小时              │
│                                                             │
│  有链路追踪 → 看到每个环节的耗时 → 1 分钟定位问题        │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14

OpenTelemetry 链路追踪原理 ​

┌─────────────────────────────────────────────────────────────┐
│              OpenTelemetry 链路追踪                        │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Trace(一次请求的完整链路):                             │
│                                                             │
│  Span A(订单服务)                                        │
│  ├── Span B(查询用户)耗时 5ms                          │
│  ├── Span C(查询商品)耗时 10ms                         │
│  ├── Span D(调用库存服务)耗时 3s ← 问题在这里!       │
│  │   ├── Span D1(HTTP)                               │
│  │   └── Span D2(DB 查询)                            │
│  └── Span E(创建订单)耗时 8ms                          │
│                                                             │
│  Trace ID:贯穿整个请求的唯一 ID                          │
│  Parent ID:谁调用了我                                     │
│  Span ID:我自己的唯一 ID                                  │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐  │
│  │  HTTP Header 传递 Trace ID:                       │  │
│  │                                                     │  │
│  │  Header: traceparent: 00-{TraceID}-{SpanID}-01    │  │
│  │                                                     │  │
│  │  网关生成 TraceID → 传递给下游 → 所有服务共享     │  │
│  └─────────────────────────────────────────────────────┘  │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

升华:系统保护的分层策略 ​

┌─────────────────────────────────────────────────────────────┐
│              系统保护分层策略                               │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  第一层:限流(入口控制)                                  │
│  → 网关层:总 QPS 限制                                    │
│                                                             │
│  第二层:熔断(故障隔离)                                  │
│  → 服务间调用:当下游故障时快速失败                        │
│                                                             │
│  第三层:降级(兜底策略)                                  │
│  → 服务不可用时,返回默认响应或缓存数据                    │
│                                                             │
│  第四层:隔离(资源隔离)                                  │
│  → 线程池隔离:不同服务用不同线程池                        │
│  → 舱壁模式:一个服务耗尽资源不影响其他                    │
│                                                             │
│  一句话总结:                                              │
│  限流是"不要来太多",熔断是"它病了别找它",              │
│  降级是"它病了给个替代方案",隔离是"别让它们互相传染"。  │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

"AI 可查 vs 必须理解"清单 ​

AI 可查:
✅ Sentinel / Hystrix / Resilience4j 的具体配置参数
✅ Kong / APISIX / Spring Cloud Gateway 的路由配置语法
✅ OpenTelemetry 的埋点代码

必须理解:
🔴 令牌桶的 burst 参数含义(突发流量处理能力)
🔴 熔断器三状态的转换条件
🔴 限流和熔断的使用场景区别
🔴 为什么需要链路追踪(快速定位问题)
1
2
3
4
5
6
7
8
9
10

学习状态:🟡 开始学习

最后更新于:

Pager
上一篇1. Web 中间件全景 / The Web Middleware Landscape
下一篇1. 缓存架构全景 / Cache Architecture Overview

持续记录,持续成长

Copyright © Tidenflow