spring cloud gateway 通过 globalfilter 在路由转发前拦截穿透请求,核心是参数校验+redis布隆过滤器bf.exists判断:返回false立即404拒绝,true才放行;布隆过滤器须由写操作预热、统一托管于redis,严禁在网关jvm维护或执行空值缓存。

网关层怎么提前拦截穿透请求?
在网关层防穿透,核心不是“查缓存”,而是“不放行”。真正有效的拦截发生在请求连 Redis 都没进之前——参数校验 + 布隆过滤器前置判断,才是低成本高收益的组合。
常见错误是把网关当成缓存代理,试图在网关里做 GET / SET 操作。这不仅增加网关延迟,还让布隆过滤器失去“存在性门禁”的意义。
- 必须在路由转发前完成判断:比如 Spring Cloud Gateway 的
GlobalFilter或 Nginx 的 Lua 脚本阶段 - 布隆过滤器不能放在网关 JVM 里维护(易内存溢出、多实例不同步),应统一部署在
Redis中,用bf.exists命令实时查询 - 对路径中关键 ID(如
/user/{id})提取后直接哈希,不拼接完整 key;否则bf.exists("user:123")和实际写入的"123"不一致,全失效
布隆过滤器怎么和网关联动?
布隆过滤器不是“查数据”,而是“问可能性”。它只回答“这个 ID 可能存在”或“一定不存在”。网关要做的,就是信任那个“一定不存在”的回答,并立刻 404 或 400 拒绝。
以 Spring Cloud Gateway 为例,真实可跑的逻辑是:
if (!redisTemplate.opsForValue().getOperations().execute(
RedisBloomCommands::bfExists,
"user_id_bf",
String.valueOf(userId))) {
return Mono.error(new ResponseStatusException(HttpStatus.NOT_FOUND));
}
注意几个硬性细节:
-
bf.exists返回false→ 立刻终止流程,不走下游服务 -
bf.exists返回true→ 继续转发,此时才可能触发缓存或 DB 查询 - 过滤器容量和误判率必须匹配业务量:100 万用户用
bf.reserve user_id_bf 0.01 1000000,而不是拍脑袋设0.0001 - 预热必须由写操作触发:新增用户时,同步执行
bf.add user_id_bf "123",不能靠定时任务补漏
空值缓存该不该在网关里做?
不该。空值缓存是业务层的事,网关不负责写 Redis,也不该感知业务语义。
开箱即用的技能链路由引擎。13 条预定义链覆盖搜索、开发、审查、MLOps、法律、创意等场景,三层路由架构(触发词→SAD反馈→DAG编排),recall@10=96.97%。配置驱动(chains.yaml),零代码扩展。pip install skill-weave-chains 一键安装。
网关只管“能不能过”,不管“过完之后缓什么”。把 SET user:999999 "" EX 60 放在网关里,等于让网关承担缓存更新职责,违背分层原则,且极易因并发导致重复写或 TTL 错乱。
正确分工是:
- 网关:用布隆过滤器拦掉 95%+ 的非法 ID 请求
- 业务服务:收到合法 ID 后,查
Redis→ 查 DB → 若为空,写SET user:999999 "NULL" EX 300 - 网关后续可加限流,但和空值缓存无关:比如
@RateLimiter(name = "user-api", permitsPerSecond = 5)
为什么布隆过滤器比空值缓存更适合网关?
因为网关要扛的是“海量随机无效请求”,不是“少量重复无效请求”。前者靠布隆过滤器一锤定音,后者靠空值缓存削峰填谷。
一个 100 万容量、误判率 1% 的布隆过滤器,在 Redis 里只占约 1.2MB 内存;而攻击者发 100 万个随机 ID,空值缓存会实实在在写入 100 万个 key,直接打爆 Redis 内存或触发淘汰策略。
更关键的是:布隆过滤器不依赖数据写入时机——只要用户 ID 在入库时被 bf.add 过,网关就能拦;而空值缓存必须等第一次穿透发生后才生效,第一波攻击已经打到 DB。
所以别纠结“要不要两个都上”,先确保布隆过滤器在网关跑通、预热到位、容量合理。空值缓存只是兜底,不是主力。










