Go 后端高并发架构:从外到内的立体防御体系

文档结构(7 大章节,约 1200 行)

层级 章节 核心内容
最外层 二、边缘拦截与防御 CDN 缓存策略(Cache-Control头)、Nginx 令牌桶限流(limit_req/limit_conn)、WAF 防注入防爬、IP 自动封禁、请求签名/PoW 挑战
中层 三、缓冲隔离与降级 微服务核心/非核心隔离、自适应协程池 (信号量+扩缩容)、三级缓存(L1本地+L2 Redis+L3 DB)、singleflight 热点保护 、缓存三问题防护、MQ 异步削峰、熔断器(Closed→Open→HalfOpen)
底层 四、一致性与保底 DB 限流令牌桶、3种幂等方案 (唯一约束/Token/参数哈希)、乐观锁+重试 、Redis 分布式锁+DB行锁、本地消息表+定时补偿 (指数退避)、Saga 编排(正向+逆向补偿)

额外覆盖

  • 全链路监控:Prometheus 指标定义、各层流量上报
  • 压测规划:并发压测代码示例、单 Pod 容量公式
  • 落地 CheckList:40+ 条生产环境检查项

设计哲学

scss 复制代码
边缘 (CDN/Nginx/WAF):10000 QPS → 3000 QPS
缓冲 (Redis/MQ/熔断):3000 QPS  → 300 QPS
保底 (幂等/乐观锁/补偿):300 QPS → 安全落地 DB

所有 Go 代码示例均可直接参考落地,包含完整的中间件、连接池、协程池、熔断器、幂等管理器等实现。

Go 后端高并发架构:从外到内的立体防御体系

核心思路:高并发不是某一个组件能解决的,需要从外到内、层层设防,形成纵深防御体系。每一层让流量更平滑、更少、更安全地落到数据库。


目录

  • 一、立体防御全景图
  • 二、最外层:边缘拦截与防御
    • [2.1 CDN 静态资源缓存](#2.1 CDN 静态资源缓存 "#21-cdn-%E9%9D%99%E6%80%81%E8%B5%84%E6%BA%90%E7%BC%93%E5%AD%98")
    • [2.2 Nginx 网关限流](#2.2 Nginx 网关限流 "#22-nginx-%E7%BD%91%E5%85%B3%E9%99%90%E6%B5%81")
    • [2.3 WAF 与防爬虫](#2.3 WAF 与防爬虫 "#23-waf-%E4%B8%8E%E9%98%B2%E7%88%AC%E8%99%AB")
    • [2.4 黑名单与 IP 封禁](#2.4 黑名单与 IP 封禁 "#24-%E9%BB%91%E5%90%8D%E5%8D%95%E4%B8%8E-ip-%E5%B0%81%E7%A6%81")
  • 三、中层:缓冲、隔离与降级
    • [3.1 微服务架构拆分](#3.1 微服务架构拆分 "#31-%E5%BE%AE%E6%9C%8D%E5%8A%A1%E6%9E%B6%E6%9E%84%E6%8B%86%E5%88%86")
    • [3.2 核心与非核心链路隔离](#3.2 核心与非核心链路隔离 "#32-%E6%A0%B8%E5%BF%83%E4%B8%8E%E9%9D%9E%E6%A0%B8%E5%BF%83%E9%93%BE%E8%B7%AF%E9%9A%94%E7%A6%BB")
    • [3.3 协程池隔离与并发控制](#3.3 协程池隔离与并发控制 "#33-%E5%8D%8F%E7%A8%8B%E6%B1%A0%E9%9A%94%E7%A6%BB%E4%B8%8E%E5%B9%B6%E5%8F%91%E6%8E%A7%E5%88%B6")
    • [3.4 Redis 抗读并发](#3.4 Redis 抗读并发 "#34-redis-%E6%8A%97%E8%AF%BB%E5%B9%B6%E5%8F%91")
    • [3.5 消息队列抗写并发](#3.5 消息队列抗写并发 "#35-%E6%B6%88%E6%81%AF%E9%98%9F%E5%88%97%E6%8A%97%E5%86%99%E5%B9%B6%E5%8F%91")
    • [3.6 服务熔断与降级](#3.6 服务熔断与降级 "#36-%E6%9C%8D%E5%8A%A1%E7%86%94%E6%96%AD%E4%B8%8E%E9%99%8D%E7%BA%A7")
  • 四、底层:数据一致性与保底
    • [4.1 数据库接入前的流量平滑](#4.1 数据库接入前的流量平滑 "#41-%E6%95%B0%E6%8D%AE%E5%BA%93%E6%8E%A5%E5%85%A5%E5%89%8D%E7%9A%84%E6%B5%81%E9%87%8F%E5%B9%B3%E6%BB%91")
    • [4.2 幂等性设计](#4.2 幂等性设计 "#42-%E5%B9%82%E7%AD%89%E6%80%A7%E8%AE%BE%E8%AE%A1")
    • [4.3 乐观锁与分布式锁](#4.3 乐观锁与分布式锁 "#43-%E4%B9%90%E8%A7%82%E9%94%81%E4%B8%8E%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81")
    • [4.4 补偿机制与最终一致性](#4.4 补偿机制与最终一致性 "#44-%E8%A1%A5%E5%81%BF%E6%9C%BA%E5%88%B6%E4%B8%8E%E6%9C%80%E7%BB%88%E4%B8%80%E8%87%B4%E6%80%A7")
    • [4.5 分布式事务(Saga/TCC)](#4.5 分布式事务(Saga/TCC) "#45-%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1sagatcc")
  • 五、全链路监控与可观测性
  • 六、压测与容量规划
  • [七、完整架构落地 CheckList](#七、完整架构落地 CheckList "#%E4%B8%83%E5%AE%8C%E6%95%B4%E6%9E%B6%E6%9E%84%E8%90%BD%E5%9C%B0-checklist")

一、立体防御全景图

yaml 复制代码
                            ┌──────────────────────────────────────────────┐
                            │              用户请求 (QPS: 10000+)            │
                            └─────────────────────┬────────────────────────┘
                                                  │
    ╔═════════════════════════════════════════════╪═════════════════════════╗
    ║           第一层:最外层拦截 (Edge Defense)    │                        ║
    ║                                              ▼                        ║
    ║  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐             ║
    ║  │  CDN     │  │  WAF     │  │  Nginx   │  │ 网关层    │             ║
    ║  │ 静态资源  │  │ 防爬/CC  │  │ 限流/黑名单│  │ 认证/鉴权  │             ║
    ║  └──────────┘  └──────────┘  └────┬─────┘  └──────────┘             ║
    ╚═══════════════════════════════════╪══════════════════════════════════╝
                                        │ 过滤后 QPS: 3000
    ╔═══════════════════════════════════╪══════════════════════════════════╗
    ║          第二层:中层缓冲 (Buffer)   │                                 ║
    ║                                    ▼                                 ║
    ║  ┌──────────────────────────────────────────────────────────────┐   ║
    ║  │                     API Gateway / BFF                          │   ║
    ║  │  - 统一入口  - 协议转换  - 聚合裁剪  - 限流降级              │   ║
    ║  └───────┬──────────────────┬──────────────────┬─────────────────┘   ║
    ║          │                  │                  │                     ║
    ║          ▼                  ▼                  ▼                     ║
    ║  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐              ║
    ║  │  订单服务     │  │  商品服务     │  │  用户服务     │              ║
    ║  │  (核心链路)   │  │  (核心链路)   │  │  (核心链路)   │              ║
    ║  └──────┬───────┘  └──────┬───────┘  └──────┬───────┘              ║
    ║         │                 │                 │                        ║
    ║  ┌──────┴─────────────────┴─────────────────┴──────┐               ║
    ║  │              Redis Cluster (缓存层)               │               ║
    ║  │  - 热点数据缓存      - 分布式锁                   │               ║
    ║  │  - 限流计数器        - 排行榜/计数器              │               ║
    ║  └──────────────────────────────────────────────────┘               ║
    ║         │                 │                 │                        ║
    ║  ┌──────┴─────────────────┴─────────────────┴──────┐               ║
    ║  │        消息队列 (MQ - 异步削峰)                   │               ║
    ║  │  - RocketMQ / Kafka / RabbitMQ                   │               ║
    ║  │  - 异步写入  - 流量削峰  - 解耦服务               │               ║
    ║  └──────────────────────────────────────────────────┘               ║
    ║         │                                                           ║
    ║         │ 非核心链路                                                ║
    ║         ▼                                                           ║
    ║  ┌──────────────┐  ┌──────────────┐                                ║
    ║  │  日志服务     │  │  通知服务     │  ← 独立协程池隔离               ║
    ║  │  (非核心)    │  │  (非核心)    │                                 ║
    ║  └──────────────┘  └──────────────┘                                ║
    ╚══════════════════════════════════════════════════════════════════════╝
                                        │ 写入 QPS: 300
    ╔═══════════════════════════════════╪══════════════════════════════════╗
    ║          第三层:底层保底 (Consistency)│                               ║
    ║                                    ▼                                 ║
    ║  ┌──────────────────────────────────────────────────────────────┐   ║
    ║  │                   数据库层 (MySQL / TiDB)                      │   ║
    ║  │  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐     │   ║
    ║  │  │ 读写分离  │  │ 分库分表  │  │ 乐观锁    │  │ 幂等设计  │     │   ║
    ║  │  │ (主从)   │  │ (Sharding)│  │ (CAS)    │  │ (防重)   │     │   ║
    ║  │  └──────────┘  └──────────┘  └──────────┘  └──────────┘     │   ║
    ║  │  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐     │   ║
    ║  │  │ 连接池    │  │ 补偿任务  │  │ 死信队列  │  │ 对账脚本  │     │   ║
    ║  │  │ (限流)   │  │ (重试)   │  │ (兜底)   │  │ (修复)   │     │   ║
    ║  │  └──────────┘  └──────────┘  └──────────┘  └──────────┘     │   ║
    ║  └──────────────────────────────────────────────────────────────┘   ║
    ╚══════════════════════════════════════════════════════════════════════╝

设计哲学

yaml 复制代码
最外层 (减少打到服务层的流量)
  ├─ CDN: 缓存静态内容,80%流量在此终结
  ├─ WAF: 拦截恶意请求
  └─ Nginx: 限流、黑名单、协议校验

中层 (保护数据库不被冲垮)
  ├─ Redis: 扛住读流量,热点数据不进DB
  ├─ MQ: 扛住写流量,削峰填谷
  └─ 熔断降级: 核心链路保活

底层 (守护数据最终正确)
  ├─ 幂等: 确保不会重复处理
  ├─ 乐观锁: 确保并发不冲突
  └─ 补偿: 确保最终一致性

二、最外层:边缘拦截与防御

目标:在流量到达应用服务之前,尽可能多地拦截和缓存,将打到后端的请求量降到最低。

2.1 CDN 静态资源缓存

原理
scss 复制代码
用户 → CDN 边缘节点 (命中 → 直接返回,不经过源站)
                (未命中 → 回源 → 缓存到边缘)
Go 后端配合 CDN 的关键配置
go 复制代码
// middleware/cache_control.go
package middleware

import (
    "net/http"
    "time"
)

// CDNCacheMiddleware 为不同类型的资源设置缓存策略
func CDNCacheMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        path := r.URL.Path

        // 静态资源:长期缓存(带 hash 的文件名)
        if isStaticAsset(path) {
            w.Header().Set("Cache-Control", "public, max-age=31536000, immutable")
            w.Header().Set("Expires", time.Now().Add(365*24*time.Hour).Format(http.TimeFormat))
            next.ServeHTTP(w, r)
            return
        }

        // API 响应:根据不同接口设置不同缓存策略
        switch {
        case isPublicAPI(path):
            // 公共数据(如商品列表):短时 CDN 缓存
            w.Header().Set("Cache-Control", "public, max-age=60, s-maxage=300")
            w.Header().Set("Surrogate-Control", "max-age=300") // CDN 专用头
            
        case isUserSpecific(path):
            // 用户相关数据:不缓存
            w.Header().Set("Cache-Control", "private, no-cache, no-store, must-revalidate")
            
        default:
            w.Header().Set("Cache-Control", "no-cache")
        }

        // ETag 支持条件请求
        w.Header().Set("ETag", generateETag(r))
        
        next.ServeHTTP(w, r)
    })
}

func isStaticAsset(path string) bool {
    return strings.HasPrefix(path, "/static/") ||
           strings.HasPrefix(path, "/assets/") ||
           strings.HasSuffix(path, ".js") ||
           strings.HasSuffix(path, ".css") ||
           strings.HasSuffix(path, ".png")
}

func isPublicAPI(path string) bool {
    // 商品列表、公开配置等
    return strings.HasPrefix(path, "/api/v1/products") ||
           strings.HasPrefix(path, "/api/v1/public/")
}

func isUserSpecific(path string) bool {
    // 订单、个人中心等
    return strings.HasPrefix(path, "/api/v1/orders") ||
           strings.HasPrefix(path, "/api/v1/user/")
}
CDN 缓存策略总结
资源类型 Cache-Control CDN 缓存时长 示例
静态资源 (带hash) max-age=31536000, immutable 永久 app.a3f2.js
公共 API 数据 public, s-maxage=300 5 分钟 商品列表
半动态数据 public, s-maxage=10 10 秒 首页推荐
用户私有数据 private, no-cache 不缓存 订单详情

2.2 Nginx 网关限流

2.2.1 连接数限制
nginx 复制代码
# nginx.conf

# 定义限流区域
# 单个 IP 最多 10 个并发连接
limit_conn_zone $binary_remote_addr zone=per_ip:10m;

# 整个 server 最多 10000 个并发连接
limit_conn_zone $server_name zone=per_server:10m;

server {
    listen 80;
    
    # 应用连接限制
    limit_conn per_ip 10;
    limit_conn per_server 10000;
    
    # 超过限制返回 503
    limit_conn_status 503;
    
    location / {
        proxy_pass http://backend;
        proxy_connect_timeout 3s;
        proxy_read_timeout 5s;
    }
}
2.2.2 请求速率限制(令牌桶)
nginx 复制代码
# 请求速率限制
# 单个 IP 每秒最多 50 个请求,突发 20
limit_req_zone $binary_remote_addr zone=req_per_ip:10m rate=50r/s;

# 针对敏感接口更严格限制 (登录/注册/秒杀)
limit_req_zone $binary_remote_addr zone=req_sensitive:10m rate=5r/s;

server {
    location /api/ {
        limit_req zone=req_per_ip burst=20 nodelay;
        limit_req_status 429;
        proxy_pass http://backend;
    }
    
    location /api/v1/login {
        limit_req zone=req_sensitive burst=3 nodelay;
        limit_req_status 429;
        proxy_pass http://backend;
    }
    
    location /api/v1/seckill/ {
        limit_req zone=req_sensitive burst=10 nodelay;
        limit_req_status 429;
        proxy_pass http://backend;
    }
}
2.2.3 Go 应用层限流(配合 Nginx)
go 复制代码
// middleware/rate_limiter.go
package middleware

import (
    "context"
    "net/http"
    "sync"
    "time"
    
    "golang.org/x/time/rate"
)

// IPRateLimiter 基于 IP 的应用层令牌桶限流器
type IPRateLimiter struct {
    mu       sync.RWMutex
    limiters map[string]*rateLimiterEntry
    rate     rate.Limit
    burst    int
    ttl      time.Duration
}

type rateLimiterEntry struct {
    limiter  *rate.Limiter
    lastSeen time.Time
}

func NewIPRateLimiter(r rate.Limit, burst int) *IPRateLimiter {
    l := &IPRateLimiter{
        limiters: make(map[string]*rateLimiterEntry),
        rate:     r,
        burst:    burst,
        ttl:      10 * time.Minute,
    }
    // 定期清理过期 IP
    go l.cleanup()
    return l
}

func (l *IPRateLimiter) Allow(ip string) bool {
    l.mu.Lock()
    entry, exists := l.limiters[ip]
    if !exists {
        entry = &rateLimiterEntry{
            limiter:  rate.NewLimiter(l.rate, l.burst),
            lastSeen: time.Now(),
        }
        l.limiters[ip] = entry
    }
    entry.lastSeen = time.Now()
    l.mu.Unlock()

    return entry.limiter.Allow()
}

func (l *IPRateLimiter) cleanup() {
    for {
        time.Sleep(time.Minute)
        l.mu.Lock()
        for ip, entry := range l.limiters {
            if time.Since(entry.lastSeen) > l.ttl {
                delete(l.limiters, ip)
            }
        }
        l.mu.Unlock()
    }
}

// RateLimiterMiddleware 限流中间件
func RateLimiterMiddleware(limiter *IPRateLimiter) func(http.Handler) http.Handler {
    return func(next http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            ip := getClientIP(r)

            if !limiter.Allow(ip) {
                w.Header().Set("Retry-After", "1")
                w.Header().Set("X-RateLimit-Limit", "50")
                http.Error(w, `{"code":429,"msg":"请求过于频繁,请稍后重试"}`, http.StatusTooManyRequests)
                return
            }

            next.ServeHTTP(w, r)
        })
    }
}

func getClientIP(r *http.Request) string {
    // 优先取 X-Forwarded-For(经过 Nginx 代理)
    if xff := r.Header.Get("X-Forwarded-For"); xff != "" {
        parts := strings.Split(xff, ",")
        return strings.TrimSpace(parts[0])
    }
    if xri := r.Header.Get("X-Real-IP"); xri != "" {
        return xri
    }
    // fallback
    host, _, _ := net.SplitHostPort(r.RemoteAddr)
    return host
}

2.3 WAF 与防爬虫

Nginx + Lua 简易 WAF 规则
nginx 复制代码
# 基于 lua-resty-waf 或自定义规则
location / {
    access_by_lua_block {
        local uri = ngx.var.uri
        local ua = ngx.var.http_user_agent or ""
        local ip = ngx.var.remote_addr
        
        -- 规则1: 拦截 SQL 注入尝试
        local args = ngx.var.query_string or ""
        if args:match("(%d+)%s*=%s*%d+%s+or%s+") then
            ngx.log(ngx.WARN, "SQL injection attempt from ", ip)
            ngx.exit(403)
        end
        
        -- 规则2: 拦截无 UA 的请求(爬虫特征)
        if ua == "" or ua == "-" then
            ngx.log(ngx.WARN, "Empty UA from ", ip)
            -- 对 API 路径放宽,对页面路径拦截
            if not uri:find("^/api/") then
                ngx.exit(403)
            end
        end
        
        -- 规则3: 单 IP 短时间高频访问(CC 攻击特征)
        local count = ngx.shared.limit:incr("cc_" .. ip, 1, 10)
        if count and count > 500 then
            ngx.log(ngx.ERR, "CC attack detected from ", ip)
            -- 自动加入黑名单
            ngx.shared.blacklist:set(ip, true, 3600)
            ngx.exit(403)
        end
    }
    
    proxy_pass http://backend;
}
Go 应用层防爬虫
go 复制代码
// middleware/anti_crawler.go
package middleware

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
    "net/http"
    "time"
)

// AntiCrawler 防爬虫中间件
type AntiCrawler struct {
    // 合法请求的频率统计
    frequency map[string]*SlidingWindow // IP -> 滑动窗口
    // 签名密钥
    signSecret string
}

type SlidingWindow struct {
    timestamps []int64
    mu         sync.Mutex
}

func NewAntiCrawler(secret string) *AntiCrawler {
    return &AntiCrawler{
        frequency:  make(map[string]*SlidingWindow),
        signSecret: secret,
    }
}

// ★ 方式一:请求签名校验(防接口被脚本调用)
func (a *AntiCrawler) ValidateSignature(r *http.Request) bool {
    timestamp := r.Header.Get("X-Timestamp")
    sign := r.Header.Get("X-Sign")
    nonce := r.Header.Get("X-Nonce")
    
    // 时间戳过期检查(5分钟有效期)
    ts, err := strconv.ParseInt(timestamp, 10, 64)
    if err != nil || time.Now().Unix()-ts > 300 {
        return false
    }
    
    // 重新计算签名
    payload := timestamp + nonce + r.URL.Path + a.signSecret
    mac := hmac.New(sha256.New, []byte(a.signSecret))
    mac.Write([]byte(payload))
    expectedSign := hex.EncodeToString(mac.Sum(nil))
    
    return hmac.Equal([]byte(sign), []byte(expectedSign))
}

// ★ 方式二:PoW (Proof of Work) 挑战-应答
func (a *AntiCrawler) IssueChallenge(w http.ResponseWriter, r *http.Request) string {
    // 生成随机 challenge
    challenge := fmt.Sprintf("%d_%s", time.Now().Unix(), randomString(16))
    
    // 存入 Redis,5 分钟有效
    rdb.Set(ctx, "challenge:"+challenge, "pending", 5*time.Minute)
    
    return challenge
}

func (a *AntiCrawler) VerifyChallenge(challenge, answer string) bool {
    val, _ := rdb.Get(ctx, "challenge:"+challenge).Result()
    if val != "pending" {
        return false
    }
    
    // 验证 PoW(例如 hash 前 N 位为 0)
    hash := sha256.Sum256([]byte(challenge + answer))
    return hex.EncodeToString(hash[:])[:4] == "0000"
}

// ★ 方式三:行为分析(滑动窗口计数)
func (a *AntiCrawler) IsAbnormalBehavior(ip string) bool {
    window, exists := a.frequency[ip]
    if !exists {
        window = &SlidingWindow{}
        a.frequency[ip] = window
    }
    
    window.mu.Lock()
    defer window.mu.Unlock()
    
    now := time.Now().UnixMilli()
    window.timestamps = append(window.timestamps, now)
    
    // 只保留最近 10 秒的记录
    threshold := now - 10000
    var valid []int64
    for _, ts := range window.timestamps {
        if ts > threshold {
            valid = append(valid, ts)
        }
    }
    window.timestamps = valid
    
    // 10 秒内超过 200 次请求 = 异常
    return len(valid) > 200
}

2.4 黑名单与 IP 封禁

go 复制代码
// middleware/blacklist.go
package middleware

// IPBlacklist IP 黑名单(Redis 实现,支持自动过期)
type IPBlacklist struct {
    rdb       *redis.Client
    maxViolations int      // 触发多少次违规才封禁
    banDuration   time.Duration // 封禁时长
}

func NewIPBlacklist(rdb *redis.Client) *IPBlacklist {
    return &IPBlacklist{
        rdb:          rdb,
        maxViolations: 5,
        banDuration:   time.Hour,
    }
}

// RecordViolation 记录一次违规
func (b *IPBlacklist) RecordViolation(ctx context.Context, ip, reason string) error {
    key := fmt.Sprintf("blacklist:violation:%s", ip)
    
    pipe := b.rdb.Pipeline()
    
    // 增加违规计数
    incr := pipe.Incr(ctx, key)
    // 设置过期时间(计数窗口)
    pipe.Expire(ctx, key, 10*time.Minute)
    
    _, err := pipe.Exec(ctx)
    if err != nil {
        return err
    }
    
    count := incr.Val()
    
    // 超过阈值,自动封禁
    if count >= int64(b.maxViolations) {
        return b.BanIP(ctx, ip, reason)
    }
    
    return nil
}

// BanIP 封禁 IP
func (b *IPBlacklist) BanIP(ctx context.Context, ip, reason string) error {
    key := fmt.Sprintf("blacklist:banned:%s", ip)
    
    data := map[string]interface{}{
        "reason":     reason,
        "banned_at":  time.Now().Unix(),
        "expires_at": time.Now().Add(b.banDuration).Unix(),
    }
    
    pipe := b.rdb.Pipeline()
    pipe.HMSet(ctx, key, data)
    pipe.Expire(ctx, key, b.banDuration)
    
    // 同时清理违规计数
    pipe.Del(ctx, fmt.Sprintf("blacklist:violation:%s", ip))
    
    _, err := pipe.Exec(ctx)
    return err
}

// IsBanned 检查 IP 是否被封禁
func (b *IPBlacklist) IsBanned(ctx context.Context, ip string) bool {
    key := fmt.Sprintf("blacklist:banned:%s", ip)
    exists, _ := b.rdb.Exists(ctx, key).Result()
    return exists > 0
}

// BlacklistMiddleware 黑名单中间件
func (b *IPBlacklist) BlacklistMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        ip := getClientIP(r)
        
        if b.IsBanned(r.Context(), ip) {
            http.Error(w, `{"code":403,"msg":"访问被拒绝"}`, http.StatusForbidden)
            return
        }
        
        next.ServeHTTP(w, r)
    })
}

三、中层:缓冲、隔离与降级

目标:保护核心服务不被冲垮,用 Redis 扛读、用 MQ 抗写,非核心链路隔离开。

3.1 微服务架构拆分

scss 复制代码
                     ┌─────────────┐
                     │  API Gateway │ (BFF / 网关)
                     └──────┬──────┘
                            │
        ┌─────────┬─────────┼─────────┬─────────┐
        ▼         ▼         ▼         ▼         ▼
   ┌─────────┐┌─────────┐┌─────────┐┌─────────┐┌─────────┐
   │ 用户服务 ││ 商品服务 ││ 订单服务 ││ 支付服务 ││ 库存服务 │
   │(核心)   ││(核心)   ││(核心)   ││(核心)   ││(核心)   │
   └─────────┘└─────────┘└─────────┘└─────────┘└─────────┘
        │                                     │
   ┌─────────┐┌─────────┐               ┌─────────┐
   │ 通知服务 ││ 日志服务 │               │ 营销服务 │
   │(非核心) ││(非核心) │               │(非核心) │
   └─────────┘└─────────┘               └─────────┘
拆分原则
原则 说明
高内聚低耦合 一个服务只做一件事,通过 API 通信
按业务域拆分 用户、商品、订单、支付各自独立
核心/非核心隔离 订单、支付是核心;通知、日志是非核心
独立部署 每个服务独立发布、独立扩缩容
独立数据库 每个服务有自己的数据库,不共享

3.2 核心与非核心链路隔离

go 复制代码
// service/service_context.go
package service

import (
    "context"
    "sync"
)

// ServicePool 按优先级隔离协程池
type ServicePool struct {
    // 核心业务:大池子,高并发
    corePool chan struct{} // 信号量,控制并发数
    
    // 非核心业务:小池子,限并发
    nonCorePool chan struct{}
    
    // 通知/日志:极小池子,不影响核心
    bgPool chan struct{}
}

func NewServicePool(coreMax, nonCoreMax, bgMax int) *ServicePool {
    return &ServicePool{
        corePool:    make(chan struct{}, coreMax),
        nonCorePool: make(chan struct{}, nonCoreMax),
        bgPool:      make(chan struct{}, bgMax),
    }
}

// ExecuteCore 执行核心业务(高优先级)
func (p *ServicePool) ExecuteCore(ctx context.Context, fn func() error) error {
    // 尽力获取信号量,如果池满了就等待
    p.corePool <- struct{}{}
    defer func() { <-p.corePool }()
    
    return fn()
}

// ExecuteNonCore 执行非核心业务(低优先级,池满直接丢弃)
func (p *ServicePool) ExecuteNonCore(ctx context.Context, fn func()) {
    select {
    case p.nonCorePool <- struct{}{}:
        go func() {
            defer func() { <-p.nonCorePool }()
            fn()
        }()
    case <-ctx.Done():
        return
    default:
        // ★ 非核心链路池满 → 直接丢弃,不影响核心链路
        log.Println("[warn] non-core pool full, task discarded")
    }
}

// ExecuteBackground 执行后台任务(最低优先级)
func (p *ServicePool) ExecuteBackground(fn func()) {
    select {
    case p.bgPool <- struct{}{}:
        go func() {
            defer func() { <-p.bgPool }()
            fn()
        }()
    default:
        // 静默丢弃
    }
}

// --- 使用示例 ---

var svcPool = NewServicePool(1000, 100, 20)

// 下单(核心链路)
func CreateOrder(ctx context.Context, req CreateOrderReq) error {
    return svcPool.ExecuteCore(ctx, func() error {
        // 1. 扣库存
        // 2. 创建订单
        // 3. 发起支付
        return nil
    })
}

// 发短信通知(非核心链路)
func SendSMSNotification(phone, msg string) {
    svcPool.ExecuteNonCore(context.Background(), func() {
        // 调用短信 API ...
    })
    // 池满了直接丢弃通知,不影响下单
}

// 打日志(后台链路,丢弃也无妨)
func LogUserBehavior(userID int64, action string) {
    svcPool.ExecuteBackground(func() {
        // 写入 ES / ClickHouse ...
    })
}

3.3 协程池隔离与并发控制

go 复制代码
// pool/adaptive_worker_pool.go
package pool

import (
    "context"
    "sync"
    "sync/atomic"
    "time"
)

// AdaptiveWorkerPool 自适应协程池
// 根据系统负载动态调整并发度
type AdaptiveWorkerPool struct {
    name        string
    
    // 配置
    maxWorkers  int32
    minWorkers  int32
    
    // 运行时状态
    activeCount int32           // 当前活跃协程数
    pendingTasks chan func()    // 待处理任务队列
    stopCh      chan struct{}
    
    // 统计
    totalSubmitted  uint64
    totalCompleted  uint64
    totalDropped    uint64
    totalTimeout    uint64
    
    mu sync.RWMutex
}

func NewAdaptiveWorkerPool(name string, minWorkers, maxWorkers int, queueSize int) *AdaptiveWorkerPool {
    p := &AdaptiveWorkerPool{
        name:         name,
        maxWorkers:   int32(maxWorkers),
        minWorkers:   int32(minWorkers),
        pendingTasks: make(chan func(), queueSize),
        stopCh:       make(chan struct{}),
    }
    
    // 启动最小数量 worker
    for i := 0; i < minWorkers; i++ {
        go p.worker()
    }
    
    // 启动自适应扩缩容控制器
    go p.autoScaler()
    
    return p
}

func (p *AdaptiveWorkerPool) worker() {
    for {
        select {
        case task := <-p.pendingTasks:
            atomic.AddInt32(&p.activeCount, 1)
            task()
            atomic.AddUint64(&p.totalCompleted, 1)
            atomic.AddInt32(&p.activeCount, -1)
        case <-p.stopCh:
            return
        }
    }
}

// Submit 提交任务
// timeout: 等待超时时间,超过则丢弃
func (p *AdaptiveWorkerPool) Submit(task func(), timeout time.Duration) error {
    atomic.AddUint64(&p.totalSubmitted, 1)
    
    select {
    case p.pendingTasks <- task:
        return nil
    case <-time.After(timeout):
        atomic.AddUint64(&p.totalDropped, 1)
        return fmt.Errorf("pool [%s] is full, task dropped", p.name)
    }
}

// autoScaler 自适应扩缩容
func (p *AdaptiveWorkerPool) autoScaler() {
    ticker := time.NewTicker(3 * time.Second)
    defer ticker.Stop()
    
    for {
        select {
        case <-ticker.C:
            queueLen := len(p.pendingTasks)
            capacity := cap(p.pendingTasks)
            
            // 队列利用率 > 80%:扩容
            if float64(queueLen)/float64(capacity) > 0.8 {
                p.scaleUp()
            }
            
            // 队列利用率 < 20%:缩容
            if float64(queueLen)/float64(capacity) < 0.2 {
                p.scaleDown()
            }
        case <-p.stopCh:
            return
        }
    }
}

func (p *AdaptiveWorkerPool) scaleUp() {
    p.mu.Lock()
    defer p.mu.Unlock()
    
    active := atomic.LoadInt32(&p.activeCount)
    if active < p.maxWorkers {
        toAdd := (p.maxWorkers - active) / 2
        if toAdd < 1 {
            toAdd = 1
        }
        for i := int32(0); i < toAdd; i++ {
            go p.worker()
        }
        log.Printf("[pool:%s] scaled up: +%d workers", p.name, toAdd)
    }
}

func (p *AdaptiveWorkerPool) scaleDown() {
    // 缩容可由 worker 自行退出(通过 stopCh 信号)
    // 此处简化,实际可用 context.WithCancel 控制
}

// Stats 获取统计数据
func (p *AdaptiveWorkerPool) Stats() PoolStats {
    return PoolStats{
        Name:          p.name,
        Submitted:     atomic.LoadUint64(&p.totalSubmitted),
        Completed:     atomic.LoadUint64(&p.totalCompleted),
        Dropped:       atomic.LoadUint64(&p.totalDropped),
        ActiveWorkers: atomic.LoadInt32(&p.activeCount),
        QueueLen:      len(p.pendingTasks),
        QueueCap:      cap(p.pendingTasks),
    }
}

3.4 Redis 抗读并发

3.4.1 多级缓存架构
dart 复制代码
请求 → 本地缓存 (sync.Map/ristretto) → Redis → 数据库
       命中率 ~60%, TTL 短          命中率 ~30%   命中率 ~10%
go 复制代码
// cache/multi_level_cache.go
package cache

import (
    "context"
    "encoding/json"
    "sync"
    "time"
    
    "github.com/redis/go-redis/v9"
)

// MultiLevelCache 三级缓存
// L1: 本地内存(进程内,超快)
// L2: Redis(分布式,跨实例共享)
// L3: 数据库(权威数据源)
type MultiLevelCache struct {
    rdb     *redis.Client
    
    // L1 本地缓存
    local   sync.Map
    localTTL time.Duration
    
    // 统计
    hitsL1    uint64
    hitsL2    uint64
    misses    uint64
}

type cacheEntry struct {
    data      []byte
    expiredAt time.Time
}

func NewMultiLevelCache(rdb *redis.Client, localTTL time.Duration) *MultiLevelCache {
    c := &MultiLevelCache{
        rdb:      rdb,
        localTTL: localTTL,
    }
    // 定期清理过期本地缓存
    go c.localCleaner()
    return c
}

// Get 三级缓存读取
func (c *MultiLevelCache) Get(ctx context.Context, key string, dest interface{}, 
    dbLoader func() (interface{}, error), redisTTL time.Duration,
) error {
    // L1: 本地缓存
    if val, ok := c.local.Load(key); ok {
        entry := val.(*cacheEntry)
        if time.Now().Before(entry.expiredAt) {
            atomic.AddUint64(&c.hitsL1, 1)
            return json.Unmarshal(entry.data, dest)
        }
        c.local.Delete(key)
    }
    
    // L2: Redis
    data, err := c.rdb.Get(ctx, key).Bytes()
    if err == nil {
        atomic.AddUint64(&c.hitsL2, 1)
        // 回填 L1
        c.setLocal(key, data)
        return json.Unmarshal(data, dest)
    }
    
    // L3: 数据库 + 回填 L1/L2
    atomic.AddUint64(&c.misses, 1)
    
    result, err := dbLoader()
    if err != nil {
        return err
    }
    
    // ★ 使用 singleflight 防止缓存击穿
    // 见下面的 HotKeyProtector
    
    // 序列化并回填缓存
    jsonData, _ := json.Marshal(result)
    
    // 回填 L2 (Redis) --- 异步,不阻塞主流程
    go func() {
        c.rdb.Set(context.Background(), key, jsonData, redisTTL)
    }()
    
    // 回填 L1
    c.setLocal(key, jsonData)
    
    // 反序列化返回
    rawData, _ := json.Marshal(result)
    return json.Unmarshal(rawData, dest)
}

func (c *MultiLevelCache) setLocal(key string, data []byte) {
    c.local.Store(key, &cacheEntry{
        data:      data,
        expiredAt: time.Now().Add(c.localTTL),
    })
}
3.4.2 热点 Key 保护(Singleflight)
go 复制代码
// cache/hot_key_protector.go
package cache

import (
    "context"
    "sync"
    
    "golang.org/x/sync/singleflight"
)

// HotKeyProtector 热点 Key 保护器
// 防止缓存击穿:同一 Key 同时只有 1 个请求去加载 DB
type HotKeyProtector struct {
    sf          singleflight.Group
    
    // 互斥锁保护器(针对极高并发场景)
    mu          sync.Mutex
    lockKeys    map[string]*sync.Mutex
}

func NewHotKeyProtector() *HotKeyProtector {
    return &HotKeyProtector{
        lockKeys: make(map[string]*sync.Mutex),
    }
}

// ★ 方式一:singleflight(推荐,最简单)
func (p *HotKeyProtector) LoadWithSingleflight(
    ctx context.Context,
    key string,
    cacheLoader func() (interface{}, error),
    dbLoader func() (interface{}, error),
) (interface{}, error) {
    // 先查缓存
    if val, ok := cacheLoader(); ok {
        return val, nil
    }
    
    // ★ singleflight: 同一 key 并发调用 dbLoader 时,只执行一次
    val, err, shared := p.sf.Do(key, func() (interface{}, error) {
        return dbLoader()
    })
    
    if shared {
        log.Printf("[cache] key=%s shared singleflight result", key)
    }
    
    return val, err
}

// ★ 方式二:Redis SETNX 分布式互斥锁(跨实例)
func (p *HotKeyProtector) LoadWithDistributedLock(
    ctx context.Context,
    rdb *redis.Client,
    key string, 
    loader func() (interface{}, error),
) (interface{}, error) {
    lockKey := "hotkey:lock:" + key
    cacheKey := "hotkey:data:" + key
    
    // 1. 尝试从 Redis 读取(可能已被其他实例加载)
    cached, err := rdb.Get(ctx, cacheKey).Result()
    if err == nil {
        var result interface{}
        json.Unmarshal([]byte(cached), &result)
        return result, nil
    }
    
    // 2. 尝试获取加载锁
    locked, _ := rdb.SetNX(ctx, lockKey, "1", 5*time.Second).Result()
    
    if locked {
        // 3. 获得锁 → 从 DB 加载
        data, err := loader()
        if err != nil {
            rdb.Del(ctx, lockKey)
            return nil, err
        }
        
        // 4. 写入 Redis 并释放锁
        jsonData, _ := json.Marshal(data)
        pipe := rdb.Pipeline()
        pipe.Set(ctx, cacheKey, jsonData, 10*time.Minute)
        pipe.Del(ctx, lockKey)
        pipe.Exec(ctx)
        
        return data, nil
    }
    
    // 5. 没抢到锁 → 自旋等待,然后重试读缓存
    for i := 0; i < 20; i++ {
        time.Sleep(50 * time.Millisecond)
        
        cached, err := rdb.Get(ctx, cacheKey).Result()
        if err == nil {
            var result interface{}
            json.Unmarshal([]byte(cached), &result)
            return result, nil
        }
    }
    
    // 6. 超时 → 直接查 DB(兜底)
    return loader()
}

// ★ 方式三:本地互斥锁(单实例,性能最高)
func (p *HotKeyProtector) LoadWithLocalLock(
    key string,
    loader func() (interface{}, error),
) (interface{}, error) {
    p.mu.Lock()
    lock, exists := p.lockKeys[key]
    if !exists {
        lock = &sync.Mutex{}
        p.lockKeys[key] = lock
    }
    p.mu.Unlock()
    
    lock.Lock()
    defer lock.Unlock()
    
    return loader()
}
3.4.3 缓存穿透/击穿/雪崩防护
go 复制代码
// cache/defense.go
package cache

// CacheDefense 缓存三大问题防护
type CacheDefense struct {
    rdb *redis.Client
}

// 1. ★ 缓存穿透 (请求不存在的数据)
// 方案:布隆过滤器 + 空值缓存
func (d *CacheDefense) AntiPenetration(key string, loader func() (interface{}, error)) (interface{}, error) {
    // Step1: 布隆过滤器判断 Key 是否存在
    if !d.bloomFilter.Exists(key) {
        return nil, ErrNotFound // 直接拒绝,不查 DB
    }
    
    // Step2: 查缓存
    val, err := d.rdb.Get(ctx, key).Result()
    if err == nil {
        if val == "__NULL__" {
            return nil, ErrNotFound // 命中空值缓存
        }
        return val, nil
    }
    
    // Step3: 查 DB
    data, err := loader()
    if err != nil || data == nil {
        // ★ 空值也缓存(避免频繁查 DB),TTL 短一些
        d.rdb.Set(ctx, key, "__NULL__", 1*time.Minute)
        d.bloomFilter.Add(key)
        return nil, ErrNotFound
    }
    
    // 正常缓存
    jsonData, _ := json.Marshal(data)
    d.rdb.Set(ctx, key, jsonData, 10*time.Minute)
    return data, nil
}

// 2. ★ 缓存击穿 (热点 Key 过期,大量请求打到 DB)
// 方案:互斥锁 / singleflight(见 3.4.2 HotKeyProtector)

// 3. ★ 缓存雪崩 (大量 Key 同时过期)
// 方案:过期时间加随机偏移
func (d *CacheDefense) SetWithRandomTTL(key string, data interface{}, baseTTL time.Duration) {
    // ★ TTL = baseTTL + random(0, 0.3*baseTTL)
    jitter := time.Duration(rand.Int63n(int64(baseTTL) * 3 / 10))
    ttl := baseTTL + jitter
    
    jsonData, _ := json.Marshal(data)
    d.rdb.Set(ctx, key, jsonData, ttl)
}

// 4. ★ 缓存预热(系统启动/发布前主动加载热点数据)
func (d *CacheDefense) WarmUp(ctx context.Context, hotKeys []string) error {
    for _, key := range hotKeys {
        // 检查缓存是否存在
        exists, _ := d.rdb.Exists(ctx, key).Result()
        if exists > 0 {
            continue
        }
        
        // 从 DB 加载
        data, err := d.loadFromDB(ctx, key)
        if err != nil {
            log.Printf("warm-up failed: key=%s err=%v", key, err)
            continue
        }
        
        // 设置随机 TTL(防雪崩)
        d.SetWithRandomTTL(key, data, 10*time.Minute)
    }
    return nil
}

// 5. ★ 熔断降级(Redis 不可用,直接返回兜底数据)
func (d *CacheDefense) GetWithFallback(ctx context.Context, key string, fallback interface{}) (interface{}, error) {
    val, err := d.rdb.Get(ctx, key).Result()
    if err != nil {
        if errors.Is(err, redis.Nil) {
            return fallback, nil // Key 不存在,返回兜底值
        }
        // Redis 挂了 → 熔断,直接返回兜底
        log.Printf("[cache] redis unreachable, return fallback for key=%s", key)
        return fallback, nil
    }
    
    var result interface{}
    json.Unmarshal([]byte(val), &result)
    return result, nil
}

3.5 消息队列抗写并发

go 复制代码
// mq/write_peaker.go
package mq

import (
    "context"
    "encoding/json"
    "time"
)

// WritePeaker 写流量削峰器
// 将同步写 DB 改为异步写 MQ + 消费者批量写 DB
type WritePeaker struct {
    producer MessageProducer
}

type OrderCreatedEvent struct {
    OrderID   int64     `json:"order_id"`
    OrderNo   string    `json:"order_no"`
    UserID    int64     `json:"user_id"`
    Amount    float64   `json:"amount"`
    Items     []Item    `json:"items"`
    CreatedAt time.Time `json:"created_at"`
    // 事件版本号(幂等消费用)
    EventVersion int64  `json:"event_version"`
}

// Producer: 下单时,先发 MQ 再快速返回
func (p *WritePeaker) CreateOrderAsync(ctx context.Context, event OrderCreatedEvent) error {
    body, _ := json.Marshal(event)
    
    // ★ 快速发送 MQ,不直接写 DB
    return p.producer.Send(ctx, &Message{
        Topic: "order-create",
        Key:   event.OrderNo,
        Body:  body,
    })
}

// Consumer: MQ 消费者批量写入 DB(削峰填谷)
func (p *WritePeaker) ConsumeAndPersist(ctx context.Context, msgs []*Message) error {
    // ★ 批量写入,减少 DB 连接开销
    tx, _ := db.BeginTx(ctx, nil)
    defer tx.Rollback()
    
    stmt, _ := tx.Prepare(`
        INSERT INTO orders (order_no, user_id, amount, status, created_at)
        VALUES (?, ?, ?, 0, NOW())
        ON DUPLICATE KEY UPDATE updated_at = NOW()
    `)
    defer stmt.Close()
    
    for _, msg := range msgs {
        var event OrderCreatedEvent
        json.Unmarshal(msg.Body, &event)
        
        stmt.Exec(event.OrderNo, event.UserID, event.Amount)
        // 同时写 order_items ...
    }
    
    return tx.Commit()
}

3.6 服务熔断与降级

go 复制代码
// circuit/circuit_breaker.go
package circuit

import (
    "context"
    "errors"
    "sync"
    "time"
)

// State 熔断器状态
type State int

const (
    StateClosed   State = iota // 正常(关闭)
    StateOpen                  // 熔断(打开)
    StateHalfOpen              // 半开(试探恢复)
)

// CircuitBreaker 熔断器
type CircuitBreaker struct {
    mu sync.RWMutex
    
    state       State
    failureCount int
    successCount int
    
    // 配置
    failureThreshold int           // 失败多少次触发熔断
    successThreshold int           // 半开状态成功多少次恢复
    timeout          time.Duration // 熔断后多久进入半开
    halfOpenMax      int           // 半开状态允许的最大请求数
    
    lastFailureTime time.Time
}

func NewCircuitBreaker(failureThreshold int, timeout time.Duration) *CircuitBreaker {
    return &CircuitBreaker{
        state:            StateClosed,
        failureThreshold: failureThreshold,
        successThreshold: 5,
        timeout:          timeout,
        halfOpenMax:      10,
    }
}

// Execute 执行受保护的操作
func (cb *CircuitBreaker) Execute(ctx context.Context, fn func() error) error {
    if !cb.allowRequest() {
        return errors.New("circuit breaker is open, request rejected")
    }
    
    err := fn()
    
    if err != nil {
        cb.recordFailure()
        return err
    }
    
    cb.recordSuccess()
    return nil
}

func (cb *CircuitBreaker) allowRequest() bool {
    cb.mu.RLock()
    state := cb.state
    cb.mu.RUnlock()
    
    switch state {
    case StateClosed:
        return true
    
    case StateOpen:
        // 检查是否超过超时时间,可以进入半开
        cb.mu.Lock()
        defer cb.mu.Unlock()
        
        if time.Since(cb.lastFailureTime) > cb.timeout {
            cb.state = StateHalfOpen
            cb.successCount = 0
            log.Println("[circuit] state: OPEN → HALF_OPEN")
            return true
        }
        return false
    
    case StateHalfOpen:
        cb.mu.RLock()
        defer cb.mu.RUnlock()
        return cb.successCount < cb.halfOpenMax
    
    default:
        return false
    }
}

func (cb *CircuitBreaker) recordFailure() {
    cb.mu.Lock()
    defer cb.mu.Unlock()
    
    cb.failureCount++
    cb.lastFailureTime = time.Now()
    
    if cb.failureCount >= cb.failureThreshold && cb.state == StateClosed {
        cb.state = StateOpen
        cb.failureCount = 0
        log.Printf("[circuit] state: CLOSED → OPEN (failures=%d)", cb.failureCount)
    }
}

func (cb *CircuitBreaker) recordSuccess() {
    cb.mu.Lock()
    defer cb.mu.Unlock()
    
    cb.failureCount = 0 // 成功后重置失败计数
    
    if cb.state == StateHalfOpen {
        cb.successCount++
        if cb.successCount >= cb.successThreshold {
            cb.state = StateClosed
            cb.successCount = 0
            log.Println("[circuit] state: HALF_OPEN → CLOSED")
        }
    }
}

// StateString 返回状态字符串
func (cb *CircuitBreaker) StateString() string {
    cb.mu.RLock()
    defer cb.mu.RUnlock()
    
    switch cb.state {
    case StateClosed:
        return "CLOSED"
    case StateOpen:
        return "OPEN"
    case StateHalfOpen:
        return "HALF_OPEN"
    default:
        return "UNKNOWN"
    }
}

四、底层:数据一致性与保底

目标:无论上层如何削峰、异步、降级,最终落入数据库的数据必须是正确、一致的。

4.1 数据库接入前的流量平滑

go 复制代码
// db/connection_pool.go
package db

import (
    "context"
    "database/sql"
    "sync"
    "time"
)

// DBPoolManager 数据库连接池管理 + 流量平滑
type DBPoolManager struct {
    master *sql.DB
    slaves []*sql.DB
    
    // 流量平滑:令牌桶
    writeLimiter *rateLimiter
    readLimiter  *rateLimiter
    
    mu sync.RWMutex
}

// rateLimiter 简易令牌桶
type rateLimiter struct {
    tokens    float64
    rate      float64  // 每秒生成 token 数
    maxTokens float64
    mu        sync.Mutex
    lastTime  time.Time
}

func newRateLimiter(rate, maxTokens float64) *rateLimiter {
    return &rateLimiter{
        tokens:    maxTokens,
        rate:      rate,
        maxTokens: maxTokens,
        lastTime:  time.Now(),
    }
}

func (r *rateLimiter) Allow() bool {
    r.mu.Lock()
    defer r.mu.Unlock()
    
    // 生成 token
    elapsed := time.Since(r.lastTime).Seconds()
    r.tokens += elapsed * r.rate
    if r.tokens > r.maxTokens {
        r.tokens = r.maxTokens
    }
    r.lastTime = time.Now()
    
    if r.tokens >= 1 {
        r.tokens -= 1
        return true
    }
    return false
}

// ★ 限流写操作(保护主库)
func (m *DBPoolManager) WriteWithRateLimit(ctx context.Context, fn func(*sql.Tx) error) error {
    if !m.writeLimiter.Allow() {
        return fmt.Errorf("db write rate limit exceeded")
    }
    
    tx, _ := m.master.BeginTx(ctx, nil)
    defer tx.Rollback()
    
    if err := fn(tx); err != nil {
        return err
    }
    
    return tx.Commit()
}

// ★ 读写分离
func (m *DBPoolManager) GetReader() *sql.DB {
    m.mu.RLock()
    defer m.mu.RUnlock()
    
    if len(m.slaves) == 0 {
        return m.master
    }
    
    // 随机选一个从库(或轮询)
    idx := rand.Intn(len(m.slaves))
    return m.slaves[idx]
}

// ★ 强制走主库(写后读一致性)
func (m *DBPoolManager) GetMaster() *sql.DB {
    return m.master
}

4.2 幂等性设计

go 复制代码
// idempotent/idempotent.go
package idempotent

import (
    "context"
    "crypto/md5"
    "database/sql"
    "encoding/hex"
    "fmt"
    "time"
)

// 幂等性保证的三种方案

// ★ 方案一:数据库唯一约束(最简单、最可靠)
const schema = `
CREATE TABLE orders (
    id         BIGINT PRIMARY KEY AUTO_INCREMENT,
    order_no   VARCHAR(32) NOT NULL UNIQUE COMMENT '幂等键: 业务单号',
    -- UNIQUE 约束天然保证不会重复创建
    user_id    BIGINT NOT NULL,
    amount     DECIMAL(10,2) NOT NULL,
    status     TINYINT NOT NULL DEFAULT 0,
    created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
);
`

// CreateOrder 利用唯一约束保证幂等
func CreateOrder(ctx context.Context, db *sql.DB, order Order) error {
    _, err := db.ExecContext(ctx, `
        INSERT INTO orders (order_no, user_id, amount, status, created_at)
        VALUES (?, ?, ?, 0, NOW())
    `, order.OrderNo, order.UserID, order.Amount)
    
    if err != nil {
        if isDuplicateKeyError(err) {
            // 已存在 → 幂等返回成功
            return nil
        }
        return err
    }
    return nil
}

func isDuplicateKeyError(err error) bool {
    return strings.Contains(err.Error(), "Duplicate entry") ||
           strings.Contains(err.Error(), "duplicate key")
}

// ★ 方案二:幂等 Token(Redis + DB)
type IdempotentManager struct {
    rdb *redis.Client
}

// GenerateToken 生成幂等 Token(客户端提交前获取)
func (m *IdempotentManager) GenerateToken(ctx context.Context) (string, error) {
    token := generateUUID()
    key := fmt.Sprintf("idempotent:token:%s", token)
    
    // 存入 Redis,30 分钟过期
    err := m.rdb.Set(ctx, key, "valid", 30*time.Minute).Err()
    if err != nil {
        return "", err
    }
    
    return token, nil
}

// CheckAndConsume 校验并消费 Token(保证一次有效)
func (m *IdempotentManager) CheckAndConsume(ctx context.Context, token string) (bool, error) {
    key := fmt.Sprintf("idempotent:token:%s", token)
    
    // ★ Lua 脚本保证原子性:检查 + 删除
    script := `
        if redis.call("GET", KEYS[1]) == "valid" then
            return redis.call("DEL", KEYS[1])
        else
            return 0
        end
    `
    
    result, err := m.rdb.Eval(ctx, script, []string{key}).Int64()
    if err != nil {
        return false, err
    }
    
    return result == 1, nil
}

// ★ 方案三:业务参数哈希(无需额外存储)
type IdempotentByHash struct {
    db *sql.DB
}

// 幂等表设计
const idempotentTableSQL = `
CREATE TABLE IF NOT EXISTS idempotent_keys (
    id          BIGINT PRIMARY KEY AUTO_INCREMENT,
    biz_type    VARCHAR(32) NOT NULL COMMENT '业务类型: create_order / refund',
    hash_key    CHAR(32) NOT NULL COMMENT '业务参数 MD5',
    created_at  DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    UNIQUE KEY uk_biz_hash (biz_type, hash_key)
);
`

func (m *IdempotentByHash) ExecuteOnce(
    ctx context.Context, 
    bizType string, 
    params interface{}, 
    fn func() error,
) error {
    // 1. 计算参数哈希
    raw, _ := json.Marshal(params)
    hash := md5.Sum(raw)
    hashStr := hex.EncodeToString(hash[:])
    
    // 2. 尝试插入幂等记录
    _, err := m.db.ExecContext(ctx, `
        INSERT INTO idempotent_keys (biz_type, hash_key, created_at)
        VALUES (?, ?, NOW())
    `, bizType, hashStr)
    
    if err != nil {
        if isDuplicateKeyError(err) {
            // 已经执行过 → 直接成功
            return nil
        }
        return err
    }
    
    // 3. 首次执行 → 执行业务逻辑
    return fn()
}

4.3 乐观锁与分布式锁

go 复制代码
// lock/locks.go
package lock

// ★ 乐观锁(CAS --- Compare And Swap)
// 适用场景:并发冲突概率低的场景(如用户信息更新)

// UpdateWithOptimisticLock 带乐观锁的更新
func UpdateWithOptimisticLock(ctx context.Context, db *sql.DB, 
    orderID int64, expectedVersion int64, newStatus int,
) (bool, error) {
    result, err := db.ExecContext(ctx, `
        UPDATE orders 
        SET status = ?, version = version + 1, updated_at = NOW()
        WHERE id = ? AND version = ?
    `, newStatus, orderID, expectedVersion)
    
    if err != nil {
        return false, err
    }
    
    n, _ := result.RowsAffected()
    if n == 0 {
        // version 不匹配 → 说明已被其他人修改
        return false, ErrVersionConflict
    }
    
    return true, nil
}

// RetryWithOptimisticLock 乐观锁冲突自动重试
func RetryWithOptimisticLock(ctx context.Context, db *sql.DB,
    orderID int64, maxRetries int, fn func() (int, error), // fn 返回 newStatus
) error {
    for i := 0; i < maxRetries; i++ {
        // 1. 读取当前版本
        var version int64
        err := db.QueryRowContext(ctx, 
            "SELECT version FROM orders WHERE id = ?", orderID,
        ).Scan(&version)
        if err != nil {
            return err
        }
        
        // 2. 执行业务逻辑
        newStatus, err := fn()
        if err != nil {
            return err
        }
        
        // 3. CAS 更新
        ok, err := UpdateWithOptimisticLock(ctx, db, orderID, version, newStatus)
        if err != nil {
            return err
        }
        if ok {
            return nil // 成功
        }
        
        // 版本冲突 → 重试
        time.Sleep(time.Duration(i*10) * time.Millisecond)
    }
    
    return ErrMaxRetriesExceeded
}

// ★ 分布式锁(Redis Redlock)
// 适用场景:并发冲突概率高、需要互斥的场景(如秒杀扣库存)
type RedisLock struct {
    rdb *redis.Client
}

func (l *RedisLock) Lock(ctx context.Context, key string, ttl time.Duration) (string, error) {
    token := generateUUID()
    ok, err := l.rdb.SetNX(ctx, key, token, ttl).Result()
    if err != nil {
        return "", err
    }
    if !ok {
        return "", ErrLockFailed
    }
    return token, nil
}

// Unlock 安全释放锁(Lua 保证原子性)
func (l *RedisLock) Unlock(ctx context.Context, key, token string) error {
    script := `
        if redis.call("GET", KEYS[1]) == ARGV[1] then
            return redis.call("DEL", KEYS[1])
        else
            return 0
        end
    `
    _, err := l.rdb.Eval(ctx, script, []string{key}, token).Result()
    return err
}

// Extend 续期(长事务场景)
func (l *RedisLock) Extend(ctx context.Context, key, token string, ttl time.Duration) error {
    script := `
        if redis.call("GET", KEYS[1]) == ARGV[1] then
            return redis.call("PEXPIRE", KEYS[1], ARGV[2])
        else
            return 0
        end
    `
    _, err := l.rdb.Eval(ctx, script, []string{key}, token, int(ttl.Milliseconds())).Result()
    return err
}

// 使用示例
func SafeDeductStock(ctx context.Context, lock *RedisLock, skuID int64, qty int) error {
    lockKey := fmt.Sprintf("stock:lock:%d", skuID)
    
    // 1. 获取分布式锁
    token, err := lock.Lock(ctx, lockKey, 10*time.Second)
    if err != nil {
        return fmt.Errorf("acquire lock failed: %w", err)
    }
    defer lock.Unlock(ctx, lockKey, token)
    
    // 2. 执行业务逻辑(锁保护下的临界区)
    result, err := db.ExecContext(ctx, `
        UPDATE inventory 
        SET stock = stock - ?, locked_stock = locked_stock + ?
        WHERE sku_id = ? AND stock >= ?
    `, qty, qty, skuID, qty)
    
    if err != nil {
        return err
    }
    
    if n, _ := result.RowsAffected(); n == 0 {
        return ErrInsufficientStock
    }
    
    return nil
}

// ★ 数据库行锁(SELECT ... FOR UPDATE)
// 适用场景:对一致性要求极高、冲突范围可控的场景
func DeductWithDBLock(ctx context.Context, db *sql.DB, skuID int64, qty int) error {
    tx, _ := db.BeginTx(ctx, nil)
    defer tx.Rollback()
    
    // ★ FOR UPDATE:锁定该行,事务提交前其他事务无法修改
    var stock, lockedStock int
    err := tx.QueryRowContext(ctx, `
        SELECT stock, locked_stock 
        FROM inventory 
        WHERE sku_id = ? 
        FOR UPDATE
    `, skuID).Scan(&stock, &lockedStock)
    
    if err != nil {
        return err
    }
    
    if stock < qty {
        return ErrInsufficientStock
    }
    
    // 更新库存
    _, err = tx.ExecContext(ctx, `
        UPDATE inventory 
        SET stock = stock - ?, locked_stock = locked_stock + ?
        WHERE sku_id = ?
    `, qty, qty, skuID)
    
    if err != nil {
        return err
    }
    
    return tx.Commit()
}

4.4 补偿机制与最终一致性

go 复制代码
// compensation/compensation.go
package compensation

// CompensationManager 补偿管理器
type CompensationManager struct {
    db     *sql.DB
    mq     MessageProducer
    rdb    *redis.Client
}

// CompensationTask 补偿任务记录
type CompensationTask struct {
    ID            int64
    BizType       string     // 业务类型
    BizID         string     // 业务 ID
    Status        string     // pending / success / failed
    RetryCount    int
    MaxRetries    int
    NextRetryAt   time.Time
    ErrorMessage  string
    CreatedAt     time.Time
    UpdatedAt     time.Time
}

// ★ 本地消息表 + 定时补偿
const compensationTableSQL = `
CREATE TABLE IF NOT EXISTS compensation_tasks (
    id            BIGINT PRIMARY KEY AUTO_INCREMENT,
    biz_type      VARCHAR(32) NOT NULL,
    biz_id        VARCHAR(64) NOT NULL COMMENT '业务ID',
    status        VARCHAR(16) NOT NULL DEFAULT 'pending',
    retry_count   INT NOT NULL DEFAULT 0,
    max_retries   INT NOT NULL DEFAULT 10,
    next_retry_at DATETIME NOT NULL COMMENT '下次重试时间',
    error_message TEXT,
    created_at    DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at    DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    INDEX idx_status_retry (status, next_retry_at),
    UNIQUE KEY uk_biz (biz_type, biz_id)
);
`

// RegisterTask 注册补偿任务(本地事务,与业务写入同一个事务)
func (m *CompensationManager) RegisterTask(ctx context.Context, tx *sql.Tx, 
    bizType, bizID string) error {
    
    _, err := tx.ExecContext(ctx, `
        INSERT INTO compensation_tasks (biz_type, biz_id, status, next_retry_at)
        VALUES (?, ?, 'pending', DATE_ADD(NOW(), INTERVAL 1 MINUTE))
        ON DUPLICATE KEY UPDATE status = 'pending', retry_count = 0
    `, bizType, bizID)
    
    return err
}

// CompensationScanner 补偿扫描器(定时扫描未成功的任务)
type CompensationScanner struct {
    manager *CompensationManager
    // 各业务类型的重试处理器
    handlers map[string]RetryHandler
}

type RetryHandler func(ctx context.Context, bizID string) error

func (s *CompensationScanner) RegisterHandler(bizType string, handler RetryHandler) {
    s.handlers[bizType] = handler
}

func (s *CompensationScanner) Scan(ctx context.Context, batchSize int) {
    rows, err := s.manager.db.QueryContext(ctx, `
        SELECT id, biz_type, biz_id, retry_count
        FROM compensation_tasks
        WHERE status = 'pending' 
          AND next_retry_at <= NOW()
        LIMIT ?
    `, batchSize)
    
    if err != nil {
        return
    }
    defer rows.Close()
    
    for rows.Next() {
        var taskID int64
        var bizType, bizID string
        var retryCount int
        
        rows.Scan(&taskID, &bizType, &bizID, &retryCount)
        
        handler, ok := s.handlers[bizType]
        if !ok {
            continue
        }
        
        // 执行重试
        err := handler(ctx, bizID)
        
        if err == nil {
            // 成功 → 标记完成
            s.markSuccess(ctx, taskID)
        } else {
            // 失败 → 更新重试信息
            retryCount++
            s.markRetry(ctx, taskID, retryCount, err)
        }
    }
}

func (s *CompensationScanner) markSuccess(ctx context.Context, taskID int64) {
    s.manager.db.ExecContext(ctx, `
        UPDATE compensation_tasks 
        SET status = 'success', updated_at = NOW() 
        WHERE id = ?
    `, taskID)
}

func (s *CompensationScanner) markRetry(ctx context.Context, taskID, retries int, err error) {
    // 指数退避: 1min → 2min → 4min → 8min ...
    delayMinutes := int(math.Pow(2, float64(retries)))
    if delayMinutes > 60 {
        delayMinutes = 60 // 最多 1 小时重试一次
    }
    
    s.manager.db.ExecContext(ctx, `
        UPDATE compensation_tasks 
        SET retry_count = ?, 
            next_retry_at = DATE_ADD(NOW(), INTERVAL ? MINUTE),
            error_message = ?,
            updated_at = NOW()
        WHERE id = ?
    `, retries, delayMinutes, err.Error(), taskID)
}
go 复制代码
// 本地消息表使用示例(下单 + 库存扣减)

func CreateOrderWithCompensation(ctx context.Context, db *sql.DB, comp *CompensationManager, order Order) error {
    tx, _ := db.BeginTx(ctx, nil)
    defer tx.Rollback()
    
    // 1. 创建订单
    _, err := tx.ExecContext(ctx, `
        INSERT INTO orders (order_no, user_id, amount, status) 
        VALUES (?, ?, ?, 0)
    `, order.OrderNo, order.UserID, order.Amount)
    if err != nil {
        return err
    }
    
    // 2. ★ 扣库存(同步操作,可能失败)
    result, err := tx.ExecContext(ctx, `
        UPDATE inventory SET stock = stock - 1 
        WHERE sku_id = ? AND stock > 0
    `, order.SkuID)
    if err != nil || result.RowsAffected() == 0 {
        // 库存不足 → 回滚整个事务
        return ErrInsufficientStock
    }
    
    // 3. ★ 注册补偿任务(发给下游服务)
    // 如果这里失败,事务回滚,订单也回滚 → 一致性保证
    err = comp.RegisterTask(ctx, tx, "notify_user", order.OrderNo)
    if err != nil {
        return err
    }
    
    // 4. 提交事务(三步原子成功)
    return tx.Commit()
}

4.5 分布式事务(Saga / TCC)

go 复制代码
// saga/saga.go
package saga

// Saga 编排器(用于跨服务长事务,如:订单 → 库存 → 支付 → 物流)
type SagaOrchestrator struct {
    steps []SagaStep
    comps []SagaStep // 对应的补偿步骤
}

type SagaStep struct {
    Name       string
    Execute    func(ctx context.Context) error
    Compensate func(ctx context.Context) error // 补偿/回滚
}

func (o *SagaOrchestrator) AddStep(name string, execute, compensate func(ctx context.Context) error) {
    o.steps = append(o.steps, SagaStep{
        Name:    name,
        Execute: execute,
    })
    o.comps = append(o.comps, SagaStep{
        Name:       name,
        Compensate: compensate,
    })
}

// Execute 执行 Saga(正向执行,失败则逆向补偿)
func (o *SagaOrchestrator) Execute(ctx context.Context) error {
    executedSteps := make([]int, 0)
    
    for i, step := range o.steps {
        log.Printf("[saga] executing step %d: %s", i+1, step.Name)
        
        err := step.Execute(ctx)
        if err != nil {
            log.Printf("[saga] step %s failed: %v, starting compensation", step.Name, err)
            
            // ★ 逆向补偿:从当前步往前,依次执行补偿
            for j := i; j >= 0; j-- {
                compStep := o.comps[j]
                log.Printf("[saga] compensating step: %s", compStep.Name)
                
                if compStep.Compensate != nil {
                    compErr := compStep.Compensate(ctx)
                    if compErr != nil {
                        log.Printf("[saga] compensation for %s also failed: %v", 
                            compStep.Name, compErr)
                        // 补偿失败 → 记录到补偿表,人工介入
                        // compManager.RegisterTask(ctx, ...)
                    }
                }
                
                _ = executedSteps // 用于记录
            }
            
            return fmt.Errorf("saga failed at step %s: %w", step.Name, err)
        }
        
        executedSteps = append(executedSteps, i)
    }
    
    log.Printf("[saga] all %d steps completed successfully", len(o.steps))
    return nil
}

// 使用示例:下单 Saga
func CreateOrderSaga(order Order) *SagaOrchestrator {
    saga := &SagaOrchestrator{}
    
    // Step 1: 校验库存 + 锁定库存
    saga.AddStep(
        "lock_inventory",
        func(ctx context.Context) error {
            return inventoryService.Lock(ctx, order.Items)
        },
        func(ctx context.Context) error {
            return inventoryService.Unlock(ctx, order.Items) // 补偿:释放库存
        },
    )
    
    // Step 2: 创建订单
    saga.AddStep(
        "create_order",
        func(ctx context.Context) error {
            return orderService.Create(ctx, order)
        },
        func(ctx context.Context) error {
            return orderService.Cancel(ctx, order.ID) // 补偿:取消订单
        },
    )
    
    // Step 3: 扣减优惠券
    saga.AddStep(
        "use_coupon",
        func(ctx context.Context) error {
            return couponService.Use(ctx, order.CouponID)
        },
        func(ctx context.Context) error {
            return couponService.Return(ctx, order.CouponID) // 补偿:返还优惠券
        },
    )
    
    return saga
}

五、全链路监控与可观测性

5.1 关键监控指标

go 复制代码
// metrics/metrics.go
package metrics

import (
    "github.com/prometheus/client_golang/prometheus"
    "github.com/prometheus/client_golang/prometheus/promauto"
)

// Prometheus 指标体系
var (
    // 请求量
    RequestTotal = promauto.NewCounterVec(prometheus.CounterOpts{
        Name: "http_requests_total",
        Help: "Total HTTP requests",
    }, []string{"method", "path", "status"})
    
    // 请求延迟
    RequestLatency = promauto.NewHistogramVec(prometheus.HistogramOpts{
        Name:    "http_request_duration_seconds",
        Help:    "HTTP request latency",
        Buckets: []float64{.001, .005, .01, .025, .05, .1, .25, .5, 1, 2.5, 5, 10},
    }, []string{"method", "path"})
    
    // 各层流量
    LayerTraffic = promauto.NewGaugeVec(prometheus.GaugeOpts{
        Name: "layer_traffic_qps",
        Help: "Traffic QPS at each defense layer",
    }, []string{"layer"}) // edge, gateway, service, cache, mq, db
    
    // 熔断状态
    CircuitBreakerState = promauto.NewGaugeVec(prometheus.GaugeOpts{
        Name: "circuit_breaker_state",
        Help: "Circuit breaker state: 0=closed, 1=open, 2=half_open",
    }, []string{"name"})
    
    // 数据库连接池
    DBConnPool = promauto.NewGaugeVec(prometheus.GaugeOpts{
        Name: "db_connection_pool",
        Help: "DB connection pool stats",
    }, []string{"db", "type"}) // type: active, idle, waiting
    
    // 缓存命中率
    CacheHitRatio = promauto.NewGaugeVec(prometheus.GaugeOpts{
        Name: "cache_hit_ratio",
        Help: "Cache hit ratio",
    }, []string{"level"}) // L1, L2
    
    // 消息队列
    MQMessageLag = promauto.NewGaugeVec(prometheus.GaugeOpts{
        Name: "mq_message_lag",
        Help: "MQ consumer message lag",
    }, []string{"topic", "consumer_group"})
    
    // 补偿任务
    CompensationTaskCount = promauto.NewGaugeVec(prometheus.GaugeOpts{
        Name: "compensation_task_count",
        Help: "Pending compensation task count",
    }, []string{"biz_type"})
)

// ReportLayerTraffic 上报各层流量
func ReportLayerTraffic() {
    // edge: 从 Nginx 日志统计
    // gateway: 从网关统计
    // service: 从 RPC 统计
    // cache: Redis QPS
    // mq: MQ 吞吐
    // db: DB QPS
    
    LayerTraffic.WithLabelValues("edge").Set(edgeQPS)
    LayerTraffic.WithLabelValues("service").Set(serviceQPS)
    LayerTraffic.WithLabelValues("db").Set(dbQPS)
}

5.2 健康检查与就绪探测

go 复制代码
// health/health.go
package health

// HealthChecker 健康检查器
type HealthChecker struct {
    checks map[string]HealthCheck
}

type HealthCheck func(ctx context.Context) error

func NewHealthChecker() *HealthChecker {
    return &HealthChecker{
        checks: make(map[string]HealthCheck),
    }
}

func (h *HealthChecker) Register(name string, check HealthCheck) {
    h.checks[name] = check
}

// CheckAll 检查所有组件健康状态
func (h *HealthChecker) CheckAll(ctx context.Context) map[string]string {
    results := make(map[string]string)
    
    for name, check := range h.checks {
        err := check(ctx)
        if err != nil {
            results[name] = "unhealthy: " + err.Error()
        } else {
            results[name] = "healthy"
        }
    }
    
    return results
}

// 注册各层健康检查
func RegisterHealthChecks(db *sql.DB, rdb *redis.Client, mq MessageProducer) *HealthChecker {
    hc := NewHealthChecker()
    
    // 数据库检查
    hc.Register("mysql", func(ctx context.Context) error {
        return db.PingContext(ctx)
    })
    
    // Redis 检查
    hc.Register("redis", func(ctx context.Context) error {
        return rdb.Ping(ctx).Err()
    })
    
    // MQ 检查
    hc.Register("mq", func(ctx context.Context) error {
        return mq.Ping()
    })
    
    // CDN / Nginx 依赖的外部检查
    hc.Register("external_api", func(ctx context.Context) error {
        // 检查第三方支付网关等
        return nil
    })
    
    return hc
}

六、压测与容量规划

6.1 压测金字塔

scss 复制代码
         ┌──────────────┐
         │   全链路压测   │  ← 生产环境影子流量
         │   (最真实)    │
         ├──────────────┤
         │   组件压测    │  ← 各组件极限 QPS
         │ Redis/MQ/DB  │
         ├──────────────┤
         │   单服务压测   │  ← 单 Pod 极限 QPS
         │   接口级      │
         ├──────────────┤
         │   单元测试     │  ← 并发安全验证
         │   (race)     │
         └──────────────┘

6.2 Go 并发压测工具集成

go 复制代码
// benchmark/order_bench_test.go
package benchmark

import (
    "sync"
    "sync/atomic"
    "testing"
    "time"
)

// TestConcurrentOrderCreation 并发创建订单压测
func TestConcurrentOrderCreation(t *testing.T) {
    concurrency := 1000 // 1000 并发
    totalReqs := 100000 // 总共 10 万请求
    
    var (
        successCount atomic.Int64
        failCount    atomic.Int64
        totalLatency atomic.Int64 // 纳秒
    )
    
    start := time.Now()
    
    var wg sync.WaitGroup
    semaphore := make(chan struct{}, concurrency)
    
    for i := 0; i < totalReqs; i++ {
        wg.Add(1)
        semaphore <- struct{}{}
        
        go func(idx int) {
            defer wg.Done()
            defer func() { <-semaphore }()
            
            reqStart := time.Now()
            
            // 发送创建订单请求
            err := createOrderRequest(idx)
            
            latency := time.Since(reqStart).Nanoseconds()
            totalLatency.Add(latency)
            
            if err != nil {
                failCount.Add(1)
            } else {
                successCount.Add(1)
            }
        }(i)
    }
    
    wg.Wait()
    elapsed := time.Since(start).Seconds()
    
    t.Logf("=== Benchmark Results ===")
    t.Logf("Total Requests: %d", totalReqs)
    t.Logf("Concurrency: %d", concurrency)
    t.Logf("Duration: %.2fs", elapsed)
    t.Logf("QPS: %.0f", float64(totalReqs)/elapsed)
    t.Logf("Success: %d, Fail: %d", successCount.Load(), failCount.Load())
    t.Logf("Avg Latency: %.2fms", float64(totalLatency.Load())/float64(totalReqs)/1e6)
    t.Logf("Success Rate: %.2f%%", float64(successCount.Load())/float64(totalReqs)*100)
}

6.3 容量规划公式

ini 复制代码
单 Pod QPS = 1000
目标 QPS = 10000
核心服务 Pod 数 = ceil(10000 / 1000) * 2 = 20 (2倍冗余)

数据库连接数:
  maxOpenConns = Pod数 * 每个Pod最大连接数 = 20 * 20 = 400
  (主库 max_connections 需 > 400)

Redis:
  缓存命中率 90% → 实际打到 DB 的 QPS = 10000 * 0.1 = 1000
  DB 需要承受 1000 QPS → 确保 DB 连接池和配置满足

MQ:
  峰值写 QPS = 5000
  MQ 消费者数 ≥ ceil(5000 / 单消费者处理能力)
  单消费者批量处理能力 = 100/s → 消费者数 = 50

七、完整架构落地 CheckList

7.1 最外层 CheckList

  • CDN 缓存静态资源,正确设置 Cache-Control
  • Nginx 配置 limit_connlimit_req 限流
  • 敏感接口(登录/秒杀)单独限制速率
  • WAF 规则生效(SQL注入/XSS/CC攻击拦截)
  • IP 黑名单机制就绪(自动封禁 + 手动解封)
  • 防爬签名验证对关键 API 生效
  • 网关统一鉴权(JWT Token 校验)

7.2 中层 CheckList

  • 核心/非核心服务隔离部署(不同 K8s namespace/node pool)
  • 核心/非核心链路协程池隔离
  • Redis 集群部署,主从 + 哨兵/Cluster 模式
  • 多级缓存策略落地(L1 本地 + L2 Redis)
  • 热点 Key 保护(singleflight / 互斥锁)
  • 缓存穿透/击穿/雪崩防护措施就绪
  • MQ 集群部署(RocketMQ / Kafka)
  • 写流量削峰(同步改异步的接口已确认)
  • 熔断器配置合理(失败阈值、超时时间、半开策略)
  • 降级策略已制定(每个接口有降级兜底返回值)

7.3 底层 CheckList

  • 数据库读写分离(主库写、从库读)
  • 分库分表策略(若订单量超过千万级)
  • 数据库连接池大小合理(不超过 DB max_connections)
  • 所有写接口有幂等保障(唯一约束 / Token / 参数哈希)
  • 高冲突场景使用乐观锁 + 重试
  • 极高冲突场景使用分布式锁 / DB行锁
  • 本地消息表 + 补偿任务就绪
  • 跨服务调用有 Saga/TCC 编排
  • 每日凌晨全量对账脚本
  • 死信队列有人工处理流程

7.4 可观测性 CheckList

  • Prometheus 指标采集(请求量/延迟/各层流量)
  • Grafana 大盘已搭建(QPS/延迟/错误率/缓存命中率)
  • 链路追踪接入(Jaeger / Zipkin)
  • 告警规则配置(DB连接数/缓存命中率/MQ积压/熔断)
  • 健康检查接口(/health, /ready)
  • 日志结构化 + 集中采集(ELK / Loki)

最终总结

高并发防御不是某一个技术点的胜利,而是从边缘到核心层层衰减的系统工程:

scss 复制代码
边缘层 (CDN/Nginx/WAF):把 10000 QPS 拦到 3000
  ↓
缓冲层 (Redis/MQ/熔断):把 3000 QPS 缓冲到 300
  ↓
保底层 (幂等/乐观锁/补偿):300 QPS 安全落地到 DB

每一层都不可替代,层层设防,才能让系统在高并发场景下稳定运行。

相关推荐
青云计划1 小时前
Spring
java·后端·spring
无尽冬.2 小时前
个人八股之string字符串
java·开发语言·经验分享·后端·异世界
RainCity2 小时前
Java Swing 自定义组件库分享(五)
java·笔记·后端
fox_lht4 小时前
第十二章 泛型、接口和生命周期
开发语言·后端·rust
ikoala4 小时前
用了几周明基 RD280UG,我终于明白程序员为什么需要一台“专用显示器”
前端·后端·程序员
文心快码BaiduComate4 小时前
Comate搭载DeepSeek-V4
前端·后端
小杍随笔4 小时前
Axum+Leptos全栈集成实战
开发语言·后端·架构·rust
小小小小宇4 小时前
订单超时自动取消方案详解
后端
java1234_小锋4 小时前
SpringBoot可以同时处理多少请求?
java·spring boot·后端