Java 对IP请求进行限流.
高并发系统下, 有三把利器 缓存 降级 限流.
- 缓存: 将常用数据缓存起来, 减少数据库或者磁盘IO
- 降级: 保护核心系统, 降低非核心业务请求响应
- 限流: 在某一个时间窗口内对请求进行限速, 保护系统
本文主要介绍限流, 常见限流算法中又分为计数器算法, 漏桶算法, 令牌桶算法.
计数器算法
比较简单, 直接用一个map + counter即可实现. 请求来了, 以IP为key,
查询下之前响应次数, 如果调用次数超出MAX_COUT, 返回失败, 属于简单粗暴型选手.
漏桶算法
请求全部进入漏桶, 漏桶恒定速率输出反馈. 这样可以保证数据传输平滑,
但是无法预防突发大量请求, 一秒来了100个请求, 都要阻塞排队, 从小水管输出数据.
令牌桶算法
令牌桶是以固定速度往桶里存令牌, 例如一秒存1000个令牌, 业务请求来了, 直接从桶里获取令牌响应输出.
跟漏桶的差异在于, 他可以预存令牌, 如果一秒钟来了100个请求, 桶里有100个令牌,
那么可以立刻响应给客户端, 而不是排队输出.
令牌桶的实现
guava中提供了令牌桶的一个封装实现RateLimiter, 可以直接调用, 省的我们自己包装ConcurrentHashMap + Timer.
我们预设的场景是服务器端提供一个API供不同客户端查询, 要限流每个IP每秒只能调用两次该API.
首先要定义一个服务器端的缓存, 定期清理即可, 缓存 IP : 令牌桶
1 // 根据IP分不同的令牌桶, 每天自动清理缓存
2 private static LoadingCache<String, RateLimiter> caches = CacheBuilder.newBuilder()
3 .maximumSize(1000)
4 .expireAfterWrite(1, TimeUnit.DAYS)
5 .build(new CacheLoader<String, RateLimiter>() {
6 @Override
7 public RateLimiter load(String key) throws Exception {
8 // 新的IP初始化 (限流每秒两个令牌响应)
9 return RateLimiter.create(2);
10 }
11 });
然后在业务代码中进行限流调用
1 private static void login(int i) throws ExecutionException {
2 // 模拟IP的key
3 String ip = String.valueOf(i).charAt(0) + "";
4 RateLimiter limiter = caches.get(ip);
5
6 if (limiter.tryAcquire()) {
7 System.out.println(i + " success " + new SimpleDateFormat("HH:mm:ss.sss").format(new Date()));
8 } else {
9 System.out.println(i + " failed " + new SimpleDateFormat("HH:mm:ss.sss").format(new Date()));
10 }
11 }
模拟客户端调用
1 for (int i = 0; i < 1000; i++) {
2 // 模拟实际业务请求
3 Thread.sleep(100);
4 login(i);
5 }
完整代码

【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通