后端下载限速(redis记录实时并发,bucket4j动态限速)
- ✅ 使用 Redis 记录 所有用户的实时并发下载数
- ✅ 使用 Bucket4j 实现 全局下载速率限制(动态)
- ✅ 支持 动态调整限速策略
- ✅ 下载接口安全、稳定、可监控
🧩 整体架构概览
模块 | 功能 |
---|---|
Redis | 存储全局并发数和带宽令牌桶状态 |
Bucket4j + Redis | 分布式限速器(基于令牌桶算法) |
Spring Boot Web | 提供文件下载接口 |
AOP / Interceptor(可选) | 用于统一处理限流逻辑 |
📦 1. Maven 依赖(pom.xml
)
<dependencies><!-- Spring Boot --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!-- Redis 连接池 --><dependency><groupId>io.lettuce.core</groupId><artifactId>lettuce-core</artifactId></dependency><!-- Bucket4j 核心与 Redis 集成 --><dependency><groupId>com.github.vladimir-bukhtoyarov</groupId><artifactId>bucket4j-core</artifactId><version>5.3.0</version></dependency><dependency><groupId>com.github.vladimir-bukhtoyarov</groupId><artifactId>bucket4j-redis</artifactId><version>5.3.0</version></dependency></dependencies>
🛠️ 2. Redis 工具类:记录全局并发数
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;import java.util.Collections;
import java.util.concurrent.TimeUnit;@Component
public class GlobalDownloadCounter {private final StringRedisTemplate redisTemplate;private final DefaultRedisScript<Long> incrScript;private final DefaultRedisScript<Long> decrScript;public static final String KEY_CONCURRENT = "global:download:concurrent";private static final long TTL_SECONDS = 60; // 自动清理僵尸计数public GlobalDownloadCounter(StringRedisTemplate redisTemplate) {this.redisTemplate = redisTemplate;// Lua 脚本:原子增加并发数并设置过期时间String scriptIncr = """local key = KEYS[1]local ttl = tonumber(ARGV[1])local count = redis.call('GET', key)if not count thenredis.call('SET', key, 1)redis.call('EXPIRE', key, ttl)return 1elsecount = tonumber(count) + 1redis.call('SET', key, count)redis.call('EXPIRE', key, ttl)return countend""";incrScript = new DefaultRedisScript<>(scriptIncr, Long.class);// Lua 脚本:原子减少并发数String scriptDecr = """local key = KEYS[1]local count = redis.call('GET', key)if not count or tonumber(count) <= 0 thenreturn 0elsecount = tonumber(count) - 1redis.call('SET', key, count)return countend""";decrScript = new DefaultRedisScript<>(scriptDecr, Long.class);}public long increment() {return redisTemplate.execute(incrScript, Collections.singletonList(KEY_CONCURRENT), TTL_SECONDS).longValue();}public long decrement() {return redisTemplate.execute(decrScript, Collections.singletonList(KEY_CONCURRENT)).longValue();}public long getCurrentCount() {String value = redisTemplate.opsForValue().get(KEY_CONCURRENT);return value == null ? 0 : Long.parseLong(value);}
}
⚙️ 3. Bucket4j 配置:分布式限速器(带 Redis)
import io.github.bucket4j.Bandwidth;
import io.github.bucket4j.Refill;
import io.github.bucket4j.distributed.proxy.ProxyManager;
import io.github.bucket4j.distributed.proxy.RedisProxyManager;
import io.github.bucket4j.redis.lettuce.cas.LettuceReactiveProxyManager;
import io.lettuce.core.RedisClient;
import io.lettuce.core.api.StatefulRedisConnection;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import java.time.Duration;@Configuration
public class BandwidthLimiterConfig {@Beanpublic RedisClient redisClient() {return RedisClient.create("redis://localhost:6379");}@Beanpublic StatefulRedisConnection<String, String> redisConnection(RedisClient redisClient) {return redisClient.connect();}@Beanpublic ProxyManager<String> proxyManager(StatefulRedisConnection<String, String> connection) {return LettuceReactiveProxyManager.builder().build(connection.reactive());}@Beanpublic Bandwidth globalBandwidthLimit() {// 默认 10MB/sreturn Bandwidth.classic(10 * 1024 * 1024, Refill.greedy(10 * 1024 * 1024, Duration.ofSeconds(1)));}@Beanpublic Bucket globalBandwidthLimiter(ProxyManager<String> proxyManager, Bandwidth bandwidthLimit) {return proxyManager.builder().build("global:bandwidth:limiter", bandwidthLimit);}
}
📡 4. 下载接口实现
import io.github.bucket4j.Bucket;
import org.springframework.web.bind.annotation.*;import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;@RestController
@RequestMapping("/api/download")
public class DownloadController {private static final int MAX_CONCURRENT_DOWNLOADS = 100;private final GlobalDownloadCounter downloadCounter;private final Bucket bandwidthLimiter;public DownloadController(GlobalDownloadCounter downloadCounter, Bucket bandwidthLimiter) {this.downloadCounter = downloadCounter;this.bandwidthLimiter = bandwidthLimiter;}@GetMapping("/{fileId}")public void downloadFile(@PathVariable String fileId, HttpServletResponse response) throws IOException {long currentCount = downloadCounter.getCurrentCount();if (currentCount >= MAX_CONCURRENT_DOWNLOADS) {response.setStatus(HttpServletResponse.SC_TOO_MANY_REQUESTS);response.getWriter().write("Too many downloads. Please try again later.");return;}downloadCounter.increment();try {// 设置响应头response.setContentType("application/octet-stream");response.setHeader("Content-Disposition", "attachment; filename=" + fileId + ".bin");ServletOutputStream out = response.getOutputStream();// 文件路径(示例)String filePath = "/path/to/files/" + fileId + ".bin";if (!Files.exists(Paths.get(filePath))) {response.setStatus(HttpServletResponse.SC_NOT_FOUND);response.getWriter().write("File not found.");return;}byte[] buffer = new byte[8192]; // 每次读取 8KBRandomAccessFile file = new RandomAccessFile(filePath, "r");int bytesRead;while ((bytesRead = file.read(buffer)) != -1) {if (bytesRead > 0) {boolean consumed = bandwidthLimiter.tryConsume(bytesRead);if (!consumed) {Thread.sleep(100); // 等待令牌生成continue;}out.write(buffer, 0, bytesRead);out.flush();}}file.close();out.close();} catch (InterruptedException e) {Thread.currentThread().interrupt(); // 恢复中断状态} finally {downloadCounter.decrement();}}
}
🔁 5. 动态调整下载速率接口(可选)
@RestController
@RequestMapping("/api/limit")
public class RateLimitController {private final Bucket bandwidthLimiter;private final Bandwidth globalBandwidthLimit;public RateLimitController(Bucket bandwidthLimiter, Bandwidth globalBandwidthLimit) {this.bandwidthLimiter = bandwidthLimiter;this.globalBandwidthLimit = globalBandwidthLimit;}@PostMapping("/set-bandwidth")public String setBandwidth(@RequestParam int mbPerSecond) {Bandwidth newLimit = Bandwidth.classic(mbPerSecond * 1024 * 1024,Refill.greedy(mbPerSecond * 1024 * 1024, Duration.ofSeconds(1)));bandwidthLimiter.replaceConfiguration(newLimit);return "Global bandwidth limit updated to " + mbPerSecond + " MB/s";}
}
📊 6. 监控接口(可选)
@GetMapping("/monitor/concurrent")
public ResponseEntity<Long> getConcurrentDownloads() {return ResponseEntity.ok(downloadCounter.getCurrentCount());
}
🧪 7. 测试建议
你可以使用 curl
或 Postman 发起多并发请求测试:
for i in {1..200}; docurl -X GET "http://localhost:8080/api/download/file1" --output "file$i.bin" &
done
观察是否触发限流、并发控制是否生效。
✅ 总结
组件 | 作用 |
---|---|
Redis | 分布式存储并发数和限流令牌桶 |
Lua 脚本 | 原子操作并发计数器 |
Bucket4j + Redis | 全局下载速率限制 |
Spring Boot Controller | 处理下载逻辑 |
try-finally | 保证资源释放 |
动态接口 /set-bandwidth | 支持运行时修改限速 |
📌 扩展建议(可选)
- 将限流逻辑封装到 AOP 切面 中
- 添加 Prometheus 指标暴露并发数、限流次数等
- 使用 Nginx 或 Gateway 做额外的限流保护
- 加入用户身份识别,支持 用户级限速
- 使用 Kafka 异步记录日志或审计下载行为
相关文章:
后端下载限速(redis记录实时并发,bucket4j动态限速)
✅ 使用 Redis 记录 所有用户的实时并发下载数✅ 使用 Bucket4j 实现 全局下载速率限制(动态)✅ 支持 动态调整限速策略✅ 下载接口安全、稳定、可监控 🧩 整体架构概览 模块功能Redis存储全局并发数和带宽令牌桶状态Bucket4j Redis分布式限…...

vue3 手动封装城市三级联动
要做的功能 示意图是这样的,因为后端给的数据结构 不足以使用ant-design组件 的联动查询组件 所以只能自己分装 组件 当然 这个数据后端给的不一样的情况下 可能组件内对应的 逻辑方式就不一样 毕竟是 三个 数组 省份 城市 区域 我直接粘贴组件代码了 <temp…...
Angular中Webpack与ngx-build-plus 浅学
Webpack 在 Angular 中的概念 Webpack 是一个模块打包工具,用于将多个模块和资源打包成一个或多个文件。在 Angular 项目中,Webpack 负责将 TypeScript、HTML、CSS 等文件打包成浏览器可以理解的 JavaScript 文件。Angular CLI 默认使用 Webpack 进行项目…...
大模型智能体核心技术:CoT与ReAct深度解析
**导读:**在当今AI技术快速发展的背景下,大模型的推理能力和可解释性成为业界关注的焦点。本文深入解析了两项核心技术:CoT(思维链)和ReAct(推理与行动),这两种方法正在重新定义大模…...
信息系统分析与设计复习
2024试卷 单选题(20) 1、在一个聊天系统(类似ChatGPT)中,属于控制类的是()。 A. 话语者类 B.聊天文字输入界面类 C. 聊天主题辨别类 D. 聊天历史类 解析 B-C-E备选架构中分析类分为边界类、控制类和实体类。 边界…...

Linux【5】-----编译和烧写Linux系统镜像(RK3568)
参考:讯为 1、文件系统 不同的文件系统组成了:debian、ubuntu、buildroot、qt等系统 每个文件系统的uboot和kernel是一样的 2、源码目录介绍 目录 3、正式编译 编译脚本build.sh 帮助内容如下: Available options: uboot …...
记一次spark在docker本地启动报错
1,背景 在docker中部署spark服务和调用spark服务的微服务,微服务之间通过fegin调用 2,问题,docker容器中服务器来后,注册中心都有,调用服务也正常,但是调用spark启动任务后报错,报错…...
【向量库】Weaviate 搜索与索引技术:从基础概念到性能优化
文章目录 零、概述一、搜索技术分类1. 向量搜索:捕捉语义的智能检索2. 关键字搜索:精确匹配的传统方案3. 混合搜索:语义与精确的双重保障 二、向量检索技术分类1. HNSW索引:大规模数据的高效引擎2. Flat索引:小规模数据…...
ABB馈线保护 REJ601 BD446NN1XG
配电网基本量程数字继电器 REJ601是一种专用馈线保护继电器,用于保护一次和二次配电网络中的公用事业和工业电力系统。该继电器在一个单元中提供了保护和监控功能的优化组合,具有同类产品中最佳的性能和可用性。 REJ601是一种专用馈线保护继电器…...

Heygem50系显卡合成的视频声音杂音模糊解决方案
如果你在使用50系显卡有杂音的情况,可能还是官方适配问题,可以使用以下方案进行解决: 方案一:剪映替换音色(简单适合普通玩家) 使用剪映换音色即可,口型还是对上的,没有剪映vip的&…...

Gitlab + Jenkins 实现 CICD
CICD 是持续集成(Continuous Integration, CI)和持续交付/部署(Continuous Delivery/Deployment, CD)的缩写,是现代软件开发中的一种自动化流程实践。下面介绍 Web 项目如何在代码提交到 Gitlab 后,自动发布…...

无头浏览器技术:Python爬虫如何精准模拟搜索点击
1. 无头浏览器技术概述 1.1 什么是无头浏览器? 无头浏览器是一种没有图形用户界面(GUI)的浏览器,它通过程序控制浏览器内核(如Chromium、Firefox)执行页面加载、JavaScript渲染、表单提交等操作。由于不渲…...

SDU棋界精灵——硬件程序ESP32实现opus编码
一、 音频处理框架 该项目基于Espressif的音频处理框架构建,核心组件包括 ESP-ADF 和 ESP-SR,以下是完整的音频处理框架实现细节: 1.核心组件 (1) 音频前端处理 (AFE - Audio Front-End) main/components/audio_pipeline/afe_processor.c功能: 声学回声…...

Spring AI中使用ChatMemory实现会话记忆功能
文章目录 1、需求2、ChatMemory中消息的存储位置3、实现步骤1、引入依赖2、配置Spring AI3、配置chatmemory4、java层传递conversaionId 4、验证5、完整代码6、参考文档 1、需求 我们知道大型语言模型 (LLM) 是无状态的,这就意味着他们不会保…...

Qt 按钮类控件(Push Button 与 Radio Button)(1)
文章目录 Push Button前提概要API接口给按钮添加图标给按钮添加快捷键 Radio ButtonAPI接口性别选择 Push Button(鼠标点击不放连续移动快捷键) Radio Button Push Button 前提概要 1. 之前文章中所提到的各种跟QWidget有关的各种属性/函数/方法&#…...
生成对抗网络(GAN)损失函数解读
GAN损失函数的形式: 以下是对每个部分的解读: 1. , :这个部分表示生成器(Generator)G的目标是最小化损失函数。 :判别器(Discriminator)D的目标是最大化损失函数。 GAN的训…...

汇编语言学习(三)——DoxBox中debug的使用
目录 一、安装DoxBox,并下载汇编工具(MASM文件) 二、debug是什么 三、debug中的命令 一、安装DoxBox,并下载汇编工具(MASM文件) 链接: https://pan.baidu.com/s/1IbyJj-JIkl_oMOJmkKiaGQ?pw…...
【Java基础】向上转型(Upcasting)和向下转型(Downcasting)
在面向对象编程中,转型(Casting) 是指改变对象的引用类型,主要涉及 继承关系 和 多态。 向上转型(Upcasting) ⬆️ 定义 将 子类对象 赋值给 父类引用(自动完成,无需强制转换&…...
GitHub 常见高频问题与解决方案(实用手册)
1.Push 提示权限错误(Permission denied) 问题: Bash Permission denied (publickey) fatal: Could not read from remote repository. 原因: 没有配置 SSH key 或使用了 HTTPS 而没有权限…...

数据可视化交互
目录 【实验目的】 【实验原理】 【实验环境】 【实验步骤】 一、安装 pyecharts 二、下载数据 三、实验任务 实验 1:AQI 横向对比条形图 代码说明: 运行结果: 实验 2:AQI 等级分布饼图 实验 3:多城市 AQI…...

安宝特方案丨从依赖经验到数据驱动:AR套件重构特种装备装配与质检全流程
在高压电气装备、军工装备、石油测井仪器装备、计算存储服务器和机柜、核磁医疗装备、大型发动机组等特种装备生产型企业,其产品具有“小批量、多品种、人工装配、价值高”的特点。 生产管理中存在传统SOP文件内容缺失、SOP更新不及、装配严重依赖个人经验、产品装…...

【JavaEE】万字详解HTTP协议
HTTP是什么?-----互联网的“快递小哥” 想象我们正在网上购物:打开淘宝APP,搜索“蓝牙耳机”,点击商品图片,然后下单付款。这一系列操作背后,其实有一个看不见的“快递小哥”在帮我们传递信息,…...
Vue3学习(接口,泛型,自定义类型,v-for,props)
一,前言 继续学习 二,TS接口泛型自定义类型 1.接口 TypeScript 接口(Interface)是一种定义对象形状的强大工具,它可以描述对象必须包含的属性、方法和它们的类型。接口不会被编译成 JavaScript 代码,仅…...

华为云Flexus+DeepSeek征文 | MaaS平台避坑指南:DeepSeek商用服务开通与成本控制
作者简介 我是摘星,一名专注于云计算和AI技术的开发者。本次通过华为云MaaS平台体验DeepSeek系列模型,将实际使用经验分享给大家,希望能帮助开发者快速掌握华为云AI服务的核心能力。 目录 作者简介 前言 一、技术架构概览 1.1 整体架构设…...
WEB3全栈开发——面试专业技能点P8DevOps / 区块链部署
一、Hardhat / Foundry 进行合约部署 概念介绍 Hardhat 和 Foundry 都是以太坊智能合约开发的工具套件,支持合约的编译、测试和部署。 它们允许开发者在本地或测试网络快速开发智能合约,并部署到链上(测试网或主网)。 部署过程…...

【动态规划】B4336 [中山市赛 2023] 永别|普及+
B4336 [中山市赛 2023] 永别 题目描述 你做了一个梦,梦里有一个字符串,这个字符串无论正着读还是倒着读都是一样的,例如: a b c b a \tt abcba abcba 就符合这个条件。 但是你醒来时不记得梦中的字符串是什么,只记得…...

可下载旧版app屏蔽更新的app市场
软件介绍 手机用久了,app越来越臃肿,老手机卡顿成常态。这里给大家推荐个改善老手机使用体验的方法,还能帮我们卸载不需要的app。 手机现状 如今的app不断更新,看似在优化,实则内存占用越来越大,对手机性…...

claude3.7高阶玩法,生成系统架构图,国内直接使用
文章目录 零、前言一、操作指南操作指导 二、提示词模板三、实战图书管理系统通过4o模型生成系统描述通过claude3.7生成系统架构图svg代码转换成图片 在线考试系统通过4o模型生成系统描述通过claude3.7生成系统架构图svg代码转换成图片 四、感受 零、前言 现在很多AI大模型可以…...

河北对口计算机高考MySQL笔记(完结版)(2026高考)持续更新~~~~
MySQL 基础概念 数据(Data):文本,数字,图片,视频,音频等多种表现形式,能够被计算机存储和处理。 **数据库(Data Base—简称DB):**存储数据的仓库…...

2025-06-01-Hive 技术及应用介绍
Hive 技术及应用介绍 参考资料 Hive 技术原理Hive 架构及应用介绍Hive - 小海哥哥 de - 博客园https://cwiki.apache.org/confluence/display/Hive/Home(官方文档) Apache Hive 是基于 Hadoop 构建的数据仓库工具,它为海量结构化数据提供类 SQL 的查询能力…...