首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

高并发场景,常用的Java限流接口实现

Java 限流接口是一种用于控制系统中流量的工具,它可以限制系统在一个设定的时间窗口内处理的请求数量,以保护系统免受过度的请求压力。

为什么要限流?

在一个高并发的系统中,当大量请求同时到达系统时,如果系统无法合理地处理这些请求,可能会导致系统资源被耗尽,甚至崩溃。限流的目的就是为了控制并发请求的数量,防止系统因为过度的请求压力而崩溃。

限流可以带来以下好处:

保护系统资源:限流可以保护有限的系统资源不被过度占用,确保系统能够正常提供服务。

提供公平性:限流可以让系统能够公平地处理请求,防止某些请求占用过多的资源导致其他请求被拒绝或延迟。

防止恶意攻击:通过限制每个用户的请求频率,可以有效防止恶意攻击或恶意刷接口。

如何实现?

常见的 Java 限流实现方式有以下几种:

计数器算法:基于固定窗口的请求计数,当请求数量超过阈值时进行限流。可以使用 AtomicLong 来实现计数器,结合定时任务每秒重置窗口内的计数器。

漏桶算法:漏桶算法通过一个固定容量的漏桶来控制请求的流量。每个请求按照固定的速率往桶中滴水,请求无法被处理时会丢弃。可以使用队列和定时任务来实现漏桶算法。

令牌桶算法:令牌桶算法与漏桶算法类似,但是桶中的令牌容量是固定的。每个请求需要从令牌桶中获取一个令牌才能被处理,当令牌桶为空时,请求被拒绝。可以使用队列和定时任务来实现令牌桶算法。

实现原理:

1.计数器算法

public class RateLimiter {

?private AtomicLong counter = new AtomicLong(0);

?private int limit;

?private int interval;

?public RateLimiter(int limit, int interval) {

? ?this.limit = limit;

? ?this.interval = interval;

? ?// 定时任务,每秒重置计数器

? ?ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();

? ?scheduler.scheduleAtFixedRate(this::resetCounter, 0, interval, TimeUnit.SECONDS);

?}

?public boolean allowRequest() {

? ?long currentCount = counter.incrementAndGet();

? ?if (currentCount > limit) {

? ? ?return false;

? ?}

? ?return true;

?}

?private void resetCounter() {

? ?counter.set(0);

?}}

在上述代码中,我们使用了 AtomicLong 来实现计数器,通过调用allowRequest()方法来判断当前请求是否允许被处理。每秒钟定时任务会重置计数器,这样就限制了每秒钟处理的请求数量。

计数器算法实现简单粗暴,单机在Java中可用Atomic等原子类或Semaphore,分布式就用Redis incr。这个算法虽然简单,但是有一个十分致命的问题,那就是临界问题。

假设有一个恶意用户,他在0:59时,瞬间发送了100个请求,并且1:00又瞬间发送了100个请求,那么其实这个用户在 1秒里面,瞬间发送了200个请求。

我们刚才规定的是1分钟最多100个请求(规划的吞吐量),也就是每秒钟最多1.7个请求,用户通过在时间窗口的重置节点处突发请求, 可以瞬间超过我们的速率限制。用户有可能通过算法的这个漏洞,瞬间压垮我们的应用。

2.漏桶算法

漏桶算法是另一种经典的限流算法之一,它的实现原理是通过一个固定容量的漏桶来控制请求的流量。漏桶的容量就像一根固定大小的水管,请求被视为流入水管的水,水根据一定速率(容量大小)流出水管。如果水管满了,水就会溢出,这些超出限制的请求将会被丢弃或拒绝处理。

漏桶的主要实现思路是在固定的时间内,让一个漏桶按照确认容量流出,若请求流入漏桶时漏桶已满,则拒绝这个请求。

public class LeakyBucketLimiter {

? ?private final int maxBucketSize;//滴漏速率,即每秒钟最多能处理的请求个数

? ?private final double leakRate;//桶内漏水速度,漏水速度会影响桶内剩余的水量

? ?private volatile int currentBucketSize;//当前桶中的水量

? ?private volatile long lastLeakTime;//上一次漏水的时间

? ?public LeakyBucketLimiter(int maxBucketSize, double leakRate) {

? ? ? ?this.maxBucketSize = maxBucketSize;

? ? ? ?this.leakRate = leakRate;

? ? ? ?this.currentBucketSize = 0;

? ? ? ?this.lastLeakTime = System.currentTimeMillis();

? ?}

? ?/**

? ? * 尝试获取请求处理的权限,返回true表示允许,返回false表示拒绝

? ? */

? ?public synchronized boolean tryAcquire(int requestCount) {

? ? ? ?long now = System.currentTimeMillis();

? ? ? ?double leakedWater = ((now - lastLeakTime) / 1000.0) * leakRate;

? ? ? ?if (currentBucketSize + leakedWater > maxBucketSize) {

? ? ? ? ? ?//桶已满,拒绝处理请求

? ? ? ? ? ?return false;

? ? ? ?}

? ? ? ?currentBucketSize += leakedWater;

? ? ? ?currentBucketSize = Math.min(currentBucketSize + requestCount, maxBucketSize);

? ? ? ?lastLeakTime = now;

? ? ? ?return true;

? ?}}

漏桶算法的优点是可以平滑处理请求流量,并且可以设置一个固定的处理速度,即漏桶的容量大小,保证每秒钟最多能处理的请求个数。缺点是在短时间内会拒绝大量的请求,因此可能会影响一些特定的业务场景的实现。

3.令牌桶算法

令牌桶算法是另一种常见的限流算法,类似于漏桶算法,但它与漏桶算法实现上有所不同。令牌桶算法将固定容量的桶划分为宽度固定的小块(令牌),请求需要使用令牌才能被处理。每秒钟会往桶中投放若干个令牌,每个令牌就代表一个请求,如果桶中的令牌被取完了,那么后面的请求就会直接被拒绝或被阻塞,直到有足够的令牌才能被处理。

public class TokenBucketLimiter {

? ?private int maxTokenSize;//桶的大小,表示最多存储的令牌个数

? ?private double rate;//令牌的投放速度,表示每毫秒允许添加的令牌数

? ?private volatile int currentTokenCount;//当前令牌数

? ?private volatile long lastTokenTime;//上次填充令牌时间

? ?public TokenBucketLimiter(int maxTokenSize, double rate) {

? ? ? ?this.maxTokenSize = maxTokenSize;

? ? ? ?this.rate = rate;

? ? ? ?this.currentTokenCount = maxTokenSize;

? ? ? ?this.lastTokenTime = System.currentTimeMillis();

? ?}

? ?/**

? ? * 尝试获取请求处理的权限,返回true表示允许,返回false表示拒绝

? ? */

? ?public synchronized boolean tryAcquire() {

? ? ? ?long now = System.currentTimeMillis();

? ? ? ?double newTokens = (now - lastTokenTime) * rate;

? ? ? ?if (newTokens > 0) {

? ? ? ? ? ?currentTokenCount = Math.min(currentTokenCount + (int) newTokens, maxTokenSize);

? ? ? ? ? ?lastTokenTime = now;

? ? ? ?}

? ? ? ?if (currentTokenCount > 0) {

? ? ? ? ? ?currentTokenCount--;

? ? ? ? ? ?return true;

? ? ? ?}

? ? ? ?return false;

? ?}}

令牌桶算法的优点是可以在一定程度上适应突发流量的处理,能够快速响应。而且它允许在延迟较大的情况下,一定程度上支持突发流量处理。令牌桶算法还能够平滑地处理请求,限制了请求的处理速度,提供了更好的流量控制。缺点是对短时间突发流量的处理相对较差,可能会出现短期内无法处理大量请求的情况。漏桶算法和令牌桶算法都是有效的限流算法,适用于不同的业务场景。

计数器算法简单易用,但是有临界问题。漏桶算法可以平滑处理请求流量,但可能会在短时间内拒绝大量的请求。令牌桶算法可以在一定程度上适应突发流量的处理,但可能在短时间内无法处理大量请求。根据具体业务需求和场景特点,选择合适的算法来实现限流策略。

限流是一种重要的技术手段,可以保护系统免受过度的请求压力。在实际的应用开发中,根据具体的业务场景和需求选择适合的限流算法来实现,并根据系统的实际情况设置合理的限流参数。通过合理的限流策略,可以确保系统的稳定性和可靠性。

>>>更多开发技术资源、资讯请关注访问【昂焱数据】

  • 发表于:
  • 原文链接https://page.om.qq.com/page/OMaAx63niFmV0ca9H-fUIbuw0
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券
http://www.vxiaotou.com