diff --git a/hzh/MS/02-服务治理/容错模式/README.md b/hzh/MS/02-服务治理/容错模式/README.md
index 3da78d0..e3afc12 100644
--- a/hzh/MS/02-服务治理/容错模式/README.md
+++ b/hzh/MS/02-服务治理/容错模式/README.md
@@ -183,6 +183,259 @@ flowchart LR
| **令牌桶** | 允许一定突发,平均速率受限 | API 网关通用方案 |
| **漏桶** | 强制匀速输出,不允突发 | 防止下游被打满 |
+### 令牌桶 (Token Bucket)
+
+令牌桶的核心思想:**以恒定速率往一个"桶"里放令牌,请求到来时从桶中取令牌,取到则放行,取不到则拒绝或等待。**
+
+```mermaid
+flowchart LR
+ subgraph "🪣 令牌桶内部"
+ BUCKET[令牌桶
容量 = maxBurst]
+ REFILL[⚡ 固定速率 r/s 添加令牌
最多填到 maxBurst]
+ end
+
+ REQ[请求到达] --> CHECK{桶中有令牌?}
+ CHECK -- 是 --> CONSUME["消耗 1 个令牌
✅ 请求放行"]
+ CHECK -- 否 --> DROP["❌ 请求被拒绝 / 排队等待"]
+
+ REFILL -.-> BUCKET
+ CONSUME -.->|"桶 -1"| BUCKET
+```
+
+**工作流程:**
+
+1. 初始状态:桶中有 `maxBurst` 个令牌(满桶)
+2. 以固定速率 `r`(如 100/s)不断往桶中添加令牌,但总量不超过桶容量
+3. 每个请求到来时尝试获取 N 个令牌(通常 N=1)
+4. 有足够令牌 → 消费后放行;不足 → 拒绝或阻塞
+5. **关键特性:桶未满的令牌会累积**,这使得在一段时间空闲后突然来了大量请求时,桶里有足够的令牌可以应对瞬时突发流量
+
+**为什么允许突发?**
+
+假设 `rate = 100/s`, `maxBurst = 200`:
+
+```
+时间线(每秒) 桶中令牌数 行为
+───────────────── ─────────── ─────────────
+t=0s 200 初始满桶
+t=1s 300→200 理论上应该增加到300,但桶满了,上限200
+t=2s ~ t=9s 100 稳定在 100(每秒消耗 ≈ 每秒生成)
+t=10s 200 系统空闲,桶重新蓄满
+t=11s 0 ⚡ 瞬间涌入 200 个请求全部通过(突发!)
+t=12s 0 后续只有 100 个能通过(恢复限速率)
+```
+
+这就是为什么令牌桶适合 API 网关——用户可能积攒了多个请求一口气发过来,直接全部拒绝体验很差;允许合理范围内的突发,用户体验更好。
+
+**Go 实现:**
+
+```go
+type TokenBucket struct {
+ mu sync.Mutex
+ tokens float64 // 当前令牌数
+ maxBurst float64 // 桶容量
+ rate float64 // 补充速率(令牌/秒)
+ lastRefill time.Time // 上次补充令牌的时间
+}
+
+func NewTokenBucket(rate, maxBurst float64) *TokenBucket {
+ return &TokenBucket{
+ tokens: maxBurst, // 初始满桶
+ maxBurst: maxBurst,
+ rate: rate,
+ lastRefill: time.Now(),
+ }
+}
+
+// Allow 检查是否可以通行(非阻塞)
+func (tb *TokenBucket) Allow() bool {
+ tb.mu.Lock()
+ defer tb.mu.Unlock()
+
+ now := time.Now()
+ elapsed := now.Sub(tb.lastRefill).Seconds()
+
+ // 根据经过的时间补充令牌
+ tb.tokens += elapsed * tb.rate
+ if tb.tokens > tb.maxBurst {
+ tb.tokens = tb.maxBurst // 不能超过桶容量
+ }
+ tb.lastRefill = now
+
+ // 尝试消费一个令牌
+ if tb.tokens >= 1 {
+ tb.tokens--
+ return true
+ }
+ return false
+}
+
+// Wait 阻塞等待直到获取到令牌
+func (tb *TokenBucket) Wait(ctx context.Context) error {
+ tb.mu.Lock()
+ defer tb.mu.Unlock()
+
+ for {
+ if tb.Allow() {
+ return nil
+ }
+ // 计算还需要等多久才有令牌
+ waitDuration := time.Duration((1-tb.tokens)/tb.rate) * time.Second
+ tb.mu.Unlock()
+
+ select {
+ case <-time.After(waitDuration):
+ tb.mu.Lock()
+ case <-ctx.Done():
+ return ctx.Err()
+ }
+ }
+}
+```
+
+---
+
+### 漏桶 (Leaky Bucket)
+
+漏桶的核心思想:**请求像水一样流入桶中,桶以固定的速率向下漏水,漏完才能接收新水。如果桶满了,新来的水就溢出丢弃。**
+
+```mermaid
+flowchart TD
+ REQ["💧 请求源源不断流入"] --> QUEUE[🪣 漏桶
队列缓冲区]
+ QUEUE --> PROCESS["🚰 以固定速率 drainer 处理
不管上游多快,只匀速处理"]
+
+ QUEUE --> FULL{"桶满了?"}
+ FULL -- 是 --> DROP["💨 丢弃新请求 / 返回 429"]
+
+ style QUEUE fill:#e3f2fd
+ style PROCESS fill:#c8e6c9
+ style DROP fill:#ffebee
+```
+
+**工作流程:**
+
+1. 桶有一个固定容量 `capacity`
+2. 请求到达时进入桶(相当于往桶里倒水)
+3. 处理单元以恒定速率 `rate` 取出并处理请求(相当于底部有个小孔一直在漏水)
+4. 如果请求到达速度超过处理速度,桶会被逐渐填满
+5. **桶满时,新来的请求直接被拒绝**
+
+**关键特性对比令牌桶:**
+
+```
+场景:突发流量 500qps 涌入,限流配置 rate=100/s, capacity=200
+
+令牌桶视角: 漏桶视角:
+┌──────────────┐ ┌──────────────┐
+│ ✅前200个通过 │ ← 桶里够令牌 │ ❌桶满了拒绝 │ ← 已经满了
+│ ⏱后面100个/秒 │ ← 恢复限速率 │ ──────────── │
+│ ❌多余的被拒 │ │ ✅匀速处理100/s│ ← 底部漏水
+└──────────────┘ └──────────────┘
+ ↑
+ 无论上游来多少,
+ 下游只看到匀速的100/s
+```
+
+| 维度 | 令牌桶 | 漏桶 |
+|------|--------|------|
+| **突发容忍** | ✅ 允许(空闲时令牌会累积) | ❌ 不允许(直接拒绝或排队) |
+| **输出形态** | 输入决定输出节奏 | 始终匀速输出 |
+| **队列语义** | 无队列(拒绝或立即执行) | 隐含队列(请求先进入桶再被处理) |
+| **保护对象** | 对客户端更友好 | 对下游处理单元更安全 |
+| **实现复杂度** | 需记录剩余令牌和更新时间 | 只需记录当前量和漏出速率 |
+
+**Go 实现:**
+
+```go
+type LeakyBucket struct {
+ mu sync.Mutex
+ water float64 // 当前水量(请求数)
+ capacity float64 // 桶容量
+ rate float64 // 排水速率(请求/秒)
+ lastDrain time.Time // 上次排水时间
+}
+
+func NewLeakyBucket(capacity float64, rate float64) *LeakyBucket {
+ return &LeakyBucket{
+ capacity: capacity,
+ rate: rate,
+ lastDrain: time.Now(),
+ }
+}
+
+// Add 尝试加入请求(非阻塞),已满返回 false
+func (lb *LeakyBucket) Add() bool {
+ lb.mu.Lock()
+ defer lb.mu.Unlock()
+
+ now := time.Now()
+ elapsed := now.Sub(lb.lastDrain).Seconds()
+
+ // 根据时间排出一定量的水
+ lb.water -= elapsed * lb.rate
+ if lb.water < 0 {
+ lb.water = 0 // 不能排成负的
+ }
+ lb.lastDrain = now
+
+ // 加水
+ if lb.water+1 <= lb.capacity {
+ lb.water++
+ return true // 成功放入桶中
+ }
+ return false // 桶满了,拒绝
+}
+
+// DrainLoop 持续的排水循环(实际服务中的处理方式)
+func (lb *LeakyBucket) DrainLoop(ctx context.Context) {
+ ticker := time.NewTicker(100 * time.Millisecond) // 每 100ms 排水一次
+ defer ticker.Stop()
+
+ for {
+ select {
+ case <-ctx.Done():
+ return
+ case <-ticker.C:
+ lb.mu.Lock()
+ drainAmount := lb.rate * 0.1 // 100ms 内可处理的量
+ lb.water -= drainAmount
+ if lb.water < 0 {
+ lb.water = 0
+ }
+ // 这里执行实际的处理逻辑
+ // processNextRequest()
+ lb.mu.Unlock()
+ }
+ }
+}
+```
+
+---
+
+### 如何选择?
+
+```mermaid
+flowchart TD
+ Choice["如何选?"] --> Q1{"是否需要支持突发流量?"}
+
+ Q1 -- 是 --> TB["选令牌桶 ✅"]
+ Q1 -- 否 --> LB["选漏桶 ✅"]
+
+ TB --> UseCases1["典型场景:
• REST API 限流
• 第三方调用配额
• 用户体验优先的网关"]
+
+ LB --> UseCases2["典型场景:
• DB 连接池限流
• 消息队列消费者速率
• 防抖/削峰压后端"]
+
+ style TB fill:#e8f5e9
+ style LB fill:#fff3e0
+ style UseCases1 fill:#e8f5e9
+ style UseCases2 fill:#fff3e0
+```
+
+> [!tip] 工程实践建议
+> - **生产环境推荐使用成熟库**:如 Go 的 `uber-go/ratelimit`(基于令牌桶)、Redis 的 `LIMIT` 参数配合 `XREADGROUP`(漏桶模型)
+> - **Redis + Lua 脚本**是实现分布式令牌桶的最常见方案,保证多实例下的限流一致性
+> - 很多云厂商的 API 网关默认采用令牌桶算法,因为它在限制平均速率的同时给了客户端更好的吞吐体验
+
### 限流层级
```mermaid
diff --git a/hzh/TEST/MicroService.md b/hzh/TEST/MicroService.md
index 5b1f156..7c88daf 100644
--- a/hzh/TEST/MicroService.md
+++ b/hzh/TEST/MicroService.md
@@ -492,6 +492,13 @@ D. GitOps 不需要容器镜像,直接在 Git 里存储二进制文件
请将 Prometheus 的四种基础指标类型与其用途连线:
+| 选项 | 指标类型 | 用途描述 |
+| ----- | --------- | -------------------------------- |
+| **A** | Counter | 单调递增,只能增加或重置为零(如总请求数) |
+| **B** | Summary | 采样+分桶,客户端计算百分位(SDK 内部聚合) |
+| **C** | Histogram | 采样+分桶,服务端用 PromQL 计算百分位(如请求耗时分布) |
+| **D** | Gauge | 可上可下,表示瞬时值(如内存使用量、在线人数) |
+
> [!tip]- Q13 答案
> **Counter = A(单调递增);Gauge = D(可上可下);Histogram = C(分布统计);Summary = B(客户端计算分位数)**
>