使用Sentinel作为Spring Boot应用限流组件
使用Sentinel作为Spring Boot应用限流组件过年放假期间公司 Web 服务的短信接口遭遇了恶意刷量导致阿里云账户余额直接被扣至欠费。当初实现该功能时由于心存侥幸觉得如此小规模的项目不至于被黑客盯上因此仅对同一手机号的重复请求做了简单限制并未在前端接入验证码流程也未在后端实施 IP 限流。过年回来后痛定思痛赶紧把这个技术债给还上……一、 前言对于一些核心且无需鉴权的对外接口做好限流措施是不可或缺的防线。虽然限流无法 100% 杜绝恶意攻击但至少能大幅提高恶意刷接口的成本。对于 Spring Boot/Cloud 框架开发的 Web 服务实际上有很多限流组件库可供选择例如Resilience4j (Spring Cloud 官方推荐)Bucket4j (基于令牌桶算法的 Java 限流库)Guava RateLimiter (单机限流)Hystrix (经典)Sentinel......综合考虑后我选择了Sentinel。它不仅具备上述优秀组件的特点还自带一个直观的 Dashboard 且极易上手。对于日常开发来说这种配置简单、开箱即用的工具无疑是最佳选择。二、 Spring Boot应用集成Sentinel相关链接:Sentinel的Github仓库: github.com/alibaba/Sen…Sentinel官方网站: home | Sentinel引入依赖这里使用Maven作为依赖管理工具, 在pom.xml中加入以下依赖xml!-- Sentinel -- dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-starter-alibaba-sentinel/artifactId version2023.0.3.4/version /dependency注意 如果按照官方网站的指导来做可能并非直接引入该依赖这对于刚接触的人来说比较困惑。这并不是官方文档描述有误或者过时了而是因为我们使用了 Spring Boot 作为框架Spring Cloud 开发组对其做了深度适配引入上述依赖实际上就会自动引入 Sentinel 的核心与常用依赖。配置Sentinel通过在Spring Boot的应用配置文件中进行最终配置yamlspring: cloud: # Sentinel配置 sentinel: transport: port: 8719 # 会在本地开启Http服务用于控制Sentinel dashboard: localhost:8080 # 如果不需要看板可以注释掉 eager: true # 是否提前触发 Sentinel 初始化, 建议开启, 随应用启动而初始化启动应用只需完成以上两步即可成功集成 Sentinel。它会自动将所有的 HTTP 接口注册为 Sentinel 的资源 (Resource)。启动应用以验证结果若控制台打印如下日志且无任何报错即说明集成成功vbnetINFO: Sentinel log output type is: file INFO: Sentinel log charset is: utf-8 INFO: Sentinel log base directory is: C:\Users\23111\logs\csp\ INFO: Sentinel log name use pid is: false INFO: Sentinel log level is: INFO使用Sentinel Dashboard (可选)下载Sentinel Dashboard的jar包, 从官方Github的Release中下载. 运行以下命令即可实现Dashboard的启动:ini# 如果8080端口被占用可以换成别端口, 相应地, 需要在Spring Boot应用的配置中更改过来 java -Dserver.port8080 -Dcsp.sentinel.dashboard.serverlocalhost:8080 -Dproject.namesentinel-dashboard -jar sentinel-dashboard.jar访问 http://localhost:设置的端口号 后即可进入 Sentinel 的 Web 界面默认账号密码是admin/admin。登录成功后进入首页如果左侧的菜单栏中除了当前 Dashboard 节点菜单外还有 Spring Boot 应用的名称那就说明对接成功。至于如何操作 Dashboard 此文章不做过多解释。三、使用Sentinel基于请求源IP对接口进行限流1. 选择限流的实现方案Sentinel 提供了多种限流规则。官方文档中提到若需基于 IP 限流可采用基于调用关系的流量控制。然而该方案主要适用于微服务之间已知且有限的 IP 限制。在面对公网环境下海量不可控的源 IP 时这种方式往往无法满足需求参考官方 FAQ 说明Q: 怎么针对特定调用端限流比如我想针对某个 IP 或者来源应用进行限流规则里面 limitApp流控应用的作用A: Sentinel 支持按来源限流可以参考 基于调用关系的限流。注意 origin 数量不能太多否则会导致内存暴涨并且目前不支持模式匹配。因此我们需要另辟蹊径。最终我决定采用热点参数限流。Sentinel 的热点参数限流底层基于 LRU 机制实现对于拦截公网高频恶意 IP 访问的场景非常契合且不会误伤正常用户的访问。2. 实现思路Sentinel 原生的热点参数限流要求开发者手动传入参数值作为计数 Key框架本身并不会自动提取 HTTP 请求的源 IP 并注入规则中。因此我们需要自行串联起“从请求中获取源 IP”、“将 IP 设为限流参数”、“设定限流规则”以及“触发限流逻辑”的完整流程。如果在每个需要限流的业务接口中硬编码提取 IP 和限流逻辑会对业务代码造成严重的侵入。为了保持代码的整洁与高内聚我采用了自定义注解 AOP (面向切面编程)的方式来实现既保证了高度的灵活性又实现了与业务逻辑的解耦。3. 代码参考注解定义javaimport java.lang.annotation.*; Target(ElementType.METHOD) Retention(RetentionPolicy.RUNTIME) Documented public interface RateLimitByIp { /** * 资源名称如果不填默认使用 类名.方法名 */ String value() default ; /** * 【新增】单机 QPS 阈值默认 10 */ int count() default 10; /** * 【新增】统计窗口时长秒默认 1 秒 */ int duration() default 1; /** * 限流后的提示信息 */ String message() default Too busy; }限流逻辑的AOP实现java import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule; import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager; import com.xxx.xxx.annotation.RateLimitByIp; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.annotation.Aspect; import org.springframework.stereotype.Component; import com.alibaba.csp.sentinel.Entry; import com.alibaba.csp.sentinel.EntryType; import com.alibaba.csp.sentinel.SphU; import com.alibaba.csp.sentinel.slots.block.BlockException; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import jakarta.servlet.http.HttpServletRequest; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; Slf4j Aspect Component public class IpRateLimitAspect { // 用来记录已经初始化过规则的资源避免重复加载 private static final MapString, Boolean ruleInitMap new ConcurrentHashMap(); Around(annotation(rateLimitByIp)) public Object handleRateLimit(ProceedingJoinPoint point, RateLimitByIp rateLimitByIp) throws Throwable { // 1. 获取资源名称 (如果注解没写就用 类名.方法名) String resourceName rateLimitByIp.value(); if (resourceName null || resourceName.isEmpty()) { MethodSignature signature (MethodSignature) point.getSignature(); Method method signature.getMethod(); resourceName method.getDeclaringClass().getName() . method.getName(); } // 2. 【关键优化】自动初始化规则 // 只有第一次访问该接口时才会执行规则加载逻辑 if (!ruleInitMap.containsKey(resourceName)) { initHotParamFlowRule(resourceName, rateLimitByIp.count(), rateLimitByIp.duration()); } // 3. 获取 IP ServletRequestAttributes attributes (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); if (attributes null) { return point.proceed(); } HttpServletRequest request attributes.getRequest(); String ip getIpAddress(request); // 4. Sentinel 埋点 Entry entry null; try { // 参数索引 0 是 IP entry SphU.entry(resourceName, EntryType.IN, 1, ip); return point.proceed(); } catch (BlockException ex) { log.error(IP访问限流: ip{}, 访问次数上限{}, 资源{}, ip, rateLimitByIp.count(), resourceName); throw new RuntimeException(rateLimitByIp.message()); } finally { if (entry ! null) { entry.exit(1, ip); } } } /** * 动态加载热点参数规则 */ private synchronized void initHotParamFlowRule(String resourceName, int count, int duration) { // 防止并发重复初始化 if (ruleInitMap.containsKey(resourceName)) return; // 1. 创建新规则 ParamFlowRule rule new ParamFlowRule(resourceName) .setParamIdx(0) // 我们的 Aspect 总是把 IP 放在第一个参数 .setCount(count) .setDurationInSec(duration); // 2. 获取当前已有的所有规则 ListParamFlowRule rules new ArrayList(ParamFlowRuleManager.getRules()); // 3. 移除旧规则 (如果存在同名的)避免重复添加 rules.removeIf(r - r.getResource().equals(resourceName)); // 4. 添加新规则 rules.add(rule); // 5. 重新加载 ParamFlowRuleManager.loadRules(rules); ruleInitMap.put(resourceName, true); log.info( [Sentinel] 自动加载 IP 限流规则: 资源{}, QPS{}, resourceName, count); } private String getIpAddress(HttpServletRequest request) { String ip request.getHeader(x-forwarded-for); if (ip null || ip.isEmpty() || unknown.equalsIgnoreCase(ip)) { ip request.getHeader(Proxy-Client-IP); } if (ip null || ip.isEmpty() || unknown.equalsIgnoreCase(ip)) { ip request.getHeader(WL-Proxy-Client-IP); } if (ip null || ip.isEmpty() || unknown.equalsIgnoreCase(ip)) { ip request.getRemoteAddr(); } // 多个代理的情况第一个IP为客户端真实IP if (ip ! null ip.contains(,)) { ip ip.split(,)[0].trim(); } return ip; } } 使用注解lessGetMapping(/sms/code) ResponseBody RateLimitByIp(count 3, duration 60) // 60s内最多允许3次请求 public AjaxResult sendPhoneSms(String phone) { ...... }四、结尾寄语本文分享的方案在实际应用中仍有优化空间例如通过 HTTP Header 获取源 IP 的算法在经过多层复杂反向代理时可能不够准确缺少全局的流量分析面板基于 AOP 的限流由于切面执行时机无法完全复用 Spring MVC 适配的默认接口资源名等。这也是为了快速修补安全漏洞而采取的应急方案不足之处还望海涵。另外发几句牢骚对于起步阶段的小型项目过度设计限流机制有时显得性价比不高。在项目生死未卜的阶段将大量精力投入到“防御性编程”中倒不如早点下班享受生活……然而总有一些“神人”闲来无事专挑小公司的小项目进行所谓的“技术演练”。只能说有这身手何不去打击那些真正的灰黑产诈骗网站呢。后续计划 Sentinel 默认的 Dashboard 指标数据是保存在内存中的重启就会丢失且不适合用于长期的流量分析。后续我计划直接修改 Sentinel Dashboard 的源码将这些监控指标数据持久化到时序数据库如 InfluxDB 或 TDengine中以此来搭建一个完善的流量监控面板届时再整理成文章分享出来。

相关新闻

大数据情感分析:如何利用情感数据优化供应链管理?

大数据情感分析:如何利用情感数据优化供应链管理?

用情感数据“读懂”供应链:大数据情感分析如何优化从需求到交付的每一步? 引言:你的供应链,读懂用户情绪了吗? 上周我在某生鲜APP买了一盒车厘子,等了三天才收到——打开箱子的瞬间,我差点气炸…

2026/5/17 7:14:17 阅读更多 →
一周连过华为HCIE、Cisco CCNP、TOGAF,这份备考时间表请收好

一周连过华为HCIE、Cisco CCNP、TOGAF,这份备考时间表请收好

最近整理学员考试数据,发现一个很有意思的现象: 2月6号到11号,短短6天,5个不同方向的高含金量认证全部通过—— 华为HCIE-Cloud Computing V3.0(笔试700分) Cisco 350-401 CCNP(Pass&#xff09…

2026/6/16 22:48:23 阅读更多 →
万字详解 Vue 项目从源码到上线:前端部署全流程指南

万字详解 Vue 项目从源码到上线:前端部署全流程指南

作为前端开发者,我们日常开发的 Vue 项目最终都要走向生产环境。但从本地写好的源码到线上可访问的应用,中间的部署环节常常让新手踩坑 —— 打包报错、路径不对、服务器配置异常、性能优化不到位…… 本文将从 0 到 1 详解 Vue 项目的完整部署流程&…

2026/7/3 15:55:37 阅读更多 →

最新新闻

气候适配科技面料推荐程序,根据地域温湿度匹配透气保暖功能性服饰。

气候适配科技面料推荐程序,根据地域温湿度匹配透气保暖功能性服饰。

气候适配科技面料推荐程序 —— 地域温湿度 功能性服饰匹配一、实际应用场景描述在《时尚产业与品牌创新》课程中,功能性面料(Functional Fabrics) 是科技驱动品牌创新的核心赛道。全球气候变暖导致极端天气频发:- 2024 年夏季&a…

2026/7/4 0:22:37 阅读更多 →
明日方舟桌宠Ark-Pets:5分钟打造你的智能桌面伙伴

明日方舟桌宠Ark-Pets:5分钟打造你的智能桌面伙伴

明日方舟桌宠Ark-Pets:5分钟打造你的智能桌面伙伴 【免费下载链接】Ark-Pets Arknights Desktop Pets | 明日方舟桌宠 (ArkPets) 项目地址: https://gitcode.com/gh_mirrors/ar/Ark-Pets 还在寻找能让电脑桌面焕然一新的创意工具吗?Ark-Pets作为一…

2026/7/4 0:22:37 阅读更多 →
STM32L432KC与MC74HC165A实现低功耗多路信号采集

STM32L432KC与MC74HC165A实现低功耗多路信号采集

1. 项目背景与核心价值在嵌入式系统开发中,我们经常需要处理大量输入信号,特别是在工业控制、智能家居和自动化设备等场景。传统方案需要为每个输入信号分配独立的GPIO引脚,这不仅占用宝贵的微控制器资源,还会增加电路复杂度和成本…

2026/7/4 0:22:37 阅读更多 →
MDUT数据库工具终极指南:从入门到精通的全栈开发实战

MDUT数据库工具终极指南:从入门到精通的全栈开发实战

MDUT数据库工具终极指南:从入门到精通的全栈开发实战 【免费下载链接】MDUT MDUT - Multiple Database Utilization Tools 项目地址: https://gitcode.com/gh_mirrors/md/MDUT 想要在数据库安全测试领域快速上手一款功能强大的跨平台工具吗?MDUT&…

2026/7/4 0:22:37 阅读更多 →
C语言实现量子密钥分发(BB84)协议:从原理到代码实战

C语言实现量子密钥分发(BB84)协议:从原理到代码实战

1. 项目概述:当C语言遇见量子加密如果你是一名嵌入式开发者,或者对密码学和底层编程有浓厚兴趣,那么“量子加密”这个词对你来说,可能既充满科幻感又觉得遥不可及。我们常在新闻里看到量子计算机如何“秒杀”传统加密,…

2026/7/4 0:20:36 阅读更多 →
电子邮件端到端加密实战指南:从PGP原理到安全通信部署

电子邮件端到端加密实战指南:从PGP原理到安全通信部署

1. 项目概述:为什么我们需要为电子邮件“上锁”?在数字世界里,电子邮件就像我们日常寄送的明信片。想象一下,你写了一张包含银行账户信息或私人情感的明信片,从投入邮筒到送达朋友手中,会经过分拣中心、邮递…

2026/7/4 0:20:36 阅读更多 →

日新闻

Memcached 1.6.43 发布:关键安全修复版本,多项问题得到解决

Memcached 1.6.43 发布:关键安全修复版本,多项问题得到解决

Memcached 1.6.43 正式发布,这是一个关键的安全修复版本,修复了多个方面的问题,还对部分功能进行了优化。 安全修复亮点 此次发布在安全修复上表现突出。binprot 避免了项目引用计数溢出,mcmc 因安全问题提升了上游版本号&#xf…

2026/7/4 0:04:29 阅读更多 →
终极指南:使用HMCL启动器跨平台畅玩Minecraft的完整解决方案

终极指南:使用HMCL启动器跨平台畅玩Minecraft的完整解决方案

终极指南:使用HMCL启动器跨平台畅玩Minecraft的完整解决方案 【免费下载链接】HMCL A Minecraft Launcher which is multi-functional, cross-platform and popular 项目地址: https://gitcode.com/gh_mirrors/hm/HMCL HMCL(Hello Minecraft! Lau…

2026/7/4 0:06:29 阅读更多 →
KMX63与PIC18F66K40在嵌入式HMI中的硬件协同与低功耗设计

KMX63与PIC18F66K40在嵌入式HMI中的硬件协同与低功耗设计

1. KMX63与PIC18F66K40的硬件协同架构解析KMX63作为一款三轴加速度计和磁力计组合传感器,与PIC18F66K40微控制器的搭配堪称嵌入式HMI开发的黄金组合。这套硬件组合的核心优势在于KMX63提供的高精度运动感知能力与PIC18F66K40强大的信号处理能力形成了完美互补。KMX6…

2026/7/4 0:06:29 阅读更多 →

周新闻

月新闻