Skip to content

Latest commit

 

History

History
394 lines (323 loc) · 14 KB

File metadata and controls

394 lines (323 loc) · 14 KB

RCU 快照架构设计

整体架构

┌─────────────────────────────────────────────────────────────┐
│                     HTTP API Layer                          │
│                  (api.Server)                               │
└────────────────────┬────────────────────────────────────────┘
                     │
                     ▼
┌─────────────────────────────────────────────────────────────┐
│                  Rate Limit Engine                          │
│                   (core.Engine)                             │
│  ┌──────────────────────────────────────────────────────┐   │
│  │  • 黑白名单检查                                        │   │
│  │  • 维度哈希计算                                        │   │
│  │  • 配额检查                                            │   │
│  │  • 策略执行                                            │   │
│  └──────────────────────────────────────────────────────┘   │
└────────────┬───────────────────────────────┬────────────────┘
             │                               │
             ▼                               ▼
┌─────────────────────────┐     ┌──────────────────────────────┐
│   Rules Cache           │     │   Strategy Implementations   │
│   (rules.Cache)         │     │   • Sliding Window           │
│                         │     │   • Token Bucket             │
│  ┌──────────────────┐   │     │   • Leaky Bucket             │
│  │  RCU Snapshot    │   │     └──────────────────────────────┘
│  │  ┌────────────┐  │   │
│  │  │ Immutable  │  │   │
│  │  │  RuleSet   │  │   │
│  │  └────────────┘  │   │
│  └──────────────────┘   │
└────────────┬────────────┘
             │
             ▼
┌─────────────────────────────────────────────────────────────┐
│                      Redis Storage                          │
│  • 规则存储 (Hash/String)                                    │
│  • 限流状态 (String/Sorted Set)                              │
│  • 更新通知 (Pub/Sub)                                        │
└─────────────────────────────────────────────────────────────┘

RCU 快照数据流

读取路径(Read Path - 热路径)

┌─────────────┐
│ HTTP 请求   │
└──────┬──────┘
       │
       ▼
┌─────────────────────────┐
│ api.Server.HandleCheck  │
└──────────┬──────────────┘
           │
           ▼
┌─────────────────────────────────┐
│ rules.Cache.Resolve()           │
│                                 │
│  snapshot := c.ruleSnap.Load()  │  ← 无锁原子读取 (~0.03ns)
│  return snapshot.Rules[id]      │
└──────────┬──────────────────────┘
           │
           ▼
┌──────────────────────┐
│ core.Engine.Allow()  │
└──────────────────────┘

性能特点:
✅ 无锁操作
✅ CPU 缓存友好
✅ 延迟极低 (~0.03ns)
✅ 支持百万级并发

写入路径(Write Path - 冷路径)

┌──────────────────┐
│ 规则更新触发     │
│ • API 调用       │
│ • Redis Pub/Sub  │
│ • 定时重载       │
└────────┬─────────┘
         │
         ▼
┌────────────────────────────────────────┐
│ rules.Cache.Upsert() / ReloadAll()     │
│                                        │
│ 1. oldSnap := c.ruleSnap.Load()        │  ← 读取旧快照
│                                        │
│ 2. newRules := copy(oldSnap.Rules)     │  ← 复制数据
│    newRules[id] = updatedRule          │     修改副本
│                                        │
│ 3. newSet := &ImmutableRuleSet{...}    │  ← 创建新快照
│                                        │
│ 4. c.ruleSnap.Replace(newSet)          │  ← 原子替换
└────────────────────────────────────────┘
         │
         ▼
┌────────────────────┐
│ 旧快照等待 GC 回收 │
└────────────────────┘

性能特点:
⚠️ 需要复制数据
✅ 不阻塞读操作
✅ 原子替换保证一致性

内存模型

快照生命周期

时间线:
────────────────────────────────────────────────────────────►

T0: 初始状态
    ┌─────────────┐
    │  Snapshot   │
    │  Pointer ───┼──→ ┌─────────────┐
    └─────────────┘    │  Version 1  │
                       │  Rules: 100 │
                       └─────────────┘

T1: 开始更新(复制)
    ┌─────────────┐
    │  Snapshot   │
    │  Pointer ───┼──→ ┌─────────────┐
    └─────────────┘    │  Version 1  │ ← 100 个读者引用
                       │  Rules: 100 │
                       └─────────────┘
                       
                       ┌─────────────┐
                       │  Version 2  │ ← 正在构建
                       │  Rules: 101 │
                       └─────────────┘

T2: 原子替换
    ┌─────────────┐    ┌─────────────┐
    │  Snapshot   │    │  Version 1  │ ← 旧读者仍在使用
    │  Pointer ───┼──→ │  Rules: 100 │
    └─────────────┘ ╲  └─────────────┘
                     ╲
                      ╲ ┌─────────────┐
                       ╲│  Version 2  │ ← 新读者使用
                        │  Rules: 101 │
                        └─────────────┘

T3: 旧版本被回收
    ┌─────────────┐
    │  Snapshot   │
    │  Pointer ───┼──→ ┌─────────────┐
    └─────────────┘    │  Version 2  │
                       │  Rules: 101 │
                       └─────────────┘
                       
                       ┌─────────────┐
                       │  Version 1  │ ← GC 回收
                       │  [deleted]  │
                       └─────────────┘

内存开销估算

假设规则集大小为 S 字节:

场景 内存占用 说明
稳定状态 S 只有一个版本
更新瞬间 2S 新旧两个版本共存
高频更新 2S~3S 多个版本短暂共存
极限情况 NS N个并发更新未完成

建议

  • 单个规则集控制在 1-10 MB
  • 更新频率控制在 < 10次/秒
  • 避免在事务中持有快照引用

并发控制对比

传统 RWMutex 方案

type Cache struct {
    mu   sync.RWMutex
    data map[string]Rule
}

// 读取(有锁)
func (c *Cache) Get(id string) Rule {
    c.mu.RLock()              // 🔒 获取读锁
    defer c.mu.RUnlock()      // 🔓 释放读锁
    return c.data[id]         // 读取数据
}

// 更新(写锁阻塞所有读者)
func (c *Cache) Update(id string, r Rule) {
    c.mu.Lock()               // 🔒 获取写锁(阻塞所有读者)
    defer c.mu.Unlock()       // 🔓 释放写锁
    c.data[id] = r            // 修改数据
}

缺点

  • ❌ 读操作需要加锁(~200ns 开销)
  • ❌ 写操作阻塞所有读者
  • ❌ 锁竞争导致性能下降
  • ❌ 可能出现优先级反转

RCU Snapshot 方案

type Cache struct {
    ruleSnap *rcu.Snapshot[ImmutableRuleSet]
}

// 读取(无锁)
func (c *Cache) Get(id string) Rule {
    snap := c.ruleSnap.Load()  // ⚡ 原子读指针 (~0.03ns)
    return snap.Rules[id]      // 读取数据
}

// 更新(不阻塞读者)
func (c *Cache) Update(id string, r Rule) {
    old := c.ruleSnap.Load()   // ⚡ 读取旧快照
    
    // 复制并修改
    new := make(map[string]Rule)
    for k, v := range old.Rules {
        new[k] = v
    }
    new[id] = r
    
    // 原子替换(读者不受影响)
    c.ruleSnap.Replace(&ImmutableRuleSet{Rules: new})
}

优点

  • ✅ 读操作零开销
  • ✅ 写操作不阻塞读者
  • ✅ 无锁竞争
  • ✅ 线性扩展

关键代码位置

核心实现

文件 关键函数 说明
internal/rcu/snapshot.go Load() 无锁读取快照
internal/rcu/snapshot.go Replace() 原子替换快照
internal/rules/cache.go Resolve() 规则查询(使用快照)
internal/rules/cache.go ReloadAll() 全量重载(替换快照)
internal/rules/cache.go Upsert() 单条更新(复制-替换)

测试与文档

文件 说明
internal/rcu/snapshot_test.go 单元测试和基准测试
internal/rcu/README.md RCU 详细文档
docs/RCU_INTEGRATION.md 集成说明
docs/RCU_ARCHITECTURE.md 架构设计(本文档)

监控与调试

建议的监控指标

  1. 规则集指标

    // 规则数量
    ruleCount := len(cache.GetSnapshot().Rules)
    
    // 估算内存大小
    memSize := ruleCount * avgRuleSize
  2. 更新频率

    // 每分钟更新次数
    var updateCounter atomic.Int64
    
    func (c *Cache) Upsert(...) {
        updateCounter.Add(1)
        // ...
    }
  3. 快照版本

    type ImmutableRuleSet struct {
        Version   int64
        Timestamp time.Time
        Rules     map[string]config.Rule
    }

调试技巧

  1. 查看当前规则集

    # 通过 HTTP API
    curl http://localhost:8080/api/rules
  2. 追踪更新事件

    func (c *Cache) Replace(newSet *ImmutableRuleSet) {
        slog.Info("snapshot replaced",
            "old_count", len(c.ruleSnap.Load().Rules),
            "new_count", len(newSet.Rules))
        c.ruleSnap.Replace(newSet)
    }
  3. 内存分析

    # 启用 pprof
    go tool pprof http://localhost:6060/debug/pprof/heap

未来优化方向

1. 增量更新优化

当规则集很大时,可以考虑增量更新:

type DeltaUpdate struct {
    Added   map[string]Rule
    Removed []string
}

func (c *Cache) ApplyDelta(delta DeltaUpdate) {
    old := c.ruleSnap.Load()
    new := make(map[string]Rule, len(old.Rules))
    
    // 复制旧规则
    for k, v := range old.Rules {
        if !contains(delta.Removed, k) {
            new[k] = v
        }
    }
    
    // 添加新规则
    for k, v := range delta.Added {
        new[k] = v
    }
    
    c.ruleSnap.Replace(&ImmutableRuleSet{Rules: new})
}

2. COW(Copy-on-Write)优化

使用持久化数据结构减少复制开销:

import "github.com/emirpasic/gods/maps/treemap"

type ImmutableRuleSet struct {
    Rules *treemap.Map  // 持久化树结构
}

3. 版本化快照

支持查询历史版本:

type VersionedSnapshot struct {
    current  atomic.Pointer[ImmutableRuleSet]
    history  []ImmutableRuleSet  // 保留最近N个版本
}

总结

RCU 快照机制为 Pixiu-RLS 提供了:

  1. 极致性能:读操作 0.03ns,提升 60+ 倍
  2. 并发安全:无锁设计,避免竞争
  3. 简洁代码:消除复杂的锁逻辑
  4. 可扩展性:支持百万级并发

这是高性能限流系统的关键优化之一。