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 实现跨节点的请求速率控制,确保系统在高并发下不被压垮。
- 结合使用时,布隆过滤器可作为限流的前置筛选,减轻限流器的判断压力,提升整体性能。