目录
1. 令牌桶算法
1.1 漏桶算法的原理:
- 漏桶:像一个虚拟的漏桶一样,有一个固定的容量,可以存放一定数量的请求。
- 请求进入漏桶:当请求到达时,会被放入漏桶中,占据一定的容量。
- 处理请求:系统以恒定的速率处理漏桶中的请求,不受请求到达的速率影响。
- 请求离开漏桶:每个请求在被处理后,以恒定的速率从漏桶中释放出去,就像水从漏桶中流出一样。
1.2 漏桶算法的作用:
- 平滑流量:漏桶算法能够平滑地控制系统输出的流量,使得系统能够稳定地提供服务,防止突发大量请求导致系统崩溃或性能下降。
- 控制输出速率:通过设置恒定的处理速率,漏桶算法可以控制系统对外提供服务的输出速度,保护系统的稳定性和可用性。
- 保护系统资源:漏桶算法可以限制系统对资源的消耗,防止恶意请求或异常情况下的过载对系统造成损害。
- 平衡系统负载:漏桶算法可以分散请求的压力,平衡系统的负载,避免部分资源过度利用而导致其他资源无法正常工作。
1.3 实现用例
- 逻辑类
package com.jmh.demo03.current.utils;
public class RateLimiter {
private static RateLimiter instance;
private final int maxTokens;
private final int tokensPerSecond;
private int availableTokens;
private long lastRefillTimestamp;
private RateLimiter(int maxTokens, int tokensPerSecond) {
this.maxTokens = maxTokens;
this.tokensPerSecond = tokensPerSecond;
this.availableTokens = maxTokens;
this.lastRefillTimestamp = System.currentTimeMillis();
}
public static synchronized RateLimiter getInstance(int maxTokens, int tokensPerSecond) {
if (instance == null) {
instance = new RateLimiter(maxTokens-1, tokensPerSecond);
}
return instance;
}
public synchronized boolean isAllowed() {
refillTokens();
if (availableTokens > 0) {
availableTokens--;
return true;
}
return false;
}
private void refillTokens() {
long currentTime = System.currentTimeMillis();
long elapsedTime = currentTime - lastRefillTimestamp;
long refillInterval = 1000 / tokensPerSecond;
int tokensToAdd = (int)(elapsedTime / refillInterval);
if (tokensToAdd > 0) {
lastRefillTimestamp = currentTime;
if (availableTokens + tokensToAdd <= maxTokens) {
availableTokens += tokensToAdd;
} else {
availableTokens = maxTokens;
}
}
}
}
- 调用类
package com.jmh.demo03.current.controller;
import com.jmh.demo03.current.utils.LeakyBucketRateLimiter;
import com.jmh.demo03.current.utils.RateLimiter;
import lombok.SneakyThrows;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;
/**
* @author 蒋明辉
* @data 2023/8/8 16:16
*/
@RequestMapping("/current")
@RestController
public class currentController {
//令牌桶
//参数:容量(10)和令牌产生速率(10)。
//工作原理:在令牌桶中以固定的速率生成令牌,每个令牌代表一个请求。仅当有令牌可用时,才能进行接口调用。
//效果:在开始时,令牌桶中有 10 个令牌。每秒钟会生成 10 个新令牌,所以在第一秒内您可以连续调用接口 10 次。如果超过了 10 次调用,则需要等待后续的秒数以获取新的令牌。
private final RateLimiter rateLimiter = RateLimiter.getInstance(10, 1);
/**
* 令牌桶限流
*
* @return 限流结果
*/
@PostMapping("demo01")
@SneakyThrows
public boolean demo01() {
return rateLimiter.isAllowed();
}
}
2. 漏桶算法
2.1 令牌桶算法的原理:
- 令牌生成:在令牌桶中以固定速率生成令牌,比如每秒生成一定数量的令牌。
- 令牌消耗:当请求到达时,需要从令牌桶中获取一个令牌,如果桶中有可用的令牌,则将其发放给请求,并进行处理;如果桶中没有足够的令牌,则拒绝该请求。
- 令牌存储:令牌桶具有一个最大容量,即令牌桶中可以存储的最大令牌数量。当令牌生成速度超过令牌消耗速度时,多余的令牌会被存储在桶中,但不会超过最大容量。
2.2 令牌桶算法的作用:
- 平滑流量:令牌桶算法能够平滑地控制请求的流量,使得系统能够以稳定的速率处理请求,防止突发大量请求导致系统崩溃或性能下降。
- 控制访问速率:通过限制令牌生成的速率,令牌桶算法可以控制系统对外提供服务的访问速率,保护系统的稳定性和可用性。
- 灵活适应突发流量:令牌桶算法允许在短时间内产生大量的令牌,以处理短期的突发流量,而不会导致长期的性能问题。
- 公平性:令牌桶算法保证了请求的公平性,每个请求在获取令牌时都是平等的,没有特权或优先级。
2.3 实现用例
- 逻辑类
package com.jmh.demo03.current.utils;
import java.time.Duration;
import java.time.Instant;
public class LeakyBucketRateLimiter {
private final int capacity; // 漏斗容量
private final int rate; // 每秒流出的速率
private int water; // 当前水量
private Instant lastRequestTime; // 上次请求时间
public LeakyBucketRateLimiter(int capacity, int rate) {
this.capacity = capacity;
this.rate = rate;
this.water = 0;
this.lastRequestTime = Instant.now();
}
public synchronized boolean isAllowed() {
Instant now = Instant.now();
Duration elapsedTime = Duration.between(lastRequestTime, now);
lastRequestTime = now;
int elapsedSeconds = (int) elapsedTime.getSeconds();
water = Math.max(0, water - elapsedSeconds * rate);
if (water < capacity) {
water++;
return true; // 允许请求通过
}
return false; // 限制请求访问
}
}
- 调用类
package com.jmh.demo03.current.controller;
import com.jmh.demo03.current.utils.LeakyBucketRateLimiter;
import com.jmh.demo03.current.utils.RateLimiter;
import lombok.SneakyThrows;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;
/**
* @author 蒋明辉
* @data 2023/8/8 16:16
*/
@RequestMapping("/current")
@RestController
public class currentController {
//漏桶
//参数:容量(10)和流出速率(10)。
//工作原理:漏桶按照固定的速率流出请求,不管请求是否连续到达。当漏桶存储的请求数量超过容量时,多余的请求将被丢弃。
//效果:在开始时,漏桶为空,可以立即调用接口。但是,一旦第一秒内的调用超过了容量(10 次),后续的请求将被漏桶丢弃,直到漏桶重新流出新的请求(每秒 10 次)。
private final LeakyBucketRateLimiter limiter = new LeakyBucketRateLimiter(10, 10);
/**
* 漏桶限流
* @return 限流结果
*/
@PostMapping("demo02")
@SneakyThrows
public boolean demo02() {
return limiter.isAllowed();
}
}