supplier是限流策略的可插拔入口,解耦算法与上下文,支持动态切换、组合及装饰,提升可测性、复用性与环境差异化能力。

Java中Supplier<boolean></boolean>可作为限流决策的“可插拔判断入口”,在基于Token Bucket(令牌桶)的分布式限流实现中,它不直接实现算法逻辑,而是解耦限流策略与执行上下文,支持运行时动态切换、组合或增强限流行为。
为什么用Supplier替代硬编码判断
传统限流代码常将“是否放行”写死在业务分支里,例如:
if (redisTemplate.opsForValue().decrement("bucket:api", 1) >= 0) { ... }
这导致策略不可测、难复用、无法按环境/租户/路径差异化。而Supplier把“要不要放行”变成一个可延迟求值、可注入、可缓存、可装饰的函数式接口:
- 便于单元测试:可传入
() -> true或() -> Math.random() > 0.5模拟各种结果 - 支持条件装配:Spring中可根据
@Profile("prod")注入不同Supplier - 天然适配AOP:可在Supplier外层加日志、熔断、降级等装饰器
Supplier如何封装分布式令牌桶核心逻辑
真正的令牌桶状态(剩余令牌数、上一次刷新时间)应由Redis或集中式存储维护。Supplier只负责“调用一次、返回当前是否允许”的语义。典型封装方式如下:
Supplier<boolean> rateLimiter = () -> {
String key = "token-bucket:" + userId;
Long now = System.currentTimeMillis();
// Lua脚本原子执行:填充令牌 + 判断是否足够
Boolean allowed = redisTemplate.execute(rateLimiterScript,
Collections.singletonList(key), now.toString(), "1");
return allowed != null && allowed;
};</boolean>
关键点:
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- Supplier体内部完成完整的一次限流判定,含网络IO和Lua原子操作
- 不缓存结果(避免过期),每次调用都是实时决策
- 异常应包装为RuntimeException,由外层统一处理(如降级为true)
用Supplier链式组合多级限流策略
实际场景中,常需叠加全局QPS、用户级QPS、接口级并发数等多维度控制。Supplier可借助逻辑运算符组合:
Supplier<boolean> globalLimit = getGlobalQpsSupplier();
Supplier<boolean> userLimit = getUserQpsSupplier(userId);
Supplier<boolean> concurrencyLimit = getConcurrencySupplier(apiName);
Supplier<boolean> finalDecision = () ->
globalLimit.get() && userLimit.get() && concurrencyLimit.get();</boolean></boolean></boolean></boolean>
进一步可封装为:
-
短路组合:用
andThen或自定义AndSupplier实现惰性求值 -
权重降级:某Supplier失败时,返回预设安全值(如
fallbackIfException(globalLimit, () -> true)) - 采样开关:开发环境Supplier恒返回true,生产环境才启用真实限流
与Spring生态集成的关键技巧
在Spring Boot项目中,Supplier可作为Bean被管理,实现策略即配置:
- 按条件注入:
@Bean @ConditionalOnProperty("rate-limit.enabled") - 结合
@ConfigurationProperties绑定参数,动态生成Supplier实例 - 用
@RefreshScope支持Nacos/Apollo配置热更新——每次get()都读取最新阈值 - 配合
@Async或CompletableFuture.supplyAsync()做异步限流(适合低敏感场景)
注意:Supplier本身无状态,但其闭包捕获的对象(如RedisTemplate、配置对象)必须是线程安全的。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










