Java限流详解

一、限流概述

随着互联网化程度的不断提高,大量用户同时使用某些功能,可能会导致某些功能被耗尽,从而导致系统瘫痪。为了应对这种情况,限流成为了解决问题的重要手段。限流是指根据给定的规则和策略来控制、限制应用程序流量,以达到保护后端应用的目的。Java作为一门流行的编程语言,提供了多种限流机制,可以解决大流量并发任务下服务器资源被耗光的问题。

二、限流分类

限流根据限流目的和实现方式的不同,可以分为五种类型:

1. 固定窗口算法

固定窗口算法限制时间窗口内的请求数量,同一时间窗口内的请求量不能超过设定的阈值。

public class FixedWindowRateLimiter implements RateLimiter {
    private AtomicInteger counter = new AtomicInteger(0);
    private final int limit;
    private final int intervalMs;
    private final ThreadLocal last = new ThreadLocal() {
        @Override
        protected Long initialValue() {
            return System.currentTimeMillis();
        }
    };
    public FixedWindowRateLimiter(int limit, int intervalMs) {
        this.limit = limit;
        this.intervalMs = intervalMs;
    }
    @Override
    public boolean tryAcquire() {
        long now = System.currentTimeMillis();
        long lastTime = last.get();
        if (now - lastTime > intervalMs) {
            counter.set(0);
            last.set(now);
        }
        return counter.incrementAndGet() <= limit;
    }
}

2. 滑动窗口算法

滑动窗口算法仍然限制时间窗口内的请求数量,不同的是时间窗口可以滑动,即滑动窗口算法最近的一段时间窗口都被重复使用。

public class SlidingWindowRateLimiter implements RateLimiter {
    private AtomicInteger counter = new AtomicInteger(0);
    private final int limit;
    private final int intervalMs;
    private final int bucketSize;
    private final ConcurrentLinkedDeque windows = new ConcurrentLinkedDeque();
    public SlidingWindowRateLimiter(int limit, int intervalMs, int bucketSize) {
        this.limit = limit;
        this.intervalMs = intervalMs;
        this.bucketSize = bucketSize;
    }
    @Override
    public boolean tryAcquire() {
        long now = System.currentTimeMillis();
        if (!windows.isEmpty() && now - windows.peek() > intervalMs) {
            int size = windows.size();
            for (int i = 0; i < size && i < bucketSize; i++) {
                windows.poll();
            }
            counter.set(windows.size());
        }
        if (counter.incrementAndGet() <= limit) {
            windows.offer(System.currentTimeMillis());
            return true;
        }
        counter.decrementAndGet();
        return false;
    }
}

3. Leaky Bucket算法

Leaky Bucket算法是一种限制请求流量的算法,它通过给定最大速率控制进入网络的数据数量,有效地防止了请求峰的出现,从而保护了系统。

public class LeakyBucketRateLimiter implements RateLimiter {
    private AtomicInteger counter = new AtomicInteger(0);
    private final int limit;
    private final int intervalMs;
    private final float leakRate;
    private AtomicLong leftWater = new AtomicLong(0);
    private AtomicLong lastUpdateTime = new AtomicLong(System.nanoTime());
    public LeakyBucketRateLimiter(int limit, int intervalMs, float leakRate) {
        this.limit = limit;
        this.intervalMs = intervalMs;
        this.leakRate = leakRate;
    }
    @Override
    public boolean tryAcquire() {
        long now = System.nanoTime();
        long timeDelta = now - lastUpdateTime.get();
        long left = leftWater.addAndGet((long) (-timeDelta * leakRate));
        if (left < 0) {
            left = 0;
            leftWater.set(left);
        }
        if (left < limit) {
            if (counter.addAndGet(1) <= limit) {
                return true;
            } else {
                counter.decrementAndGet();
            }
        }
        lastUpdateTime.set(now);
        return false;
    }
}

4. Token Bucket算法

Token Bucket算法是一种计量允许突发请求流量的算法,它通过对请求进行统计并抑制突发请求流量,保护系统正常运行。

public class TokenBucketRateLimiter implements RateLimiter {
    private AtomicInteger tokens = new AtomicInteger(0);
    private final int limit;
    private final int intervalMs;
    private final int refillTimeMs;
    private final int refillCount;
    private final ThreadLocal last = new ThreadLocal() {
        @Override
        protected Long initialValue() {
            return System.currentTimeMillis();
        }
    };
    public TokenBucketRateLimiter(int limit, int intervalMs, int refillTimeSec, int refillCount) {
        this.limit = limit;
        this.intervalMs = intervalMs;
        this.refillTimeMs = refillTimeSec * 1000;
        this.refillCount = refillCount;
    }
    @Override
    public boolean tryAcquire() {
        long now = System.currentTimeMillis();
        int currentCount = tokens.get();
        if (now - last.get() > refillTimeMs) {
            int newTokens = (int) ((now - last.get()) / refillTimeMs * refillCount);
            if (newTokens > 0) {
                tokens.set(Math.min(currentCount + newTokens, limit));
                last.set(now);
            }
        }
        return tokens.compareAndSet(currentCount, currentCount - 1);
    }
}

5. 基于Google Guava的流量控制

Google Guava提供了一种基于令牌桶算法的流控方式,只需定制化配置令牌生成速率和令牌桶大小,即可轻松实现流控。

private static final RateLimiter RATE_LIMITER = RateLimiter.create(1.0);
public void rateLimiter() {
    if (RATE_LIMITER.tryAcquire()) {
        // Allow access
    } else {
        // Reject request
    }
}

三、应用场景

限流机制可以用于很多场景,例如:高并发请求、海量爬虫请求、大量数据库查询操作等。以下是限流机制的一些具体应用场景:

1. 秒杀系统抢购场景

秒杀场景下用户同时发起大量请求,服务器负担过重,为此可以通过限流措施对在一定时间内访问的请求进行限制,比如高并发时采用Leaky Bucket算法进行限流,控制请求流量。

2. 海量爬虫请求场景

爬虫请求注意要保证请求合理、抢占时间均匀,限流是抵抗爬虫攻击、保护服务器资源的重要手段,可以采用固定窗口、滑动窗口等算法进行限流。

3. 流媒体播放区分不同视频质量场景

流媒体播放场景下用户可以通过质量切换功能,切换不同的码率、分辨率、FPS等参数,可以通过Token Bucket算法或基于Guava的令牌桶算法实现限流。

四、总结

本文从限流基本概念入手,详细介绍了Java下的五种常见限流算法及其应用场景。针对不同的应用场景,可以选择不同的限流算法实现或者结合使用多个限流算法进行流量控制,以达到保护系统的目的。

原创文章,作者:PDYJO,如若转载,请注明出处:https://www.506064.com/n/325064.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
PDYJOPDYJO
上一篇 2025-01-13 13:23
下一篇 2025-01-13 13:23

相关推荐

  • java client.getacsresponse 编译报错解决方法

    java client.getacsresponse 编译报错是Java编程过程中常见的错误,常见的原因是代码的语法错误、类库依赖问题和编译环境的配置问题。下面将从多个方面进行分析…

    编程 2025-04-29
  • Java JsonPath 效率优化指南

    本篇文章将深入探讨Java JsonPath的效率问题,并提供一些优化方案。 一、JsonPath 简介 JsonPath是一个可用于从JSON数据中获取信息的库。它提供了一种DS…

    编程 2025-04-29
  • Java腾讯云音视频对接

    本文旨在从多个方面详细阐述Java腾讯云音视频对接,提供完整的代码示例。 一、腾讯云音视频介绍 腾讯云音视频服务(Cloud Tencent Real-Time Communica…

    编程 2025-04-29
  • Java Bean加载过程

    Java Bean加载过程涉及到类加载器、反射机制和Java虚拟机的执行过程。在本文中,将从这三个方面详细阐述Java Bean加载的过程。 一、类加载器 类加载器是Java虚拟机…

    编程 2025-04-29
  • Java Milvus SearchParam withoutFields用法介绍

    本文将详细介绍Java Milvus SearchParam withoutFields的相关知识和用法。 一、什么是Java Milvus SearchParam without…

    编程 2025-04-29
  • Java 8中某一周的周一

    Java 8是Java语言中的一个版本,于2014年3月18日发布。本文将从多个方面对Java 8中某一周的周一进行详细的阐述。 一、数组处理 Java 8新特性之一是Stream…

    编程 2025-04-29
  • Java判断字符串是否存在多个

    本文将从以下几个方面详细阐述如何使用Java判断一个字符串中是否存在多个指定字符: 一、字符串遍历 字符串是Java编程中非常重要的一种数据类型。要判断字符串中是否存在多个指定字符…

    编程 2025-04-29
  • VSCode为什么无法运行Java

    解答:VSCode无法运行Java是因为默认情况下,VSCode并没有集成Java运行环境,需要手动添加Java运行环境或安装相关插件才能实现Java代码的编写、调试和运行。 一、…

    编程 2025-04-29
  • Java任务下发回滚系统的设计与实现

    本文将介绍一个Java任务下发回滚系统的设计与实现。该系统可以用于执行复杂的任务,包括可回滚的任务,及时恢复任务失败前的状态。系统使用Java语言进行开发,可以支持多种类型的任务。…

    编程 2025-04-29
  • Java 8 Group By 会影响排序吗?

    是的,Java 8中的Group By会对排序产生影响。本文将从多个方面探讨Group By对排序的影响。 一、Group By的概述 Group By是SQL中的一种常见操作,它…

    编程 2025-04-29

发表回复

登录后才能评论