← 返回 Day 195 正文

Day 195 · 图解

Day 195 图解 — 重试、加密、交付

Mermaid 源码可直接粘到支持 Mermaid 的 Markdown 渲染器;ASCII 图用于终端阅读。

1. 重试状态机

stateDiagram-v2 [*] --> Call Call --> Success: 2xx Call --> Classify: 异常 / 非 2xx Classify --> FailFast: 401/403/4xx/未知 Classify --> Wait: 5xx/超时/429 Wait --> Call: attempt < max 且未超预算 Wait --> GiveUp: 次数或预算耗尽 Success --> [*] FailFast --> [*] GiveUp --> [*]
stateDiagram-v2
    [*] --> Call
    Call --> Success: 2xx
    Call --> Classify: 异常 / 非 2xx
    Classify --> FailFast: 401/403/4xx/未知
    Classify --> Wait: 5xx/超时/429
    Wait --> Call: attempt < max 且未超预算
    Wait --> GiveUp: 次数或预算耗尽
    Success --> [*]
    FailFast --> [*]
    GiveUp --> [*]

ASCII 时间线(实测 01 脚本,base=0.1s,factor=2,full jitter):

attempt 1 ──503──▶ sleep 0.079s
attempt 2 ──503──▶ sleep 0.200s   (Retry-After 下限)
attempt 3 ──200──▶ done            总耗时 0.313s

2. 抖动如何削平重试洪峰

无抖动(500 客户端)               full jitter
│██████████████████│ 500           │█│█│█│█│█│█│█│█│ 峰值 5
└── 同一 10ms 桶 ──┘               └── 分散到整个窗口 ──┘

3. 信封密文布局

 v1. | kid_len | kid | nonce(12B) | ciphertext + tag(16B)
 ─┬─   ─┬──     ─┬─   ───┬─────     ─────────┬─────────
  │     │        │        │                   │
  │     │        │        └─ 随机,绝不复用    └─ AES-256-GCM 输出
  │     │        └─ 密钥 ID,支持轮换
  │     └─ 1 字节长度
  └─ 版本前缀(base64url 整体编码)

AAD(例如 record_id)不进密文,但参与认证:
密文从 row-42 复制到 row-43 → tag 校验失败 → InvalidTag

4. 请求签名与校验顺序

sequenceDiagram participant C as Client participant S as Server C->>C: canonical = METHOD/PATH/TS/NONCE/SHA256(body) C->>C: sig = HMAC-SHA256(secret, canonical) C->>S: headers + body S->>S: 1. |now-ts| <= 300 ? S->>S: 2. compare_digest(sig, expected) S->>S: 3. nonce 未用过 ? alt 全部通过 S-->>C: 200 else 时间/签名/nonce 失败 S-->>C: 401 / 409 end
sequenceDiagram
    participant C as Client
    participant S as Server
    C->>C: canonical = METHOD/PATH/TS/NONCE/SHA256(body)
    C->>C: sig = HMAC-SHA256(secret, canonical)
    C->>S: headers + body
    S->>S: 1. |now-ts| <= 300 ?
    S->>S: 2. compare_digest(sig, expected)
    S->>S: 3. nonce 未用过 ?
    alt 全部通过
        S-->>C: 200
    else 时间/签名/nonce 失败
        S-->>C: 401 / 409
    end

5. 部署与自动回滚

flowchart TD A[记录 PREV_REF] --> B[构建新镜像] B --> C[停旧容器 / 起新容器] C --> D{HEALTHCHECK 通过?} D -- 是 --> E[跑 smoke_test] D -- 否 --> R[回滚 PREV_REF] E -- 通过 --> F[部署成功] E -- 失败 --> R R --> G{旧版本健康?} G -- 是 --> H[退出码 1,人工介入] G -- 否 --> I[退出码 2,紧急告警]
flowchart TD
    A[记录 PREV_REF] --> B[构建新镜像]
    B --> C[停旧容器 / 起新容器]
    C --> D{HEALTHCHECK 通过?}
    D -- 是 --> E[跑 smoke_test]
    D -- 否 --> R[回滚 PREV_REF]
    E -- 通过 --> F[部署成功]
    E -- 失败 --> R
    R --> G{旧版本健康?}
    G -- 是 --> H[退出码 1,人工介入]
    G -- 否 --> I[退出码 2,紧急告警]

6. liveness vs readiness

            ┌──────────────┐
  probe ───▶│  /healthz    │ 进程活着? → 失败:重启容器
            └──────────────┘
            ┌──────────────┐
  probe ───▶│  /readyz     │ 能写库 + 有密钥? → 失败:摘掉流量
            └──────────────┘

合并成一个的后果:数据库抖 10 秒 → 所有副本被重启 → 雪崩

7. 告警收敛时间轴

t=0s      error █  → 发第 1 条(suppressed=0)
t=0.6s    error █  → 抑制
...
t=59.4s   error █  → 抑制(共 99 条)
t=60s     error █  → 发第 2 条(suppressed=99)