redisson分布式布隆过滤器(RBloomFilter)与限流

324 阅读4分钟

Redisson 的分布式布隆过滤器(RBloomFilter)与限流功能是应对高并发场景的利器,前者用于高效判断元素是否存在,后者控制请求速率,两者结合可显著提升系统的安全性和稳定性。以下从原理、实现到应用场景展开说明:

一、分布式布隆过滤器(RBloomFilter)

1. 核心原理

布隆过滤器是一种概率型数据结构,基于哈希函数位数组实现:

  • 添加元素:将元素通过多个哈希函数映射到位数组的多个位置,置为 1。
  • 判断存在:检查元素对应的所有位是否为 1:
    • 若全为 1,返回「可能存在」(有误判概率);
    • 若存在 0,返回「一定不存在」。

Redisson 的 RBloomFilter 将位数组存储在 Redis 中,支持跨节点共享,误判率(FPP)默认设为 0.03(3%)。

2. 初始化与使用

// 初始化布隆过滤器(需指定预计元素数量和误判率)
RBloomFilter<String> bloomFilter = redisson.getBloomFilter("userBloomFilter");
bloomFilter.tryInit(1000000, 0.01); // 预计100万个元素,误判率1%

// 添加元素
bloomFilter.add("user123");

// 判断元素是否存在
boolean mightExist = bloomFilter.contains("user123"); // 可能存在
boolean notExist = bloomFilter.contains("unknown");   // 一定不存在

3. 分布式场景优势

  • 内存共享:所有节点访问同一个 Redis 中的布隆过滤器,避免本地缓存的不一致问题。
  • 动态扩容:支持通过 tryInit() 动态调整容量和误判率。
  • 持久化:结合 Redis 的持久化机制(RDB/AOF),重启后数据不丢失。

二、分布式限流(RateLimiter)

1. 核心原理

Redisson 的分布式限流器基于令牌桶算法实现:

  • 系统以固定速率向桶中添加令牌(Token);
  • 请求需获取令牌才能被处理,无令牌则被拒绝;
  • 通过调整令牌生成速率和桶容量,可精确控制请求速率。

分布式场景下,令牌桶状态存储在 Redis 中,确保多节点间的限流一致性。

2. 基本用法

// 获取限流器(每秒生成5个令牌,桶容量为5)
RRateLimiter rateLimiter = redisson.getRateLimiter("api:limiter");
rateLimiter.trySetRate(RateType.OVERALL, 5, 1, RateIntervalUnit.SECONDS);

// 尝试获取令牌(非阻塞)
boolean acquired = rateLimiter.tryAcquire(1); // 尝试获取1个令牌
if (acquired) {
    // 处理请求
} else {
    // 拒绝请求(限流)
}

// 阻塞式获取令牌
rateLimiter.acquire(1); // 等待直到获取1个令牌

3. 限流模式

  • OVERALL:全局限流,所有节点共享同一个令牌桶(如全局限流 1000 QPS)。
  • PER_CLIENT:客户端级限流,每个节点独立计数(如单节点限流 200 QPS)。

三、布隆过滤器与限流的结合应用

1. 恶意请求拦截

使用布隆过滤器快速判断请求来源是否为黑名单,再结合限流保护系统:

// 初始化黑名单布隆过滤器
RBloomFilter<String> blacklistFilter = redisson.getBloomFilter("blacklist");
// 假设已将恶意IP加入布隆过滤器

// 处理请求
String clientIp = getClientIp(request);

// 1. 先查布隆过滤器(快速判断)
if (blacklistFilter.contains(clientIp)) {
    // 可能是黑名单,进一步精确验证
    if (isReallyInBlacklist(clientIp)) {
        return forbidden(); // 拒绝请求
    }
}

// 2. 通过布隆过滤器后,再限流
RRateLimiter limiter = redisson.getRateLimiter("api:limiter");
if (!limiter.tryAcquire()) {
    return tooManyRequests(); // 限流
}

// 3. 正常处理请求
processRequest(request);

2. 缓存预热与穿透防护

布隆过滤器可拦截不存在的键请求,避免缓存穿透:

RBloomFilter<String> cacheKeyFilter = redisson.getBloomFilter("cacheKeys");

String getFromCache(String key) {
    // 1. 先查布隆过滤器
    if (!cacheKeyFilter.contains(key)) {
        return null; // 一定不存在,直接返回
    }
    
    // 2. 再查缓存
    String value = redisCache.get(key);
    if (value != null) {
        return value;
    }
    
    // 3. 缓存未命中,查数据库(需加锁防止击穿)
    RLock lock = redisson.getLock("cache:lock:" + key);
    lock.lock();
    try {
        // 二次检查
        value = redisCache.get(key);
        if (value != null) {
            return value;
        }
        
        // 查询数据库
        value = db.query(key);
        if (value != null) {
            redisCache.set(key, value);
            // 将新键加入布隆过滤器
            cacheKeyFilter.add(key);
        }
        return value;
    } finally {
        lock.unlock();
    }
}

3. 热点数据限流

对访问频繁的热点数据单独限流,防止过载:

// 对热点商品ID单独限流(如每秒最多1000次请求)
String productId = request.getParam("productId");
RRateLimiter hotProductLimiter = redisson.getRateLimiter("product:" + productId);
hotProductLimiter.trySetRate(RateType.OVERALL, 1000, 1, RateIntervalUnit.SECONDS);

if (!hotProductLimiter.tryAcquire()) {
    return "商品访问过于频繁,请稍后再试";
}

// 处理商品请求
return getProductDetail(productId);

四、配置优化建议

1. 布隆过滤器参数调优

  • 预计元素数量(expectedInsertions):需预估系统最大数据量,避免频繁扩容。
  • 误判率(falseProbability)
    • 降低误判率会增加位数组大小和哈希函数数量,占用更多 Redis 内存;
    • 典型值:0.01(1%)或 0.03(3%)。

2. 限流参数设计

  • 突发流量应对:增大令牌桶容量(burstCapacity),允许短时间内的流量突发。
  • 平滑限流:结合 RateIntervalUnit.MILLISECONDS 设置更小的时间单位,实现更精确的限流。

3. Redis 性能考虑

  • 布隆过滤器和限流器的频繁操作会增加 Redis 压力,建议使用独立的 Redis 实例或集群。
  • 对高并发场景,可在本地缓存热点数据的布隆过滤器结果,减少 Redis 访问。

五、总结

  • 布隆过滤器适合快速判断元素是否存在,虽有一定误判率,但空间效率极高,常用于黑名单拦截、缓存穿透防护。
  • 分布式限流器通过 Redis 实现跨节点的请求速率控制,确保系统在高并发下不被压垮。
  • 结合使用时,布隆过滤器可作为限流的前置筛选,减轻限流器的判断压力,提升整体性能。