Java 对IP请求进行限流.
时间:2022-04-24
本文章向大家介绍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 }
完整代码
1 public class doLimit {
2
3 // 根据IP分不同的令牌桶, 每天自动清理缓存
4 private static LoadingCache<String, RateLimiter> caches = CacheBuilder.newBuilder()
5 .maximumSize(1000)
6 .expireAfterWrite(1, TimeUnit.DAYS)
7 .build(new CacheLoader<String, RateLimiter>() {
8 @Override
9 public RateLimiter load(String key) throws Exception {
10 // 新的IP初始化 (限流每秒两个令牌响应)
11 return RateLimiter.create(2);
12 }
13 });
14
15 public static void main(String[] args) throws InterruptedException, ExecutionException {
16 for (int i = 0; i < 1000; i++) {
17 // 模拟实际业务请求
18 Thread.sleep(100);
19 login(i);
20 }
21 }
22
23 private static void login(int i) throws ExecutionException {
24 // 模拟IP的key
25 String ip = String.valueOf(i).charAt(0) + "";
26 RateLimiter limiter = caches.get(ip);
27
28 if (limiter.tryAcquire()) {
29 System.out.println(i + " success " + new SimpleDateFormat("HH:mm:ss.sss").format(new Date()));
30 } else {
31 System.out.println(i + " failed " + new SimpleDateFormat("HH:mm:ss.sss").format(new Date()));
32 }
33 }
34 }
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- Kears 使用:通过回调函数保存最佳准确率下的模型操作
- django form和field具体方法和属性说明
- 总结PHP中初始化空数组的最佳方法
- tensorflow使用CNN分析mnist手写体数字数据集
- PHP7 mongoDB扩展使用的方法分享
- 主流开源分布式图数据库 Benchmark
- PHP封装的简单连接MongoDB类示例
- 基于Tensorflow的MNIST手写数字识别分类
- Yii框架ACF(accessController)简单权限控制操作示例
- tensorflow 动态获取 BatchSzie 的大小实例
- TP5.0框架实现无限极回复功能的方法分析
- Tensorflow之MNIST CNN实现并保存、加载模型
- tensorflow 大于某个值为1,小于为0的实例
- YII框架模块化处理操作示例
- 基于tensorflow for循环 while循环案例