[1.1 代码实现](#1.1 代码实现)
[1.2 postman测试(批量测试20次)](#1.2 postman测试(批量测试20次))
[1.3 优缺点](#1.3 优缺点)
[2.1 代码实现](#2.1 代码实现)
[2.2 测试说明](#2.2 测试说明)
[2.3 优缺点](#2.3 优缺点)
1.固定窗口限流
1.1 代码实现
java
public class FixWindowLimiter {
/**
* 每秒限制请求数
*/
private static final long perSecondLimit = 2;
/**
* 上一个窗口的开始时间
*/
public static long preStartTime = System.currentTimeMillis();
/**
* 计数器
*/
private static int counter;
public static synchronized boolean tryAcquire() {
long now = System.currentTimeMillis();
// 假设窗口时间位1秒,在窗口期内判断计数器是否超过限制的请求数
if (now - preStartTime < 1000) {
// 计数器小于限制数时放行,否则拒绝请求
if (counter < perSecondLimit) {
counter++;
return true;
} else {
return false;
}
}
// 时间窗口过期,重置计数器和时间戳
counter = 0;
preStartTime = now;
return true;
}
}
这里限定每秒内的请求数为2,当请求数大于2的时候,拒绝请求。下面是测试的controller
java
@Controller
public class infoController {
@GetMapping("/fix")
@ResponseBody
public String fix(String name){
System.out.println(name);
if (FixWindowLimiter.tryAcquire()){
return "true";
}else{
return "false";
}
}
}
1.2 postman测试(批量测试20次)
前两次返回true,说明请求可以正常访问程序;第三次返回false,说明请求被限流。
1.3 优缺点
优点:
- 实现简单,容易理解。
- 适用于突发流量较小的场景。
缺点:
- 无法处理时间窗口的临界突变问题。
- 对于高并发场景,难以保证系统稳定性。
- 无法实现更加精细的限流控制。
2.滑动窗口限流
滑动窗口算法相当于对固定窗口算法的一种改进。在滑动窗口算法中,主要是通过维护一个固定大小的时间窗口 ,并随着时间的推移向前滑动来计算窗口内的请求总数。这样可以在不牺牲实时性的情况下,平滑地处理流量变化。
2.1 代码实现
java
package com.cocoa.rateLimiter;
import java.util.concurrent.TimeUnit;
public class SlidingWindowLimiter {
// 固定时间窗口大小,单位毫秒
private long windowSize;
// 固定窗口拆分的小窗口数
private int windowNum;
// 每个窗口允许通过最大请求数
private int maxRequestCount;
// 各个窗口内请求计数
private int[] perWindowCount;
// 请求总数
private int totalCount;
// 当前窗口下标
private int windowId;
// 每个小窗口大小,毫秒
private long perWindowSize;
// 窗口右边界
private long windowRightBorder;
/**
* 构造函数
*
* @param windowSize 固定时间窗口大小
* @param windowNum 固定窗口拆分的小窗口数
* @param maxRequestCount 每个窗口允许通过最大请求数
*/
public SlidingWindowLimiter(long windowSize, int windowNum, int maxRequestCount) {
this.windowSize = windowSize;
this.windowNum = windowNum;
this.maxRequestCount = maxRequestCount;
perWindowCount = new int[windowNum];
perWindowSize = windowSize / windowNum;
windowRightBorder = System.currentTimeMillis();
}
/**
* 限流方法
* @return
*/
public synchronized boolean tryAcquire() {
long currentTime = System.currentTimeMillis();
if (currentTime > windowRightBorder){// 窗口移动
do {
windowId = (++windowId) % windowNum;
totalCount -= perWindowCount[windowId];
perWindowCount[windowId]=0;
windowRightBorder += perWindowSize;
}while (windowRightBorder < currentTime);
}
if (totalCount < maxRequestCount){
System.out.println("tryAcquire success, windowId = "+windowId);
perWindowCount[windowId]++;
totalCount++;
return true;
}else{
System.out.println("tryAcquire fail, windowId = "+windowId);
return false;
}
}
/**
* 测试方法
* @param args
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
// 10个小窗口,每个窗口100ms, 可以接收的最大请求数为10
SlidingWindowLimiter slidingWindowLimiter = new SlidingWindowLimiter(1000, 10, 10);
//TimeUnit.MILLISECONDS.sleep(900);// 构建窗口
for (int i = 0; i < 40; i++) {
boolean acquire = slidingWindowLimiter.tryAcquire();
if (acquire){
System.out.println("任务" + (i + 1) + "执行任务 " + System.currentTimeMillis());
}else{
System.out.println("任务" + (i + 1) + "被限流 "+ System.currentTimeMillis());
}
TimeUnit.MILLISECONDS.sleep(50);
}
}
}
2.2 测试说明
每个任务延时50ms,测试结果如下:前10个任务正常执行,后面的任务被拒绝。
前10次将窗口占满,后续部分窗口移动。
2.3 优缺点
优点:
- 可以根据业务需求灵活调整窗口的大小和时间间隔,实现更加精细的限流控制。
- 解决了固定窗口算法的窗口边界问题,避免突发流量压垮服务器。
缺点:
- 窗口的大小和时间间隔需要根据具体业务场景进行调整,实现较为复杂。
- 需要统计窗口内的请求次数,计算较为复杂。
3.漏桶算法
漏桶算法是一种基于固定速率的流量控制算法。在漏桶算法中,请求像水一样不断地注入漏桶,而漏桶会按照固定的速率将水漏掉。如果注入的速率持续大于漏出的速率,则会出现限流的效果。漏桶算法可以限制请求的速率,并且可以防止出现过载的情况。如果入口流量过大,漏桶可能会溢出,导致数据丢失。
3.1代码实现
java
package com.cocoa.rateLimiter;
import java.util.concurrent.TimeUnit;
public class LeakyBucketLimiter {
/**
* 桶的最大容量
*/
public long capacity = 10;
/**
* 桶内当前水量
*/
public long count = 0;
/**
* 漏水速率(每秒5次)
*/
public long rate = 5;
/**
* 上次漏水时间
*/
public static long lastLeakTime = System.currentTimeMillis();
/**
* 限流方法,返回true表示通过
*/
public synchronized boolean tryAcquire() {
// 调用漏水方法
this.leak();
// 判断是否超过最大请求数量
if (count < capacity) {
count++;
return true;
}
return false;
}
/**
* 漏水方法,计算并更新这段时间内漏水量
*/
private void leak() {
// 获取系统当前时间
long currentTime = System.currentTimeMillis();
// 计算这段时间内,需要流出的水量
long leakWater = (currentTime - lastLeakTime) * rate / 1000;
count = Math.max(count - leakWater, 0);
lastLeakTime = currentTime;
}
public static void main(String[] args) throws InterruptedException {
LeakyBucketLimiter limiter = new LeakyBucketLimiter();
for (int i = 0; i < 30; i++){
if (limiter.tryAcquire()){
System.out.println("任务" + (i + 1) + "成功执行");
}else {
System.out.println("任务" + (i + 1) + "被限流");
}
TimeUnit.MILLISECONDS.sleep(200);// 每次都可以漏出一个请求(每200ms可以成功执行一个请求,漏桶让出一个位置)
// TimeUnit.MILLISECONDS.sleep(100);// 100ms 会导致每次无法漏出请求,漏桶满了无法继续接收新的请求
}
}
}
3.2测试说明
3.3优缺点
优点:
- 可以限制请求的速率,并且不会出现过载的情况。
- 可以实现较为精细的限流控制。
缺点:
- 如果入口流量过大,超过了桶的容量,那么就需要丢弃部分请求。
- 由于速率是固定的,即使下游能够处理更大的流量,漏桶也不允许突发流量通过。
4.令牌桶算法
令牌桶算法是一种基于令牌的流量控制算法。在令牌桶算法中,系统会向令牌桶中不断添加令牌,每个请求会消耗掉一个令牌,如果令牌桶中没有足够的令牌,则请求会被拒绝。令牌桶算法可以限制请求的速率,同时不会出现过载的情况。
4.1代码实现
java
package com.cocoa.rateLimiter;
import java.util.concurrent.TimeUnit;
public class TokenBucketRateLimiter {
/**
* 桶的最大容量
*/
public long capacity = 10;
/**
* 桶内当前的令牌数量
*/
public long count = 0;
/**
* 令牌生成速率(每秒5次)
*/
public long tokenRate = 5;
/**
* 上次生成令牌的时间
*/
public long lastGenerateTime = System.currentTimeMillis();
/**
* 限流方法,返回true表示通过
*/
public boolean limit() {
// 调用生成令牌方法
this.generateTokens();
// 判断桶内是否还有令牌
if (count > 0) {
count--;
return true;
}
return false;
}
/**
* 生成令牌方法,计算并更新这段时间内生成的令牌数量
*/
private void generateTokens() {
long currentTime = System.currentTimeMillis();
// 计算这段时间内,需要生成的令牌数量
long tokens = (currentTime - lastGenerateTime) * tokenRate / 1000;
count = Math.min(count + tokens, capacity);
lastGenerateTime = currentTime;
}
public static void main(String[] args) throws InterruptedException {
TokenBucketRateLimiter limiter = new TokenBucketRateLimiter();
TimeUnit.MILLISECONDS.sleep(1000);// 生成5个令牌
limiter.generateTokens();
for (int i = 0; i < 20; i++) {
if (limiter.limit()){
System.out.println("任务" + (i + 1) + "执行任务 ");
}else{
System.out.println("任务" + (i + 1) + "被限流 ");
}
}
}
}
4.2测试说明
4.3优缺点
优点:
- 令牌桶算法可以处理突发流量。当桶满时,能够以最大速度处理请求。
- 与漏桶算法相比,令牌桶算法提供了更大的灵活性。
缺点:
- 需要维护令牌桶和令牌生成速度等状态信息,实现较为复杂。
- 当令牌桶溢出时,会导致请求被拒绝,影响用户体验。