首页 / 资讯中心 / 文章详情

秒杀接口被刷崩?Sentinel限流熔断三步扛住高并发

秒杀接口被刷崩?Sentinel限流熔断三步扛住高并发 ★ FEATURED ARTICLE
双十一零点秒杀接口的 QPS 从几百瞬间飙到几万。你信心满满地按下发布30 秒后数据库连接池被打满整个服务开始雪崩——先是库存服务超时接着订单服务连锁倒下最后连不参与秒杀的普通下单都进不来了。问题不在你的业务代码写得烂而在你没有任何「流量保护」没有限流没有熔断。本文用 Sentinel 一步步给秒杀接口装上这两道闸门。示例基于 Spring Boot 4.0.8、Spring Cloud 2025.1.3、Spring Cloud Alibaba 2025.1.0.0版本号均来自 Maven Central 实查的最新 GA和 Java 21。一、限流熔断是什么为什么秒杀接口会雪崩先说结论秒杀接口被打崩绝大多数不是代码 bug而是「没有流量保护」导致一个接口的故障像多米诺骨牌一样推倒整个系统。理解这个问题之前先把三个容易混在一起的词掰清楚。它们经常被笼统地说成「限流熔断」其实是三件事术语大白话解释类比限流Flow Control超过阈值的请求直接拒绝保护服务不被冲垮景区门口限流每天只放 3 万人满了就劝返熔断Circuit Breaking下游调用连续出错时暂时「断开」调用快速失败家里保险丝电流过大就跳闸避免烧坏电器降级Degradation熔断或限流触发后给用户一个兜底返回备用发电机主电路断了赶紧顶上应急为什么一个秒杀接口能拖垮整个系统因为现在的服务都不是孤立的而是一串调用链。用户点「立即抢购」请求要经过网关 → 秒杀服务 → 库存服务 → 订单服务 → 数据库。假设秒杀服务每秒能扛 1000 个请求结果大促来了每秒 10000 个会发生什么秒杀服务处理不过来每个请求都变慢它调下游库存服务的线程全卡在等待上线程池被占满库存服务也被拖慢数据库连接池耗尽故障沿着调用链一路传导最后普通下单也进不来了。这个过程就是「雪崩」。挡住雪崩有两道闸门限流负责在入口就把多余流量挡掉把 10000 压到 1000熔断负责在下游已经出问题时快速失败、不再傻等别让一个慢下游拖垮整条链。Sentinel 就是专门干这两件事的组件。它是阿里巴巴开源、面向分布式系统的流量防护框架在 Spring Cloud Alibaba 体系里是一等公民。它的核心优势在于不只有代码还有一个可视化控制台能实时看流量、动态改规则改完不用重启服务。二、Sentinel 限流熔断的底层原理滑动窗口和熔断状态机先说结论Sentinel 限流靠「滑动窗口计数」熔断靠「状态机 慢调用/异常比例统计」而这一切都通过一条「责任链」挂在每一个资源上。2.1 一切围绕「资源」展开Sentinel 里最核心的概念叫「资源」Resource。一个资源可以是任意一段代码、一个方法、一个 URL 接口。你给资源配规则Sentinel 在资源被调用时执行这些规则。用SentinelResource(seckill)注解就等于把「seckill」这个名字标记成了一个资源。2.2 限流滑动窗口计数限流最朴素的想法是「固定窗口」把时间切成一段一段比如 1 秒每段内计数超过阈值就拒绝。但固定窗口有个著名的坑——临界突刺假设阈值是每秒 2 个在 0.9 秒和 1.1 秒之间两个窗口各放 2 个实际 0.2 秒内涌进了 4 个请求翻倍了。Sentinel 用「滑动窗口」解决这个问题。想象一条传送带上面铺满一个个小格子每个格子代表一个时间片比如 200 毫秒。传送带持续往前走旧的格子滑出去、新的格子滑进来任何时刻你只统计当前窗口内所有格子的请求总数。因为格子是连续滑动的就不存在「整点重置」这个缝隙突刺问题被抹平了。默认情况下 Sentinel 的限流是「快速失败」请求超过阈值直接抛出BlockException走你指定的blockHandler兜底方法。除了 QPS 限流它还支持「并发线程数」限流限制同时处理请求的线程数量以及热点参数限流比如同一个商品 ID 的请求单独限流。2.3 熔断状态机 三种统计策略熔断器本质上是一个三状态的状态机跟家里保险丝跳闸的逻辑一模一样状态含义行为CLOSED闭合一切正常请求正常放行同时统计指标OPEN打开熔断中请求直接拒绝快速失败不再调下游HALF_OPEN半开试探期放行一个探测请求成功就回到 CLOSED失败就回到 OPEN什么时候从 CLOSED 跳到 OPENSentinel 支持三种判定策略1.8 之后的版本慢调用比例SLOW_REQUEST_RATIO统计时长内请求数超过最小请求数且「响应时间超过阈值」的慢调用比例超过设定值触发熔断。适合「下游变慢」这种信号。异常比例ERROR_RATIO统计时长内异常请求占总请求的比例超过阈值触发熔断。异常数ERROR_COUNT一分钟内异常请求的绝对数量超过阈值触发熔断。熔断打开后会持续一个「熔断时长」比如 10 秒这 10 秒内所有请求直接被拒。时长一到进入 HALF_OPEN放一个请求去试探下游如果这个请求成功说明下游恢复了熔断器闭合如果还失败继续打开再等 10 秒。2.4 责任链规则怎么被串起来Sentinel 内部把限流、熔断、降级、系统保护这些能力都做成一个个「槽」Slot串成一条责任链。一个资源被调用时请求会依次经过这些槽每个槽干一件事请求进来 → NodeSelectorSlot选节点→ ClusterBuilderSlot建统计 → StatisticSlot记指标→ AuthoritySlot黑白名单 → SystemSlot系统保护→ FlowSlot限流→ DegradeSlot熔断降级 → 真正的业务方法FlowSlot和DegradeSlot就是我们关心的两道闸门。任何一个槽判定「不通过」就抛BlockException请求根本到不了你的业务方法。这套设计的好处是规则和业务代码彻底解耦你写业务时完全不用关心限流逻辑在哪。三、实战手把手用 Sentinel 给秒杀接口装闸门先说结论接入 Sentinel 只需三步——加依赖、写代码、配规则其中「配规则」可以在控制台里点一行配置文件都不用改。下面按「完整可运行」的标准贴出所有文件。整篇文章只靠这些代码就能在本地跑起来不需要你自己脑补任何东西。3.1 第一步pom.xml 加依赖新建一个 Maven 项目pom.xml完整内容如下。注意 Spring Cloud Alibaba 的版本号要和 Spring Cloud 对上这里用 Spring Cloud 2025.1.3 Spring Cloud Alibaba 2025.1.0.0 Spring Boot 4.0.8三者是兼容的Spring Cloud 2025.1.x 这一代要求 Spring Boot 4.0.x。?xml version1.0 encodingUTF-8?projectxmlnshttp://maven.apache.org/POM/4.0.0xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsdmodelVersion4.0.0/modelVersionparentgroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-parent/artifactIdversion4.0.8/versionrelativePath//parentgroupIdcom.example/groupIdartifactIdsentinel-demo/artifactIdversion1.0.0/versionnamesentinel-demo/namedescriptionSentinel 限流熔断实战示例/descriptionpropertiesjava.version21/java.versionspring-cloud.version2025.1.3/spring-cloud.versionspring-cloud-alibaba.version2025.1.0.0/spring-cloud-alibaba.version/propertiesdependencyManagementdependenciesdependencygroupIdorg.springframework.cloud/groupIdartifactIdspring-cloud-dependencies/artifactIdversion${spring-cloud.version}/versiontypepom/typescopeimport/scope/dependencydependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-alibaba-dependencies/artifactIdversion${spring-cloud-alibaba.version}/versiontypepom/typescopeimport/scope/dependency/dependencies/dependencyManagementdependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdcom.alibaba.cloud/groupIdartifactIdspring-cloud-starter-alibaba-sentinel/artifactId/dependency/dependenciesbuildpluginsplugingroupIdorg.springframework.boot/groupIdartifactIdspring-boot-maven-plugin/artifactId/plugin/plugins/build/project这里有两处要说清楚spring-cloud-starter-alibaba-sentinel没有写版本号因为它的版本由spring-cloud-alibaba-dependencies这个 BOM 统一管理内部固定为 Sentinel 1.8.9。spring-cloud-dependencies和spring-cloud-alibaba-dependencies都以typepom、scopeimport的方式导入这是 Spring Cloud 体系管理依赖版本的标准写法。3.2 第二步application.yml 配置src/main/resources/application.yml完整内容server:port:8080spring:application:name:sentinel-democloud:sentinel:transport:dashboard:127.0.0.1:8858# Sentinel 控制台地址port:8719# 本机与控制台通信的端口eager:true# 启动时就注册到控制台不用等第一次请求logging:level:com.alibaba.cloud.sentinel:infotransport.dashboard告诉应用「控制台在哪」transport.port是本机开给控制台回连的端口。eager: true很关键Sentinel 默认是「懒加载」资源要等第一次被调用才会注册到控制台开了 eager 后应用一启动就能在控制台看到它避免「控制台怎么是空的」这种新手困惑。3.3 第三步写代码启动类SentinelDemoApplication.javapackagecom.example.sentinel;importorg.springframework.boot.SpringApplication;importorg.springframework.boot.autoconfigure.SpringBootApplication;SpringBootApplicationpublicclassSentinelDemoApplication{publicstaticvoidmain(String[]args){SpringApplication.run(SentinelDemoApplication.class,args);}}控制器SeckillController.java把秒杀接口标记成「seckill」资源用于演示限流packagecom.example.sentinel.controller;importcom.alibaba.csp.sentinel.annotation.SentinelResource;importcom.alibaba.csp.sentinel.slots.block.BlockException;importcom.example.sentinel.service.OrderService;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.RequestParam;importorg.springframework.web.bind.annotation.RestController;RestControllerpublicclassSeckillController{privatefinalOrderServiceorderService;publicSeckillController(OrderServiceorderService){this.orderServiceorderService;}GetMapping(/seckill)SentinelResource(valueseckill,blockHandlerseckillBlockHandler,fallbackseckillFallback)publicStringseckill(RequestParamlonguserId,RequestParamlonggoodsId){returnorderService.createOrder(userId,goodsId);}// 被限流或熔断时走这里参数和原方法一致末尾多一个 BlockExceptionpublicStringseckillBlockHandler(longuserId,longgoodsId,BlockExceptionex){return系统繁忙请稍后再试被限流/降级;}// 业务抛异常时走这里参数和原方法一致末尾多一个 ThrowablepublicStringseckillFallback(longuserId,longgoodsId,Throwableex){return秒杀下单失败库存不足或服务异常;}}这里有个必须讲清楚的点blockHandler和fallback是两个完全不同的兜底很多人搞混。blockHandler只在被 Sentinel 规则拦截抛BlockException时触发对应限流、熔断这种「流量保护」fallback只在业务代码自己抛异常时触发对应真实的下游故障。两者的方法签名也不同blockHandler末尾多一个BlockExceptionfallback末尾多一个Throwable。服务层OrderService.java把下单方法标记成「createOrder」资源用于演示熔断降级。代码里故意埋了两个坑30% 概率慢调用、10% 概率抛异常用来模拟真实的下游抖动packagecom.example.sentinel.service;importcom.alibaba.csp.sentinel.annotation.SentinelResource;importcom.alibaba.csp.sentinel.slots.block.BlockException;importorg.springframework.stereotype.Service;importjava.util.Random;ServicepublicclassOrderService{privatefinalRandomrandomnewRandom();/** * 模拟下单内部调用下游库存服务。 * 30% 概率慢调用sleep 300ms触发慢调用比例降级 * 10% 概率抛异常触发异常比例降级 */SentinelResource(valuecreateOrder,blockHandlercreateOrderBlockHandler,fallbackcreateOrderFallback)publicStringcreateOrder(longuserId,longgoodsId){if(random.nextInt(100)30){try{Thread.sleep(300);}catch(InterruptedExceptione){Thread.currentThread().interrupt();}}if(random.nextInt(100)10){thrownewRuntimeException(下游库存服务超时);}return下单成功用户userId 抢到商品goodsId;}publicStringcreateOrderBlockHandler(longuserId,longgoodsId,BlockExceptionex){return下单接口被限流请稍后重试;}publicStringcreateOrderFallback(longuserId,longgoodsId,Throwableex){return下单失败ex.getMessage();}}SentinelResource(value createOrder)把整个createOrder方法标记成独立资源这样熔断规则可以精确作用到「下单」这一层而不是整个秒杀接口——真实项目里限流常配在入口网关/Controller熔断常配在具体调用下游的地方Service粒度越细保护越精准。3.4 第四步启动控制台并配规则先从 Sentinel 官方仓库alibaba/Sentinel的 Releases 页下载控制台 jar 包 sentinel-dashboard-1.8.9.jar约 22MB与客户端 1.8.9 版本一致下载后执行java-Dserver.port8858-Dcsp.sentinel.dashboard.serverlocalhost:8858\-Dproject.namesentinel-dashboard-jarsentinel-dashboard-1.8.9.jar浏览器打开http://localhost:8858默认账号密码都是sentinel。然后启动你的应用mvn spring-boot:run回到控制台左侧「簇点链路」里能看到seckill和createOrder两个资源因为开了eager: true启动即可见。配流控规则限流对资源seckill点「流控」→「新增流控规则」配置项值含义资源名seckill要限流的资源阈值类型QPS按每秒请求数限流单机阈值2每秒最多放 2 个请求流控模式直接对该资源本身生效流控效果快速失败超阈值直接拒绝配降级规则熔断对资源createOrder点「降级」→「新增降级规则」配置项值含义资源名createOrder要熔断的资源降级策略慢调用比例响应慢的比例超阈值就熔断最大 RT100ms响应超过 100ms 算慢调用比例阈值0.5慢调用占比超过 50% 触发熔断时长10s熔断持续 10 秒最小请求数5至少 5 个请求才开始统计3.5 第五步验证效果用一段 shell 循环模拟并发请求观察限流是否生效阈值 QPS2连发 10 次应该大部分被拒foriin$(seq110);docurl-shttp://localhost:8080/seckill?userId1goodsId100donewait你会看到输出里混着「下单成功」和「系统繁忙请稍后再试被限流/降级」。回到控制台「实时监控」页能看到 QPS 曲线在阈值处被压平这就是限流在起作用。再连续刷新几次接口触发慢调用/异常观察降级当慢调用比例超过 50% 时createOrder进入熔断请求会快速走blockHandler返回「下单接口被限流」不再傻傻等下游。四、Sentinel 踩坑经验与最佳实践先说结论Sentinel 最大的坑集中在「控制台看不到东西」和「规则重启就丢」这两件事上其余都是边角。4.1 控制台看不到应用或资源这是新手遇到最多的问题几乎可以按顺序排查应用没发请求没开eager: true时资源要第一次被调用才注册。解决办法就是配spring.cloud.sentinel.eager: true。控制台地址配错transport.dashboard写成了应用自己的地址或者控制台端口写错。控制台是独立进程地址要指向它。网络不通控制台和应用的通信是双向的——应用主动连控制台的dashboard端口这里 8858控制台回连应用的transport.port8719。防火墙拦了任何一条都连不上。4.2 规则重启就丢生产必须做持久化这是最容易被忽视的坑。控制台里配的规则默认存在内存里应用一重启规则全没了。生产环境必须把规则持久化到外部存储靠 Sentinel 的「动态数据源」Datasource机制规则存到 Nacos、Redis 或本地文件应用启动时拉取控制台改规则时写入。这样规则才能跟着配置走应用漂移到哪台机器都能加载。4.3 blockHandler 和 fallback 别混用前面强调过blockHandler处理「被规则拦截」fallback处理「业务异常」。如果把熔断后的兜底逻辑写在fallback里会发现熔断根本没触发兜底——因为熔断抛的是BlockException走的是blockHandler。反过来业务真实报错时blockHandler也不会管。一句话记住限流熔断找 blockHandler业务报错找 fallback。4.4 阈值别拍脑袋要压测QPS 阈值设 100 还是 1000不能凭感觉。正确做法是先用压测工具JMeter、wrk测出接口的真实容量再留 20%~30% 余量作为限流阈值。设太低正常流量被误杀设太高等于没设。熔断的「最大 RT」也要基于 P99 响应时间设定而不是平均值。五、Sentinel vs Resilience4j vs Hystrix性能与技术选型先说结论新项目在 Spring Cloud 体系里优先选 Sentinel要控制台或 Resilience4j要轻量Hystrix 已经退场。维度HystrixResilience4jSentinel维护状态已停止维护进入维护模式活跃活跃阿里开源可视化控制台有但陈旧无需集成第三方有功能全、实时监控规则动态修改需重启部分支持控制台点改即生效限流能力弱信号量/线程池有RateLimiter强QPS/线程数/热点参数熔断能力有有有慢调用/异常比例/异常数规则持久化无需自行实现支持 Nacos/Redis/文件等中文社区一般一般非常活跃Hystrix 早在 2018 年就宣布停止新功能开发现在的新项目几乎没有理由再选它。Resilience4j 的优势是轻量、无控制台依赖、模块化适合对控制台没需求的纯代码流派。Sentinel 的优势是控制台强大、规则热更新、中文资料多在 Spring Cloud Alibaba 体系里开箱即用适合国内团队和需要实时监控告警的场景。两者并不冲突——Spring Cloud Circuit Breaker 抽象层同时支持它们可以按团队习惯选。六、总结限流和熔断是分布式系统对抗「雪崩」的两道基础闸门Sentinel 用一条责任链把它们做成了开箱即用的能力。回顾三个关键点限流看入口熔断看下游。限流配在 Controller/网关挡住洪峰熔断配在 Service 调用下游的地方快速失败防止拖垮整条链。blockHandler 和 fallback 各管各的。被规则拦截走 blockHandler业务异常走 fallback别混用。规则要持久化阈值要压测。内存规则重启就丢生产必须接 Nacos/Redis 做动态数据源阈值基于压测数据留余量不拍脑袋。Sentinel 最值钱的不是那几行代码而是控制台带来的「可观测 可热改」流量曲线实时可见规则改了立刻生效。等你真正扛过第一次大促回头看这几道闸门会庆幸自己提前装上了。
阅读完成 · 觉得有帮助?
咨询建站