SpringBoot 实现网络限速

📅 发布时间:2026/7/6 6:19:13 👁️ 浏览次数:
SpringBoot 实现网络限速
这是一个或许对你有用的社群 一对一交流/面试小册/简历优化/求职解惑欢迎加入「芋道快速开发平台」知识星球。下面是星球提供的部分资料《项目实战视频》从书中学往事上“练”《互联网高频面试题》面朝简历学习春暖花开《架构 x 系统设计》摧枯拉朽掌控面试高频场景题《精进 Java 学习指南》系统学习互联网主流技术栈《必读 Java 源码专栏》知其然知其所以然这是一个或许对你有用的开源项目国产Star破10w的开源项目前端包括管理后台、微信小程序后端支持单体、微服务架构RBAC权限、数据权限、SaaS多租户、商城、支付、工作流、大屏报表、ERP、CRM、AI大模型、IoT物联网等功能多模块https://gitee.com/zhijiantianya/ruoyi-vue-pro微服务https://gitee.com/zhijiantianya/yudao-cloud视频教程https://doc.iocoder.cn【国内首批】支持 JDK17/21SpringBoot3、JDK8/11Spring Boot2双版本来源风象南概述为什么需要带宽限速场景一文件下载服务场景二视频流媒体场景三API 接口保护核心原理令牌桶算法核心参数解析算法流程技术设计整体流程为什么选择 HandlerInterceptor核心组件职责多维度限速实现全局限速GLOBALAPI 维度限速API用户维度限速USERIP 维度限速IP关键代码实现1. 令牌桶核心算法2. 响应包装器3. 拦截器获取包装响应4. Controller 获取限速响应参数调优指南桶容量选择分块大小选择总结项目代码概述本文介绍在 Spring Boot 3 中实现多维度网络带宽限速的完整方案。基于令牌桶算法手动实现核心逻辑通过自定义HandlerInterceptor拦截请求、HttpServletResponseWrapper包装响应流、RateLimitedOutputStream控制输出速率实现对文件下载、视频流等场景的精确速度控制。基于 Spring Boot MyBatis Plus Vue Element 实现的后台管理系统 用户小程序支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能项目地址https://github.com/YunaiV/ruoyi-vue-pro视频教程https://doc.iocoder.cn/video/为什么需要带宽限速带宽限速与常见的 API 限流不同限流控制的是请求次数如每分钟100次而限速控制的是网络带宽如每秒200KB。在实际应用中带宽限速有着重要的业务价值场景一文件下载服务对于网盘或资源分发平台免费用户限制在 200KB/sVIP 用户提升到 2MB/s既能保障基础体验又能激励付费转化。场景二视频流媒体不同清晰度对应不同带宽限制480P 用 500KB/s1080P 用 3MB/s避免高码率视频占用过多服务器带宽。场景三API 接口保护大数据量接口如导出报表如果没有带宽控制单个请求可能占满整个出口带宽影响其他用户访问。基于 Spring Cloud Alibaba Gateway Nacos RocketMQ Vue Element 实现的后台管理系统 用户小程序支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能项目地址https://github.com/YunaiV/yudao-cloud视频教程https://doc.iocoder.cn/video/核心原理令牌桶算法令牌桶算法是流量控制的经典方案其思想非常直观想象一个桶系统以固定速率向桶中放入令牌请求数据时必须从桶中取走对应数量的令牌。核心参数解析桶容量Capacity决定能承受多大突发流量。容量为 200KB 时即使桶已满最多也只能连续发送 200KB 数据之后必须等待令牌补充。填充速率Refill Rate决定长期平均传输速度。每秒补充 200KB 令牌意味着平均速度就是 200KB/s。分块大小Chunk Size影响流量平滑度。将 8KB 数据拆分成 2KB×4 次写入每次写入之间进行令牌检查比一次性写入 8KB 更加平滑。算法流程发送数据前计算距离上次补充的时间差根据 时间差 × 填充速率 计算新增令牌数更新桶中令牌数不超过容量上限发送数据时检查令牌是否足够足够直接扣除令牌发送数据不足计算 (缺少令牌数 / 填充速率) 得到等待时间精确等待后发送技术设计整体流程本方案采用拦截器模式在请求处理的早期阶段完成限速组件的初始化通过请求属性传递包装后的响应对象。请求流程 ┌─────────────────────────────────────────────────────────────────────┐ │ 1. DispatcherServlet 分发请求 │ └─────────────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────────────┐ │ 2. BandwidthLimitInterceptor.preHandle() │ │ - 解析 BandwidthLimit 注解 │ │ - 从 BandwidthLimitManager 获取共享 TokenBucket │ │ - 创建 BandwidthLimitResponseWrapper 并存入 request attribute │ └─────────────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────────────┐ │ 3. Controller 处理请求 │ │ - 通过 BandwidthLimitHelper.getLimitedResponse() 获取包装后的响应 │ │ - 向响应流写入数据自动触发限速 │ └─────────────────────────────────────────────────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────────────────────┐ │ 4. BandwidthLimitInterceptor.afterCompletion() │ │ - 清理资源关闭流 │ └─────────────────────────────────────────────────────────────────────┘为什么选择 HandlerInterceptor在 Spring Boot 中实现请求处理有两种常见方式Filter和HandlerInterceptor。本方案选择HandlerInterceptor的关键原因是注解解析需要HandlerMethod对象。Filter 在DispatcherServlet之前执行此时还没有确定具体的处理方法无法获取方法上的BandwidthLimit注解。而HandlerInterceptor在处理器确定后执行可以通过HandlerMethod精确获取方法级别和类级别的注解信息。核心组件职责组件职责BandwidthLimit声明式注解配置限速参数BandwidthLimitInterceptor拦截请求解析注解创建响应包装器BandwidthLimitManager管理多维度限速桶全局/API/用户/IPBandwidthLimitResponseWrapper包装 HttpServletResponse替换 OutputStreamRateLimitedOutputStream实现限速逻辑包装 TokenBucketTokenBucket令牌桶算法实现BandwidthLimitHelper从请求属性中获取包装后的响应对象多维度限速实现本方案支持四种限速维度满足不同业务场景需求全局限速GLOBAL所有请求共享同一个限速桶适合保护服务器整体出口带宽。例如设置 10MB/s 全局限制即使有100个并发下载总带宽也不会超过 10MB/s。BandwidthLimit(value 200, unit BandwidthUnit.KB, type LimitType.GLOBAL) GetMapping(/download/global) public void downloadGlobal(HttpServletResponse response) throws IOException { HttpServletResponse limitedResponse BandwidthLimitHelper.getLimitedResponse(request, response); // 写入数据... }API 维度限速API每个接口路径独立限速不同接口的流量互不影响。/api/file/download限制 500KB/s/api/video/stream限制 2MB/s两个接口可以同时达到各自的速度上限。BandwidthLimit(value 500, unit BandwidthUnit.KB, type LimitType.API) GetMapping(/download/file) public void downloadFile(HttpServletResponse response) throws IOException { // 文件下载逻辑 } BandwidthLimit(value 2048, unit BandwidthUnit.KB, type LimitType.API) GetMapping(/stream/video) public void streamVideo(HttpServletResponse response) throws IOException { // 视频流逻辑 }用户维度限速USER根据用户标识如请求头X-User-Id进行限速每个用户独立计算带宽。配合 free 和 vip 参数可实现差异化服务BandwidthLimit(value 200, unit BandwidthUnit.KB, type LimitType.USER, free 200, vip 2048) GetMapping(/download/user) public void downloadByUser(RequestHeader(X-User-Type) String userType, HttpServletResponse response) throws IOException { // 根据请求头 X-User-Type 自动应用 200KB/s 或 2MB/s 限速 }IP 维度限速IP根据客户端 IP 地址限速防止单个 IP 占用过多带宽。支持代理环境下的 IP 获取X-Forwarded-For、X-Real-IP。BandwidthLimit(value 300, unit BandwidthUnit.KB, type LimitType.IP) GetMapping(/download/ip) public void downloadByIp(HttpServletResponse response) throws IOException { // 每个独立 IP 限制 300KB/s }关键代码实现1. 令牌桶核心算法TokenBucket 的核心在于精确的时间计算和令牌补充。使用System.nanoTime()获取纳秒级时间戳确保高精度速率控制。public synchronized void acquire(long permits) { // 1. 补充令牌 refill(); // 2. 计算等待时间 if (tokens permits) { tokens - permits; return; } long deficit permits - tokens; long waitNanos (deficit * 1_000_000_000L) / refillRate; // 3. 精确等待 sleepNanos(waitNanos); // 4. 等待后消费 tokens 0; } private void refill() { long now System.nanoTime(); long elapsedNanos now - lastRefillTime; long newTokens (elapsedNanos * refillRate) / 1_000_000_000L; tokens Math.min(capacity, tokens newTokens); lastRefillTime now; }2. 响应包装器HttpServletResponseWrapper是 Servlet 规范提供的响应包装基类通过覆盖getOutputStream()方法返回自定义的限速输出流。public class BandwidthLimitResponseWrapper extends HttpServletResponseWrapper { privatefinal TokenBucket sharedTokenBucket; // 共享的令牌桶 Override public ServletOutputStream getOutputStream() throws IOException { if (limitedOutputStream null sharedTokenBucket ! null) { // 使用共享 TokenBucket确保多维度统计正确 limitedOutputStream new RateLimitedOutputStream( super.getOutputStream(), sharedTokenBucket, bandwidthBytesPerSecond ); } return limitedOutputStream; } }3. 拦截器获取包装响应拦截器在 preHandle 中创建响应包装器存储到request attributeController 通过BandwidthLimitHelper获取。Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { BandwidthLimit annotation findAnnotation(handler); if (annotation ! null) { // 从 Manager 获取共享 TokenBucket TokenBucket bucket limitManager.getBucket(type, key, capacity, rate); // 创建包装器并存储 BandwidthLimitResponseWrapper wrappedResponse new BandwidthLimitResponseWrapper(response, bucket, bandwidthBytesPerSecond, chunkSize); request.setAttribute(BandwidthLimitWrappedResponse, wrappedResponse); } return true; }4. Controller 获取限速响应Controller 通过BandwidthLimitHelper.getLimitedResponse()获取包装后的响应所有写入操作都会自动限速。GetMapping(/download/global) public void downloadGlobal(HttpServletRequest request, HttpServletResponse response) throws IOException { HttpServletResponse limitedResponse BandwidthLimitHelper.getLimitedResponse(request, response); limitedResponse.setContentType(application/octet-stream); limitedResponse.setHeader(Content-Disposition, attachment; filenametest.bin); // 写入数据时自动限速 limitedResponse.getOutputStream().write(data); }参数调优指南桶容量选择容量决定突发流量承受能力容量设置突发能力适用场景速率 × 0.5平滑无突发流量控制严格的场景速率 × 1.0允许 1 秒突发默认推荐值速率 × 2.0允许 2 秒突发需要良好首屏加载// 注解配置 BandwidthLimit(value 200, unit BandwidthUnit.KB, capacityMultiplier 1.0)分块大小选择分块大小影响流量平滑度经验公式chunkSize bandwidth / 50带宽推荐分块理由200 KB/s1-4 KB小分块保证平滑1 MB/s4-8 KB平衡平滑与性能5 MB/s8-16 KB减少系统调用开销// 自动计算推荐 BandwidthLimit(value 200, unit BandwidthUnit.KB, chunkSize -1) // 手动指定 BandwidthLimit(value 200, unit BandwidthUnit.KB, chunkSize 4096)总结本文基于令牌桶算法通过HandlerInterceptor HttpServletResponseWrapper在 Spring Boot 中实现了多维度带宽限速。支持全局/API/用户/IP 四种限速维度提供实时统计监控适用于API接口保护、文件下载、视频流等场景。项目代码https://github.com/yuboon/java-examples/tree/master/springboot-netspeed-limit欢迎加入我的知识星球全面提升技术能力。 加入方式“长按”或“扫描”下方二维码噢星球的内容包括项目实战、面试招聘、源码解析、学习路线。文章有帮助的话在看转发吧。 谢谢支持哟 (*^__^*