【java】常见限流算法原理及应用
目录
前言
限流的作用
4种常见限流算法
固定窗口限流
基本原理
简单实现
优点和缺点
滑动窗口限流
基本原理
简单实现
优点和缺点
漏桶限流
基本原理
简单实现
优点和缺点
令牌桶限流
基本原理
简单实现
优点和缺点
算法比较与选择
前言
在现代分布式系统和高并发场景下,限流已成为保障系统稳定性和可用性的重要手段。随着互联网应用规模的不断扩大,系统经常会面对海量请求和突发流量,如何有效控制和管理这些请求流量成为一项关键挑战。限流算法作为流量控制的重要工具,能够帮助系统平衡资源分配、抵御恶意攻击,并在高峰期维持服务的连续性与可靠性。本文将深入探讨几种常见的限流算法及其应用场景,帮助读者更好地理解限流机制的工作原理与优化策略。
限流的作用
限流的作用在于防止系统过载、保障服务的可用性、优化资源利用、平滑高峰流量,并确保服务质量和用户体验。通过控制请求的频率,限流机制能够在高并发或突发流量的情况下保护系统资源,提升其整体性能和响应能力,从而避免系统崩溃或服务中断。
4种常见限流算法
固定窗口限流
基本原理
计数器算法是在一定的时间间隔里,记录请求次数,当请求次数超过间隔内的最大次数时,触发限流策略。当进入下一个时间窗口时进行访问次数的清零。例如,如果设置了1秒钟内最多允许100个请求的上限,那么系统会统计当前窗口内的请求数量,当请求数量未达到上限时,允许请求通过,一旦达到上限,则拒绝剩余的请求,直到进入下一个时间窗口为止,在新的时间窗口开始时,计数器会被重置,重新统计请求数量。如下图所示:
简单实现
import org.redisson.api.*;
import org.springframework.stereotype.Service;import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;@Service
public class RedisLimiterManager {@ResourceRedissonClient redissonClient;/*** 限流操作** @param key 限流键* @param limit 请求限制数量* @param windowSize 窗口大小*/public void doRateLimit(String key, Long limit, Long windowSize) {// 加分布式锁RLock rLock = redissonClient.getLock(key);try {// 加锁rLock.lock(100, TimeUnit.MILLISECONDS);// 获取原子计数器RAtomicLong counter = redissonClient.getAtomicLong(key);// 计数long count = counter.incrementAndGet();// 如果是第一个请求,初始化窗口并设置过期时间if (count == 1) {// 窗口时长设置为1秒counter.expire(windowSize, TimeUnit.SECONDS);}// 如果请求数量超过限制,触发限流if (count > limit) {// 触发限流的不记在请求数量中counter.decrementAndGet();// 执行限流逻辑}// 请求通过,继续处理业务逻辑} finally {rLock.unlock();}}
}
优点和缺点
优点:实现简单,易于理解。
缺点:存在明显的临界问题,比如: 假设限流阀值为5
个请求,单位时间窗口是1s
,如果我们在单位时间内的前0.8-1s
和1-1.2s
,分别并发5个请求。虽然都没有超过阀值,但是如果算0.8-1.2s,则并发数高达10,已经超过单位时间1s不超过5阀值的定义啦。
滑动窗口限流
基本原理
滑动窗口顾名思义,就是持续的滑动,它的窗口大小是固定的,但是起止时间是动态的,窗口大小被分割成大小相等的若干子窗口,每次滑动,都会滑过一个子窗口,同时每个子窗口单独计数,并且所有子窗口的计数求和不应大于整体窗口的阈值。这样的话,当新请求的时间点大于时间窗口右边界时,窗口右移一个子窗口,最左边的子窗口的计数值舍弃。 例如,如果设定的限流策略是“每秒钟最多允许100个请求”,那么系统会不断滑动地统计过去1秒内的请求次数。如果请求次数未达到上限,允许请求通过;否则拒绝请求。如下图所示:
简单实现
import org.redisson.api.*;
import org.springframework.stereotype.Service;import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;@Service
public class RedisLimiterManager {@ResourceRedissonClient redissonClient;/*** 限流操作** @param key 限流键* @param limit 请求限制数量* @param windowSize 窗口大小*/public void doRateLimit(String key, Long limit, Long windowSize) {// 获取计数的有序集合RScoredSortedSet<Long> counter = redissonClient.getScoredSortedSet(key);// 使用分布式锁RLock rLock = redissonClient.getLock(key);try {// 加锁rLock.lock(200, TimeUnit.MILLISECONDS);// 当前时间戳long currentTimestamp = System.currentTimeMillis();// 窗口起始时间戳long windowStartTimestamp = currentTimestamp - windowSize * 1000;// 移除窗口外的请求(即移除时间小于窗口起始时间的请求)counter.removeRangeByScore(0, true, windowStartTimestamp, false);// 将当前时间戳作为score和member存入有序集合中counter.add(currentTimestamp, currentTimestamp);// 获取当前窗口内的请求数量long count = counter.size();// 判断是否超过限流阈值if (count > limit) {// 执行限流逻辑}// 请求通过,继续处理业务逻辑} finally {rLock.unlock();}}
}
优点和缺点
优点:
- 简单易懂,精度较高,也解决了固定窗口的临界时间处理问题。
缺点:
- 突发流量无法处理,一旦到达限流后,请求都会直接暴力被拒绝,影响用户的体验。
漏桶限流
基本原理
漏桶算法可以形象地理解为一个固定容量的水桶,水以不规则的速度流入桶中,但以固定的速率从桶底漏出。假设水桶的容量是固定的,如果水流入的速度超过了漏出的速度,且水桶已满,多余的水(请求)将被丢弃。如下图所示:
简单实现
import org.redisson.api.*;
import org.springframework.stereotype.Service;import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;@Service
public class RedisLimiterManager {private static final String KEY_PREFIX = "leakyBucketRateLimiter:";@ResourceRedissonClient redissonClient;/*** 限流操作** @param key 限流键* @param leakRate 漏出速率* @param bucketSize 桶的容量*/public void doRateLimit(String key, Long leakRate, Long bucketSize) {// 获取当前请求的水位桶RBucket<Long> bucket = redissonClient.getBucket(KEY_PREFIX + key);// 获取最后一次漏出请求的时间RBucket<Long> lastLeakTimeBucket = redissonClient.getBucket(KEY_PREFIX + key + ":lastLeakTime");// 创建分布式锁RLock lock = redissonClient.getLock(KEY_PREFIX + "LOCK:" + key);try {// 尝试获取锁lock.lock(100, TimeUnit.MILLISECONDS);// 当前时间戳long currentTime = System.currentTimeMillis();// 当前水位Long currentWaterLevel = bucket.get();// 上次漏出时间Long lastLeakTime = lastLeakTimeBucket.get();// 初始化水位和漏出时间if (currentWaterLevel == null) {currentWaterLevel = 0L;}if (lastLeakTime == null) {lastLeakTime = currentTime;}// 计算自上次漏出以来经过的时间long timeElapsed = currentTime - lastLeakTime;// 计算漏出的请求数量long leakedAmount = timeElapsed / leakRate;// 更新水位if (leakedAmount > 0) {// 更新水位,确保不为负currentWaterLevel = Math.max(0, currentWaterLevel - leakedAmount);// 更新最后漏出时间lastLeakTimeBucket.set(currentTime);}// 检查是否可以接受新的请求if (currentWaterLevel < bucketSize) {// 接受请求,水位加一bucket.set(currentWaterLevel + 1);// 请求通过,继续处理业务逻辑} // 触发限流逻辑} finally {lock.unlock();}}
}
优点和缺点
优点:
- 既能够限流,还能够平滑控制处理速度。
缺点:
- 需要对请求进行缓存,会增加服务器的内存消耗。
- 无法应对突然激增的流量,因为只能以固定的速率处理请求,对系统资源利用不够友好。
- 桶流入水(发请求)的速率如果一直大于桶流出水(处理请求)的速率的话,那么桶会一直是满的,一部分新的请求会被丢弃,导致服务质量下降。
令牌桶限流
基本原理
令牌桶算法以恒定的速率生成令牌并放入桶中,令牌数不会超过桶容量,当有请求到来时,会尝试申请一块令牌,如果没有令牌则会拒绝请求,有足够的令牌则会处理请求,并且减少桶内令牌数。如下图所示:
简单实现
import org.redisson.api.*;
import org.springframework.stereotype.Service;import javax.annotation.Resource;@Service
public class RedisLimiterManager {@ResourceRedissonClient redissonClient;/*** 限流操作** @param key 限流键*/public void doRateLimit(String key) {RRateLimiter rRateLimiter = redissonClient.getRateLimiter(key);// 设置令牌桶限流器的限流效果:如限流的类型、每个限流时间窗口内生成的令牌数量、速率的时间间隔和时间间隔的单位。rRateLimiter.trySetRate(RateType.OVERALL, 2, 1, RateIntervalUnit.SECONDS);// 尝试获取 1 个令牌boolean canOp = rRateLimiter.tryAcquire(1);if (!canOp) {// 获取令牌失败// 执行失败后的操作....}// 请求通过,继续处理业务逻辑}
}
优点和缺点
优点:
- 面对突发流量,可以在短时间内提供更多的处理能力,以处理突发流量。
- 与漏桶算法相比,令牌桶算法提供了更大的灵活性,可以动态调整生成令牌的速率。
缺点:
- 如果令牌产生速率和桶的容量设置不合理,可能会出现问题比如大量的请求被丢弃、系统过载。
算法比较与选择
固定窗口算法:业务简单,对突发流量要求不高的场景。
滑动窗口算法:需要精确控制请求速率,平滑限流时使用。
漏桶算法:适合对流量有严格平稳要求的场景,尤其是在处理突发请求能力有限、系统必须稳定输出流量的情况下。
令牌桶算法:对突发流量有要求,对稳定性和精度要求较高的场景。
相关文章:

【java】常见限流算法原理及应用
目录 前言 限流的作用 4种常见限流算法 固定窗口限流 基本原理 简单实现 优点和缺点 滑动窗口限流 基本原理 简单实现 优点和缺点 漏桶限流 基本原理 简单实现 优点和缺点 令牌桶限流 基本原理 简单实现 优点和缺点 算法比较与选择 前言 在现代分布式系统…...

Git 原理(提交对象)(结合图与案例)
Git 原理(提交对象) 这一块主要讲述下 Git 的原理。 在进行提交操作时,Git 会保存一个提交对象(commit object): 该提交对象会包含一个指向暂存内容快照的指针; 该提交对象还包含了作者的姓…...

STM32如何修改外部晶振频率和主频
对于STM32F10x系列的单片机,除了STM32F10x_CL单片机,其它的单片机一般外部晶振HSE的时钟频率都默认是8MHz。如果我们使用的外部晶振为12Mhz,那么可以把上图绿色标记改为:12000000 72MHz的主频8MHz的外部晶振HSE*倍频系数9。当然如果像上面把外…...

【JAVA入门】Day48 - 线程池
【JAVA入门】Day48 - 线程池 文章目录 【JAVA入门】Day48 - 线程池一、线程池的主要核心原理二、自定义线程池三、线程池的大小 我们之前写的代码都是,用到线程的时候再创建,用完之后线程也就消失了,实际上这是不对的,它会浪费计算…...
图像亮度均衡算法
图像亮度均衡算法 图像亮度均衡算法的作用是提升图像的对比度和细节,使得图像的亮度分布更加均匀,从而改善视觉效果。通过调整亮度值,可以更好地揭示图像中的细节,尤其在低光或高光条件下的图像处理。 常见的图像亮度均衡算法包括…...

C++中的new与delete
目录 1.简介 2.底层 1.简介 new是升级版的malloc,它会先开空间再去调用构造函数。 delete是升级版的free,它会先调用析构函数再free掉空间。 class A { public:A(int a10, int b10){a a1;b b1;}private:int a;int b; };int main() {//new会先开空间…...
在HTML中添加视频
在HTML中添加视频,你可以使用<video>标签。这个标签允许你在网页上嵌入视频内容,并支持多种视频格式,如MP4、WebM和Ogg等。不过,由于浏览器对视频格式的支持程度不同,因此通常建议提供多种格式的视频文件&#x…...

YoloV10 训练自己的数据集(推理,转化,C#部署)
目录 一、下载 三、开始训练 train.py detect.py export.py 超参数都在这个路径下 四、C#读取yolov10模型进行部署推理 如下程序是用来配置openvino 配置好引用后就可以生成dll了 再创建一个控件,作为显示 net framework 4.8版本的 再nuget工具箱里下载 …...

Science Robotic 内在触觉实现直观的物理人机交互
触觉传感器和电子皮肤是为机器人提供物理交互感的常见设备,但当用于机器人的大面积覆盖时,它们会变得复杂且昂贵。德国宇航中心近期发表的Science Robotics研究工作,使用内部高分辨率关节力扭矩传感器,在机械臂中实现了固有的全身…...

string类(C++)
哈喽各位!,久违了,最近怎么样捏,本次进入C的string类,加油加油呀! 随记:鼓励创新,宽容失败! 1.标准库的string类 1.1string类的了解 string的文献参考链接-->strin…...

【C语言】自定义类型——结构体
目录 一、结构体的类型的声明 二、结构体变量的创建和初始化 三、匿名结构体类型 四、结构体自引用 五、结构体内存对齐 (1)对齐规则 (2)计算结构体大小练习 (3)需要内存对齐的原因 (4…...

MySQL练手题--日期连续类型(困难)
一、准备工作 Create table If Not Exists Failed (fail_date date); Create table If Not Exists Succeeded (success_date date); Truncate table Failed; insert into Failed (fail_date) values (2018-12-28); insert into Failed (fail_date) values (2018-12-29); inser…...

【AD24报错】运行DRC后出现 Un-Routed Net Constraint ### Net Not Assigned 的解决方案
AD24在运行PCB设计规则检查(DRC)后报错 Un-Routed Net Constraint ### Net Not Assigned 的解决方案 一、解决方案二、可能会报错Dead Copper的因素三、可能会报错Un-Routed Net Constraint的因素 Un-Routed Net Constraint ### Net Not Assigned 的解决…...

Linux嵌入式驱动开发指南(速记版)---Linux基础篇
第一章 Ubuntu系统入门 1.1 Linux磁盘管理 1.1.1 Linux磁盘管理基本概念 关键词: Linux 磁盘管理 挂载点 /etc/fstab文件 分区 ls /dev/sd* 联系描述: Linux 磁盘管理体系通过“挂载点”概念替代了 Windows 中的“分区”概念,将硬盘部分以文…...

PDF——压缩大小的方法
方法一:QQ浏览器->格式转换->PDF转纯图PDF...

无监督神经组合优化的扩散模型框架
文章目录 Abstract1. Introduction2. Problem Description2.1 无监督神经组合优化3. Neural Probabilistic Optimization Objective for Approximate Likelihood Models3.1 具有联合变分上界的训练扩散模型Abstract 从离散集合的不可处理分布中进行采样,而不依赖相应的训练数据…...

Web前端开发
首先打开,VS code新建文件夹,命名为index.HTML,然后先对内容进行输入,也就是在波蒂里面进行输入,将社会主义核心价值观的基本内容输入好,然后在页面呈现的效果是这样的 因为有一个alert警告框标签ÿ…...

transformer模型进行英译汉,汉译英
上面是在测试集上的表现 下面是在训练集上的表现 上面是在训练集上的评估效果 这是在测试集上的评估效果,模型是transformer模型,模型应该没问题,以上的是一个源序列没加结束符和加了结束符的情况。 transformer源序列做遮挡填充的自注意力,这就让编码器的输出中每个token的语…...
python 异步读取文件,速度变快了吗
“python 异步读取文件,速度变快了吗” 当我问出这个问题,大部分人第一反应应该是python新人,不懂异步 首先说一下我对异步的理解: asyncio 是 gevent greenlet 的组合gevent 底层使用了libev、selectors 模块,这两…...

【Python】Anaconda插件:Sublime Text中的Python开发利器
上班的时候没人问我苦不苦,下班的时候总有人问为什么走这么早。 Anaconda 是一个专为Sublime Text打造的开源Python开发插件,旨在为开发者提供类似于IDE的丰富功能,提升Python编码效率。该插件提供了代码补全、语法检查、代码片段提示等多项…...
使用 Docker Compose 部署 Jenkins(LTS 版)持续集成环境
一、前言 Jenkins 是目前最流行的开源持续集成工具之一。本教程将手把手带你使用 Docker Compose 快速部署 Jenkins LTS(长期支持版本),同时保留数据持久化、Docker 命令转发等功能,适合用于生产或本地开发测试环境。 二、环境准…...

CLIP多模态大模型的优势及其在边缘计算中的应用
CLIP多模态大模型的优势及其在边缘计算中的应用 CLIP(Contrastive Language-Image Pre-training)模型,是OpenAI开发的一种多模态大模型。该模型通过对比学习的方式,在大规模图像-文本对上进行预训练,成功实现了图像和文…...
Kafka 核心架构与消息模型深度解析(二)
案例实战:Kafka 在实际场景中的应用 (一)案例背景与需求介绍 假设我们正在为一个大型电商平台构建数据处理系统。该电商平台拥有庞大的用户群体,每天会产生海量的订单数据、用户行为数据(如浏览、点击、收藏等&#…...

在 Ubuntu 24.04 LTS 上安装 Jenkins 并配置全局工具(Git、JDK、Maven)
在 Ubuntu 24.04 LTS 上安装 Jenkins 并配置全局工具(Git、JDK、Maven) Jenkins 是一款开源的持续集成和持续交付(CI/CD)工具,在 DevOps 实践中有着广泛的应用。本文将手把手带你在 Ubuntu 24.04 LTS 系统中完成 Jenkins 的安装,并配置所需的全局工具:Git、JDK 和 Maven…...

26.【新型数据架构】-零ETL架构
26.【新型数据架构】-零ETL架构:减少数据移动,原系统直接分析;典型实现(AWS Zero-ETL) 一、零ETL的本质:从“数据搬运工”到“数据翻译官” 传统ETL(Extract-Transform-Load)需要将数据从源系统抽取、清洗、转换后加载到目标系统,这一过程往往耗时费力,且面临数据延…...

应用案例 | 设备分布广, 现场维护难? 宏集Cogent DataHub助力分布式锅炉远程运维, 让现场变“透明”
在日本,能源利用与环保问题再次成为社会关注的焦点。越来越多的工业用户开始寻求更高效、可持续的方式来运营设备、管理能源。而作为一家专注于节能与自动化系统集成的企业,日本大阪的TESS工程公司给出了一个值得借鉴的答案。 01 锅炉远程监控难题如何破…...

前端项目初始化
目录 1. 安装 nvm 2. 配置 nvm 并切换到 Node.js 16.15.0 3. 安装 LightProxy 代理 4. GIT安装 1. 配置用户名和邮箱(这些信息将用于您在提交代码时的标识): 2. 生成SSH密钥(用于将本地代码仓库与远程存储库连…...
沪铜6月想法
市场回顾 2025年5月,沪铜期货主力合约价格整体呈现震荡走势。从月初到月末,价格在7.67-7.82万元/吨之间波动。截至5月31日,沪铜主力合约收盘价为7.76万元/吨。本月铜价围绕供需基本面和宏观政策预期展开波动,尤其在5月14日至5月1…...
Spring Cloud 2025 正式发布啦
文章目录 一、版本兼容性二、Spring Cloud Gateway 重大更新1、新增功能1.1 Function & Stream 处理器集成1.2 Bucket4j 限流器支持 2、重要弃用2.1. WebClientRouting 基础设施2.2. 模块和启动器重命名 3、破坏性变更3.1 X-Forwarded-* 头部默认禁用3.2 配置受信任代理:3.…...

LabVIEW的AMC架构解析
此LabVIEW 程序基于消息队列(Message Queue)机制构建 AMC 架构,核心包含消息生成(MessageGenerator )与消息处理(Message Processor )两大循环,通过队列传递事件与指令,实…...