当前位置: 首页 > news >正文

ABP VNext + Redis Bloom Filter:大规模缓存穿透防护与请求去重

ABP VNext + Redis Bloom Filter:大规模缓存穿透防护与请求去重 🚀


📚 目录

  • ABP VNext + Redis Bloom Filter:大规模缓存穿透防护与请求去重 🚀
    • TL;DR ✨
    • 1. 引言 🎉
    • 2. 环境与依赖 🛠️
    • 3. Bloom Filter 原理简述 🎓
    • 4. ABP 中的配置化注入 🔧
      • 4.1 配置类
      • 4.2 Module 注册
    • 5. 请求去重 Behavior 🔐
      • 5.1 Key 生成:SHA-256 哈希
      • 5.2 自定义异常
      • 5.3 Behavior 实现
    • 6. 缓存穿透防护策略 🔍
    • 7. Prometheus 全链路监控 📈
      • 7.1 中间件配置
      • 7.2 自定义指标
    • 8. 从启动到拦截 🚀


TL;DR ✨

  • 在 ABP VNext 应用层,通过 BloomFilter.Redis.NetCore + StackExchange.Redis 实现高性能、可配置的请求去重与缓存穿透防护 (NuGet)
  • 支持 IOptionsMonitor<BloomSettings> 动态调整预估容量、误判率与 Filter Key
  • 使用 SHA-256 生成紧凑稳定的请求 Key 并结合 ContainsAsync + AddAsync 避免并发竞态
  • 自定义 DuplicateRequestException 映射 HTTP 409,嵌入日志(ILogger)与 Prometheus 指标,全链路可观测 (NuGet)

1. 引言 🎉

在高并发场景下,比如秒杀、验证码、消息幂等等业务,常会遇到:

  • 缓存穿透:恶意或无效 Key 直击数据库,拖垮后端 😱
  • 请求去重:同一业务上下文多次触发同一逻辑,浪费计算与 I/O 🔄

如何在 ABP VNext (.NET 7/8) 中:

  1. 请求去重:管道最前端拦截重复请求
  2. 缓存穿透防护:访问 DB/缓存前快速判断 Key 是否“可能存在”

并在生产级角度引入:

  • 配置化 (IOptionsMonitor<BloomSettings>)
  • SHA-256 Key 生成
  • ContainsAsync + AddAsync 竞态处理
  • Prometheus 全链路监控
ContainsAsync false
ContainsAsync true
客户端发起请求
BloomFilterBehavior
AddAsync + 下游执行
抛出 DuplicateRequestException
调用下游 Handler
返回 HTTP 409

2. 环境与依赖 🛠️

dotnet add package StackExchange.Redis
dotnet add package BloomFilter.Redis.NetCore --version 2.5.2
dotnet add package prometheus-net.AspNetCore
  • BloomFilter.Redis.NetCore:Redis 后端 Bloom Filter 实现 (NuGet)
  • prometheus-net.AspNetCore:ASP.NET Core 集成的 Prometheus 指标服务 (NuGet)

appsettings.json 示例:

{"Redis": {"Configuration": "127.0.0.1:6379"},"BloomSettings": {"FilterKey": "bf:requests","ExpectedItems": 1000000,"FalsePositiveRate": 0.001}
}

3. Bloom Filter 原理简述 🎓

false
true
请求 Key
ContainsAsync?
AddAsync + 放行
抛出 409 异常
  • 结构:大小为 m 的位数组 + k 哈希函数

  • 误判率

    • k = ln2·(m/n)
    • 误判 ≈ (1–e–k·n/m)k
  • 对比 LRU

    • LRU:存全量 Key,无误判
    • Bloom:低内存、高速,允许小概率误判

4. ABP 中的配置化注入 🔧

4.1 配置类

public class BloomSettings
{public string FilterKey { get; set; }public long ExpectedItems { get; set; }public double FalsePositiveRate { get; set; }
}

4.2 Module 注册

public override void ConfigureServices(ServiceConfigurationContext context)
{var config = context.Services.GetConfiguration();// 绑定配置Configure<BloomSettings>(config.GetSection("BloomSettings"));// 注入 Redis 连接context.Services.AddSingleton<IConnectionMultiplexer>(sp =>ConnectionMultiplexer.Connect(config["Redis:Configuration"]));// 注入 BloomFilter(FilterRedisBuilder.Build 来自 BloomFilter.Redis.NetCore)context.Services.AddSingleton<IBloomFilter>(sp =>{var settings = sp.GetRequiredService<IOptionsMonitor<BloomSettings>>().CurrentValue;return FilterRedisBuilder.Build(config["Redis:Configuration"],   // Redis 连接字符串settings.FilterKey,              // Bloom Filter Key & 名称(int)settings.ExpectedItems,     // 预估容量settings.FalsePositiveRate       // 误判率);});// 注册 Pipeline Behaviorcontext.Services.AddTransient(typeof(IPipelineBehavior<,>),typeof(BloomFilterBehavior<,>));
}

FilterRedisBuilder.Build 方法在 vla/BloomFilter.NetCore 库中提供,可配置容量、误判率并返回 IBloomFilter 实例 (GitHub)。


5. 请求去重 Behavior 🔐

5.1 Key 生成:SHA-256 哈希

public static class BloomKeyGenerator
{public static string Generate(object request){var raw = $"{request.GetType().Name}:{JsonSerializer.Serialize(request, new JsonSerializerOptions {IgnoreNullValues = true})}";using var sha = SHA256.Create();var hash = sha.ComputeHash(Encoding.UTF8.GetBytes(raw));return Convert.ToHexString(hash); // .NET 5+ API}
}

🔍 提示:极端高吞吐场景可选用 xxHash 等非加密哈希,进一步降 CPU 开销 (GitHub)。

5.2 自定义异常

public class DuplicateRequestException : BusinessException
{public DuplicateRequestException(): base("DUPLICATE_REQUEST", "请求已被拦截(重复请求)") { }
}

全局异常处理中映射为 HTTP 409 Conflict。

5.3 Behavior 实现

public class BloomFilterBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>where TRequest : IRequest<TResponse>
{private readonly IBloomFilter _bloom;private readonly ILogger _logger;private static readonly Counter ContainsCount = Metrics.CreateCounter("bloom_contains_total", "BloomFilter ContainsAsync 调用总数");private static readonly Counter AddCount = Metrics.CreateCounter("bloom_add_total",      "BloomFilter AddAsync 调用总数");private static readonly Counter DuplicateCount = Metrics.CreateCounter("bloom_duplicate_total","重复请求拦截总数");public BloomFilterBehavior(IBloomFilter bloomFilter,ILogger<BloomFilterBehavior<TRequest, TResponse>> logger){_bloom = bloomFilter;_logger = logger;}public async Task<TResponse> Handle(TRequest request,RequestHandlerDelegate<TResponse> next,CancellationToken cancellationToken){var key = BloomKeyGenerator.Generate(request);ContainsCount.Inc();if (await _bloom.ContainsAsync(key)){DuplicateCount.Inc();_logger.LogWarning("重复请求拦截: {Key}", key);throw new DuplicateRequestException();}AddCount.Inc();await _bloom.AddAsync(key);return await next();}
}

如所用库的 AddAsync 返回布尔值,可用返回值判断“新加”/“已存在”,进一步简化逻辑。


6. 缓存穿透防护策略 🔍

public async Task<ProductDto> GetAsync(Guid productId)
{var key = productId.ToString();// 1. Bloom 预判断if (!await _bloom.ContainsAsync(key)){// 未命中,尝试真实查询并预热var entity = await _repository.GetAsync(productId);if (entity != null){await _bloom.AddAsync(key);}return entity; // null 或真实结果}// 2. 可能存在,直接查询缓存/DBreturn await _repository.GetAsync(productId);
}
false
entity ≠ null
entity = null
true
GetAsync(Product)
ContainsAsync?
Db.GetAsync
AddAsync
返回 null
Cache/DB 查询

✔️ 优化:此策略无需离线“预热”,首访合法请求正常命中并写入 Bloom。


7. Prometheus 全链路监控 📈

7.1 中间件配置

var builder = WebApplication.CreateBuilder(args);// 注册 Prometheus 指标服务 & HTTP 埋点
builder.Services.AddMetricServer();
builder.Services.AddHttpMetrics();var app = builder.Build();
app.UseHttpMetrics();   // 自动统计 HTTP 请求
app.UseMetricServer();  // 暴露 /metrics
app.MapControllers();
app.Run();

Prometheus 客户端包:prometheus-net.AspNetCore (NuGet)。

7.2 自定义指标

  • bloom_contains_total
  • bloom_add_total
  • bloom_duplicate_total
  • false_positive_total(业务层捕获误判并上报)

8. 从启动到拦截 🚀

docker run -d --name redis -p 6379:6379 redis:7
dotnet run --project YourAbpApp
ClientAPIRedisDB第 1 次 请求ContainsAsync(key)falseAddAsync(key)OKGetAsync(key)返回数据200 OK第 2 次 请求ContainsAsync(key)true409 ConflictClientAPIRedisDB

http://www.dtcms.com/a/310971.html

相关文章:

  • 嵌入式第十八课!!数据结构篇入门及单向链表
  • Python 类三大方法体系深度解析:静态方法、类方法与实例方法
  • LeetCode 每日一题 2025/7/28-2025/8/3
  • js的BOM
  • Redis核心机制与实践深度解析:从持久化到分布式锁
  • 中科院开源HYPIR图像复原大模型:1.7秒,老照片变8K画质
  • 北斗变形监测在地质灾害预警中的应用
  • 机器学习消融实验:方法论演进、跨领域应用与前沿趋势
  • sqli-labs:Less-19关卡详细解析
  • 基于单片机智能雨刷器/汽车刮水器设计
  • CCF IVC 2025“汽车安全攻防赛” -- Crypto -- WriteUp
  • 进程 Vs 线程
  • 小迪23年-22~27——php简单回顾(1)
  • 滚珠花键在汽车制造中有哪些高要求?
  • 【05】VisionMaster入门到精通——圆查找
  • 内网渗透——红日靶场七
  • 排序算法-堆排序
  • MybatisPlus-静态工具DB
  • 在 AKS 中运行 Azure DevOps 私有代理-1
  • 云原生三剑客:Kubernetes + Docker + Spring Cloud 实战指南与深度整合
  • 基于 LFU 策略的存储缓存系统设计与实现
  • 深入 Go 底层原理(八):sync 包的实现剖析
  • Node.js 操作 MongoDB
  • 【机器学习】“回归“算法模型的三个评估指标:MAE(衡量预测准确性)、MSE(放大大误差)、R²(说明模型解释能力)
  • 分布式事务----spring操作多个数据库,事务以及事务回滚还有用吗
  • Oracle 11gR2 Clusterware应知应会
  • 【unity组件_Transform 】
  • 设计模式篇:在前端,我们如何“重构”观察者、策略和装饰器模式
  • 蓝桥杯----串口
  • 内存、硬盘与缓存的技术原理及特性解析