赞
踩
目录
当今互联网时代,随着网络流量的快速增长和系统负载的不断加重,限流算法作为一种重要的网络管理工具变得愈发重要。限流算法通过控制系统的输入和输出流量,有效地保护系统不受过载的影响,确保系统能够稳定可靠地运行。本文将介绍几种常见的限流算法及其应用场景,旨在帮助读者更好地理解限流算法的原理和实际应用,从而为网络性能优化提供有力支持。限流算法的研究和应用对于保障网络安全、提升系统稳定性具有重要意义,在当前信息化社会具有广泛的应用前景。
固定窗口限流 就是在单位时间(时间窗口)内,只能接收指定数量的请求。
用汉堡店举例:固定窗口限流就是 在固定的时间内只能接待指定数量的顾客。比如一个小时只能接待10个顾客。
固定窗口限流的思路比较简单,代码实现为:
- import java.util.concurrent.TimeUnit;
- import java.util.concurrent.atomic.AtomicInteger;
-
- public class FixedWindowRateLimiter {
- private final int limit; // 限制的请求数
- private final long windowSizeInMillis; // 窗口大小(毫秒)
- private final AtomicInteger counter;
- private long windowStartTime;
-
- public FixedWindowRateLimiter(int limit, long windowSizeInMillis) {
- this.limit = limit;
- this.windowSizeInMillis = windowSizeInMillis;
- this.counter = new AtomicInteger(0);
- this.windowStartTime = System.currentTimeMillis();
- }
-
- public boolean allowRequest() {
- long currentTime = System.currentTimeMillis();
- long elapsedTime = currentTime - windowStartTime;
-
- if (elapsedTime > windowSizeInMillis) {
- // 进入新的窗口,重置计数器和窗口开始时间
- counter.set(0);
- windowStartTime = currentTime;
- }
-
- // 检查请求数是否超过限制
- if (counter.incrementAndGet() > limit) {
- return false; // 超过限制,拒绝请求
- }
-
- return true; // 没有超过限制,允许请求通过
- }
- }
固定窗口限流可能会引发流量突刺,也就是可能会发生以下情况:
我们用红色来标识 在该时间窗口区域发生了请求,也就是说固定窗口限流在窗口的边界处可能会发生流量突刺,在短时间内发生多次请求
滑动窗口限流 就是在单位时间(时间窗口)内,只能接收指定数量的请求。但单位时间是滑动的。
我们用图片来标识滑动窗口限流和固定窗口限流的区别:
这是固定窗口限流,他的时间窗口是由时间窗口大小决定的。
这是滑动窗口限流,他的时间窗口是不断滑动的。
也就是说:滑动窗口限流不会一次性消除旧窗口的请求次数,而是不断的通过滑动的方式抹除。
用汉堡店举例:滑动窗口限流就是 单位时间内限制接客数,相比较于固定窗口而言,假设我们在5.59接待了五位客人,如果时间窗口长度为小时,滑动单位为1分钟,那么六点的时候,并不会刷新窗口接待客人人数,而是继续保留5.59的接待人数。因为此时二者仍位于一个窗口内。
滑动窗口限流的代码思路为:
- import java.util.concurrent.TimeUnit;
- import java.util.concurrent.atomic.AtomicInteger;
- import java.util.concurrent.locks.Lock;
- import java.util.concurrent.locks.ReentrantLock;
-
- public class SlidingWindowRateLimiter {
- private final int limit; // 限制的请求数
- private final long windowSizeInMillis; // 窗口大小(毫秒)
- private final int[] counter;
- private final Lock lock;
- private long windowStartTime;
-
- public SlidingWindowRateLimiter(int limit, long windowSizeInMillis) {
- this.limit = limit;
- this.windowSizeInMillis = windowSizeInMillis;
- this.counter = new int[(int) (windowSizeInMillis / 1000)];
- this.lock = new ReentrantLock();
- this.windowStartTime = System.currentTimeMillis();
- }
-
- public boolean allowRequest() {
- long currentTime = System.currentTimeMillis();
- long elapsedTime = currentTime - windowStartTime;
-
- lock.lock();
- try {
- // 滑动窗口,将旧的窗口移除
- if (elapsedTime > windowSizeInMillis) {
- int numToRemove = (int) ((elapsedTime - windowSizeInMillis) / 1000);
- for (int i = 0; i < numToRemove; i++) {
- counter[i] = 0;
- }
- windowStartTime = currentTime - (elapsedTime % windowSizeInMillis);
- }
-
- // 统计请求数
- int currentWindowIndex = (int) (elapsedTime / 1000);
- counter[currentWindowIndex]++;
-
- // 检查请求数是否超过限制
- int totalRequests = 0;
- for (int i = 0; i < counter.length; i++) {
- totalRequests += counter[i];
- }
- if (totalRequests > limit) {
- return false; // 超过限制,拒绝请求
- }
- } finally {
- lock.unlock();
- }
-
- return true; // 没有超过限制,允许请求通过
- }
- }
滑动窗口可以缓解流量突刺:例如固定窗口(窗口大小为1小时,每个窗口最多处理10个请求)可以在1.59进行了十次请求,2.01进行了十次请求。但是在滑动窗口中,如果我们将滑动参数设置为1min,窗口大小设置为1小时,那么1.59时,滑动窗口的范围是1.59-2.59。此时如果设置最大请求数量为10,那么1.59执行的十次请求就已经填满了请求数量上限。2.01的就无法进行请求。通过这种思路避免了窗口边界流量突刺这种情况。
滴桶限流 就是 接收指定数量的请求,按照指定的速率处理。
用汉堡店举例:滴桶限流就是每个小时接待6个客户,然后每十分钟处理一个客户的请求
java代码实现:
- import java.util.concurrent.TimeUnit;
-
- public class LeakyBucketRateLimiter {
- private final int capacity; // 桶的容量
- private final double rate; // 水滴漏出速率(水滴/秒)
- private double water; // 当前桶中的水滴数量
- private long lastLeakTime; // 上次漏水的时间戳
-
- public LeakyBucketRateLimiter(int capacity, double rate) {
- this.capacity = capacity;
- this.rate = rate;
- this.water = 0;
- this.lastLeakTime = System.nanoTime();
- }
-
- public synchronized boolean allowRequest() {
- leak();
-
- if (water >= 1) {
- water -= 1;
- return true; // 水滴足够,允许请求通过
- } else {
- return false; // 水滴不足,拒绝请求
- }
- }
-
- private void leak() {
- long now = System.nanoTime();
- double elapsedTime = (now - lastLeakTime) / 1e9;
- double waterToLeak = elapsedTime * rate;
-
- if (waterToLeak > 0) {
- water = Math.max(0, water - waterToLeak);
- lastLeakTime = now;
- }
- }
- }
滴桶限流的并发性比较差,我们在代码中就可以看到,他需要对水滴(请求)逐个进行处理
令牌桶限流就是创建一个桶,生成指定数量的令牌,只有拿到令牌的请求才可以进行处理。
- import java.util.concurrent.TimeUnit;
-
- public class TokenBucketRateLimiter {
- private final int capacity; // 令牌桶容量
- private final double rate; // 令牌生成速率(令牌/秒)
- private double tokens; // 当前桶中的令牌数
- private long lastRefillTime; // 上次填充令牌的时间戳
-
- public TokenBucketRateLimiter(int capacity, double rate) {
- this.capacity = capacity;
- this.rate = rate;
- this.tokens = capacity;
- this.lastRefillTime = System.nanoTime();
- }
-
- public synchronized boolean allowRequest() {
- refill();
-
- if (tokens >= 1) {
- tokens -= 1;
- return true; // 令牌足够,允许请求通过
- } else {
- return false; // 令牌不足,拒绝请求
- }
- }
-
- private void refill() {
- long now = System.nanoTime();
- double elapsedTime = (now - lastRefillTime) / 1e9;
- double tokensToAdd = elapsedTime * rate;
-
- if (tokensToAdd > 0) {
- tokens = Math.min(capacity, tokens + tokensToAdd);
- lastRefillTime = now;
- }
- }
- }
用汉堡店举例:令牌桶限流就是 汉堡店 有一个 餐卷桶,并且会按时补充餐卷桶的餐卷数,只有拿到了餐卷才可以购买汉堡
令牌桶限流的并发性能比较高,我们可以批量对拿到令牌的请求进行处理。
在实际的开发中,其实我们并不用手动去实现这些限流算法,很多第三方库都已经为我们实现了限流算法,我们只需要直接使用就好了。而在实际开发中,常用的限流算法是 滴桶限流和令牌桶限流。因此我们要掌握好这两个限流算法。
如果我的内容对你有帮助,请点赞,评论,收藏。创作不易,大家的支持就是我坚持下去的动力!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。