限流策略是确保系统在高负载情况下稳定运行的重要手段,它通过对系统资源的使用施加一定的限制,防止系统因过载而崩溃。
常见的限流策略包括固定窗口限流、滑动窗口限流、漏桶限流和令牌桶限流等。
限流策略
1. 固定窗口限流:
• 原理:将时间划分为固定长度的窗口(如1秒),每个窗口内允许通过的请求数量有固定的上限。
每当新的请求到达时,系统检查当前窗口内的请求数量是否已达到阈值,若是则拒绝请求,否则接受请求并递增计数。
• 特点:实现简单,但可能存在流量突刺现象,即在一个窗口的开始时刻,所有请求都会被接受,而在窗口结束时刻,所有请求都会被拒绝。
2. 滑动窗口限流:
• 原理:设定一个时间窗口,但窗口是连续滑动的,每个请求都会落入一个时间窗口内。
系统累计最近一段时间(如1秒内)的所有窗口的请求数量,当累计请求数超过阈值时,拒绝后续请求。
• 特点:能平滑处理请求流量,避免固定窗口限流中的“突刺”问题,适用于对请求速率波动较大、需要更精细控制的服务,如实时交易、用户登录等高并发场景。
3. 漏桶限流:
• 原理:使用一个虚拟的“桶”,桶的流出速率(即处理请求的速率)是恒定的,而流入速率(即请求到达的速率)可能变化。
无论流入速率如何,系统均以固定速率处理请求,多余的请求要么排队等待,要么直接拒绝。
• 特点:能平滑输出流量,防止突发请求对后端服务造成冲击,适用于需要严格控制处理速率、保证公平性的场景,如网络传输、API调用速率限制等。
4. 令牌桶限流:
• 原理:令牌桶算法通过在固定时间间隔生成令牌,并为请求分配令牌来决定是否允许请求进入。
如果令牌桶满了,新增的令牌会被丢弃,而没有令牌的请求则会被限制进入系统。
• 特点:能够更灵活地应对突发流量,在限流的同时可以允许一定量的突发请求,适用于对流量突刺容忍度较高,且希望流量平滑的场景。
滑动窗口算法与令牌桶的区别
• 滑动窗口算法: • 关注的是单位时间内的请求总量,通过连续滑动的窗口来累计请求数,当累计请求数超过阈值时拒绝后续请求。 • 适用于对请求速率波动较大、需要更精细控制的服务。 • 令牌桶算法: • 关注的是请求的平均速率,通过以恒定速率生成令牌并为请求分配令牌来决定是否允许请求进入。 • 允许一定程度的突发流量,当有大量请求流入时,可以使用堆积的令牌去应对。
使用场景及示例
滑动窗口算法使用场景:
• 实时交易、用户登录等高并发场景:这些场景请求速率波动较大,需要更精细的流量控制。
滑动窗口算法示例:
import java.util.ArrayDeque;
import java.util.Deque;
publicclassSlidingWindowMax {
publicint[] maxSlidingWindow(int[] nums, int k) {
intn= nums.length;
int[] result = newint[n - k + 1];
Deque<Integer> deque = newArrayDeque<>();
for (inti=0; i < n; i++) {
// 移除窗口外的元素
while (!deque.isEmpty() && deque.peekFirst() <= i - k) {
deque.pollFirst();
}
// 移除窗口内比当前元素小的元素
while (!deque.isEmpty() && nums[deque.peekLast()] <= nums[i]) {
deque.pollLast();
}
// 添加当前元素到窗口
deque.offerLast(i);
// 计算当前窗口的最大值
if (i + 1 >= k) {
result[i + 1 - k] = nums[deque.peekFirst()];
}
}
return result;
}
}
令牌桶算法使用场景:
• 网络传输、API调用速率限制等:这些场景需要严格控制处理速率,保证公平性。
令牌桶算法示例:
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
publicclassTokenBucketRateLimiter {
privatefinal AtomicInteger tokens;
privatefinalint capacity;
privatefinallong refillInterval;
privatefinallong refillTokens;
privatefinal ScheduledExecutorService scheduler;
publicTokenBucketRateLimiter(int capacity, long refillInterval, long refillTokens) {
this.tokens = newAtomicInteger(capacity);
this.capacity = capacity;
this.refillInterval = refillInterval;
this.refillTokens = refillTokens;
this.scheduler = newScheduledThreadPoolExecutor(1);
this.scheduler.scheduleAtFixedRate(() -> {
intavailableTokens= capacity - tokens.get();
inttokensToAdd= Math.min(availableTokens, refillTokens);
tokens.addAndGet(tokensToAdd);
}, refillInterval, refillInterval, TimeUnit.MILLISECONDS);
}
publicbooleantryAcquire() {
intcurrentTokens= tokens.get();
if (currentTokens > 0) {
return tokens.compareAndSet(currentTokens, currentTokens - 1);
}
returnfalse;
}
publicvoidshutdown() {
scheduler.shutdown();
}
}
以上示例展示了如何在Java中实现滑动窗口算法和令牌桶算法,并简要介绍了它们的使用场景。
希望这些内容能够帮助你更好地理解和应用限流策略。
你诺喜欢,请点个关注哦
大家可以发送消息:202501
领取计算机黑皮书191本(1月有效)
由于个别人员打广告,现决定有偿进入IT资源群
扫码支付9.9元我只能说绝对超值
然后加个人VX,拉你进群(备注IT资源)
推荐文章:
推荐不错的学习笔记 Spring Cloud Alibaba笔记