尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

微服务雪崩场景下 Sentinel 限流熔断与降级实战

微服务雪崩场景下 Sentinel 限流熔断与降级实战 线上业务半夜报警订单接口的 TP99 从正常 80ms 直接飙到 3000ms数据库 CPU 一路冲到 100%紧接着支付回调超时、对账任务失败、短信服务堆积一个接口抖动整个电商链路全跪了。这几乎是每一套微服务系统在流量到达一定规模后的必经之路。无论你用的是 Spring Cloud、Dubbo 还是 Kubernetes只要服务被拆成了多个节点、多个实例服务间的调用就不再是“本地函数调用”那么简单网络可能超时、节点可能宕机、上游流量可能瞬间打满线程池。而“一个服务挂了连带一圈服务全部挂掉”这个现象有一个专门的词叫服务雪崩。本文要聊的 Sentinel正是目前微服务体系里应对限流、熔断、系统保护最主流的解决方案之一。它不是单纯的“接口限流工具”而是一套完整的“流量治理”框架。读完这篇文章你将理解微服务为什么必须有限流和熔断不做的代价是什么Sentinel 的核心概念、运行原理以及它与传统限流方案比如 Redis Lua、Guava RateLimiter的差异如何在一个 Spring Cloud 项目里接入 Sentinel完成限流规则、熔断降级规则的配置生产环境落地时最容易踩的坑和最佳实践建议。1. 先理解微服务为什么会“雪崩”在单体应用时代一个应用包打天下所有请求都进同一个进程虽然问题很多但至少“挂”是挂在同一个进程里。到了微服务阶段服务被拆成用户服务、订单服务、库存服务、支付服务、消息服务等进程和网络成为常态一个服务调用另一个服务链路变长故障传播路径也变长了。服务雪崩的核心链路可以概括成三步单个服务实例出现性能问题比如某个数据库慢查询导致接口响应时间拉长。线程池或连接池被占满上游服务调用该接口时请求长时间得不到响应线程一直被占用不释放新的请求不断挤进来最终把整个服务的 Tomcat 线程池、HTTP 连接池全部耗尽。故障级联扩散被拖垮的服务又会影响它的上游调用方上游的线程也被占满形成“多米诺骨牌”式的连锁反应。举个更具体的例子。假设有一个典型的电商链路网关 - 订单服务 - 库存服务 - 商品服务。如果商品服务的数据库出现一条慢 SQL响应时间从 20ms 变成 2s订单服务调用商品服务的线程就被卡住。订单服务的 Tomcat 默认线程池如果是 200那么 200 个线程很快就全部卡在等待商品服务响应上。这时候新的下单请求进来线程池拒绝新任务订单服务自己也挂了。库存服务还在正常调度但它的请求也依赖订单服务的返回结果于是库存服务也开始堆积请求。最终结果是一次数据库抖动导致一整条业务链路全部瘫痪。这里有一个很多人没想透的问题微服务架构的弹性并不来自服务拆分本身而是来自每个服务具备独立的故障隔离能力。拆分只是把模块边界划清楚了真正让系统在局部故障时不至于整体崩溃的是限流、熔断、隔离、降级这些治理手段。2. 限流与熔断的区别很多新手混为一谈限流和熔断是两件事但实际项目里很多人把它们当成“反正都是保护服务”的同一个东西然后在配置规则时就很困惑。限流Rate Limiting解决的是“流量过大”的问题。它的核心思想是在单位时间内只允许一定数量的请求通过超出的请求直接拒绝、排队或者走降级逻辑。限流属于事前预防保护的是自己不被打垮。比如某个接口的数据库只能扛 100 QPS那就在入口处限制 100 QPS多余的请求直接快速返回“系统繁忙”。熔断Circuit Breaking解决的是“下游已经不行了”的问题。它的核心思想是当某个下游服务的错误比例或耗时超过阈值时直接切断对这个下游的调用不再把请求发给它而是快速返回兜底结果。熔断属于事中保护保护的是自己不去调用已经生病了的服务。熔断器有三个状态关闭、打开、半开。关闭时正常调用错误比例达到阈值后变为打开直接短路拦截请求经过一段冷却时间后进入半开状态放少量请求试探下游是否恢复恢复则重新关闭没恢复则继续打开。用一句话总结限流是“车太多我先限行”熔断是“前面路塌了我不往那条路走了”。为什么两者缺一不可因为很多系统只做了限流但限流只在当前服务入口生效。假设你现在订单服务只有 100 QPS 的额度于是把限流配置成 100。这时候库存服务出了故障订单服务调用库存的请求大量超时。此时订单服务的线程还是被库存服务卡住了因为这些请求已经通过了入口限流但无法在下游快速返回。如果没有熔断机制订单服务照样会被库存服务拖垮。反过来只做熔断不做限流当突发流量洪峰到来时虽然没有调用已熔断的下游但其他正常的下游接口同样会被海量请求打爆。正解是入口限流挡住超出能力范围的流量链路熔断切断对不可用下游的依赖调用二者配合才能真正把故障隔离在一个局部范围内。3. Sentinel 是什么定位与核心价值Sentinel 是阿里巴巴开源的分布式流量治理组件官方定位是“面向分布式服务架构的高可用防护组件”。它在 2018 年开源之后加入了 CNCF 云原生计算基金会目前在 GitHub 上已经有 2 万多个 Star是国内微服务项目里非常主流的选择。Sentinel 的核心价值可以拆成三层流量控制不仅支持 QPS 维度的限流还支持并发线程数限流、热点参数限流以及基于调用关系的限流。熔断降级支持慢调用比例、异常比例、异常数三种维度的熔断策略。系统自适应保护根据系统负载、CPU 使用率、总体 QPS 等指标自动对入口流量进行控制防止系统因过载而整体崩溃。相比自己用 Redis 写一套限流逻辑Sentinel 最大的优势在于它把“规则管理、指标统计、实时监控、动态推送”这些问题都解决了。我们不需要自己统计 QPS、不需要自己写滑动窗口算法、不需要自己设计规则的数据结构Sentinel 提供了一套完整的体系。值得注意的是Sentinel 并不是只能用于 Java 微服务。它原生支持 Spring Boot / Spring Cloud / Dubbo 等主流框架也提供了 OpenSergo 标准协议和多种语言的适配比如 Go 语言的 Sentinel-Golang。对大多数 Java 技术栈的团队来说Sentinel Spring Cloud Alibaba 是当前最省力的接入方式。接下来从技术原理层面拆解 Sentinel 最核心的机制它到底是怎么做到既不阻塞业务逻辑又能精准控制流量的。4. Sentinel 的核心机制滑动窗口与责任链要理解 Sentinel 的限流和熔断需要先搞清楚两件事它是怎么统计指标比如 QPS、慢调用比例的以及这些统计结果是怎么影响请求放行与否的。4.1 滑动窗口统计Sentinel 的指标统计不是简单地拿“最近 1 秒请求数”这个总量来判断的。如果只统计当前秒的请求数会存在“秒级毛刺”问题比如第 1 秒只来了 10 个请求第 2 秒前 100 毫秒突然来了 100 个请求这 100 毫秒的数据如果只按“每秒”维度看显得量很小但瞬间对后端的压力是很大的。Sentinel 默认使用滑动窗口来统计实时指标。它将一个时间窗口比如 1 秒切分成若干个小格子比如 2 个 500ms 的格子随时间推移格子不断向前滑动窗口内永远统计的是最近一段时间的数据。这样做的好处是统计结果更平滑能捕获短时间内的突发流量同时又不至于像实时计数器一样抖动剧烈。如果你使用过 Sentinel Dashboard在监控页面上看到的那条实时 QPS 曲线底层就是滑动窗口计算出来的结果。4.2 ProcessorSlotChain 责任链Sentinel 的限流、熔断、系统保护等功能不是分散写在业务代码里的而是通过一个责任链机制串联起来的。每个请求进入 Sentinel 后会被交给一个ProcessorSlotChain处理。这个责任链上有多个 Slot不同的 Slot 负责不同的事情NodeSelectorSlot负责收集资源的调用路径构建调用树ClusterBuilderSlot负责存储资源的统计信息和调用者信息StatisticSlot负责实时统计请求的 QPS、线程数、异常数等指标FlowSlot负责根据限流规则进行流量控制判断DegradeSlot负责根据熔断降级规则判断是否放行SystemSlot负责系统自适应保护逻辑。这些 Slot 是链式执行的流控判断失败就直接抛出BlockException请求被拦截判断通过则继续走到实际业务逻辑。这也是为什么我们在业务代码里只需要加一个SentinelResource注解被保护的方法在真正执行业务逻辑之前就已经经过了层层“安检”。理解了滑动窗口和责任链我们就能明白Sentinel 的限流和熔断判断是实时发生的每次请求都会根据当前统计指标和规则做出放行或拦截决定而不是定时批量处理。5. Sentinel 与其他限流方案的对比很多团队在引入 Sentinel 之前已经用过一些自研或开源的限流方案。这里做一个横向对比帮助大家做技术选型。方案限流维度熔断能力实时监控规则动态更新适用场景Redis Lua 脚本单机/分布式 QPS无需要自己开发需要自己开发简单的全局限流比如 Nginx 网关层兜底Guava RateLimiter单机 QPS令牌桶无无无单机应用限流不适用于集群Resilience4j单机 QPS/并发数支持基于断路器模式需要集成 Micrometer支持偏轻量的熔断降级需要自己组装Hystrix已停止维护线程池隔离/信号量隔离支持支持但已停止新功能开发支持老项目迁移新项目不建议再引入Sentinel单机/集群 QPS、线程数、热点参数、系统负载支持慢调用比例、异常比例、异常数自带 Dashboard开箱即用支持通过 Nacos 等配置中心推送微服务限流熔断降级一体化治理这里重点说为什么 Redis Lua 不能完全替代 Sentinel。Redis 限流适合做全局限流比如限制整个 APP 级别的接口调用总量它的问题是每次请求都要经过一次 Redis 网络调用有额外的 RTT 开销其次 Redis 限流没有统计调用链路和资源维度的能力你没法知道“哪个上游服务调用我最多”“哪个接口缠慢了”更重要的是Redis 限流没有熔断能力它解决不了下游故障引起的线程堆积问题。所以实践中的常见分层策略是网关层用 NginxLua 做粗粒度 IP/全局限流服务内用 Sentinel 做细粒度的接口限流、熔断降级和热点防护。6. Spring Cloud 项目接入 Sentinel环境准备与依赖配置下面进入实操部分。本文以 Spring Cloud Alibaba Sentinel 为例演示一个最小的完整接入流程。6.1 环境准备建议环境如下版本请以实际项目为准本文重点演示通用接入思路。JDK 1.8Maven 3.6Spring Boot 2.7.x或 3.x需要对应适配版本Spring Cloud Alibaba 2021.x 或 2022.xSentinel Dashboard 1.8.x 以上由于 Spring Cloud Alibaba 和 Spring Boot 之间版本耦合较强如果版本不匹配会出现启动时报错或者 Sentinel 自动配置不生效的问题。建议直接使用 Spring Initializr 生成一个标准项目再按下面的方式添加依赖。6.2 在 pom.xml 中添加依赖首先在pom.xml中引入 Spring Cloud Alibaba 的 BOM 和 Sentinel 相关依赖dependencyManagement dependencies dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-alibaba-dependencies/artifactId version2021.0.5.0/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement dependencies !-- Spring Boot Web 模块 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- Sentinel 针对 Spring Cloud 的适配 -- dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-starter-alibaba-sentinel/artifactId /dependency !-- 可选若使用 Nacos 作为规则持久化配置中心 -- dependency groupIdcom.alibaba.csp/groupId artifactIdsentinel-datasource-nacos/artifactId /dependency !-- 可选actuator 监控 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-actuator/artifactId /dependency /dependencies注意spring-cloud-starter-alibaba-sentinel里已经包含了 Sentinel 的核心依赖不需要再单独添加sentinel-core。如果只是纯 Spring MVC 项目而不是完整的 Spring Cloud 项目也可以直接只依赖sentinel-annotation-aspectj和sentinel-transport-simple-http。6.3 配置 application.yml在application.yml中配置服务端口、应用名和 Sentinel Dashboard 连接信息server: port: 8080 spring: application: name: sentinel-demo cloud: sentinel: transport: dashboard: localhost:8080 port: 8719 eager: true datasource: # 后续可从配置中心动态读取规则此处先不配置配置说明transport.dashboardSentinel Dashboard 的地址控制台启动后会监听在这个地址上。transport.port应用与 Dashboard 通信的端口默认 8719。如果被占用会自动尝试下一个端口。eager是否在应用启动时就主动连接 Dashboard。默认是懒加载等第一个请求进来后才注册开发环境建议设为true。6.4 下载并启动 Sentinel DashboardSentinel Dashboard 是一个独立的 Java 应用可以直接从 Sentinel 的 GitHub Release 页面下载对应的 jar 包。下载后执行java -Dserver.port8080 -Dcsp.sentinel.dashboard.serverlocalhost:8080 -Dproject.namesentinel-dashboard -jar sentinel-dashboard-1.8.6.jar启动后在浏览器访问http://localhost:8080默认用户名和密码都是sentinel。7. 完整示例定义一个被保护的接口环境就绪后我们写一个简单的 Controller并给它加上 Sentinel 资源保护。7.1 创建测试接口// 文件路径src/main/java/com/example/sentinel/controller/HelloController.java package com.example.sentinel.controller; import com.alibaba.csp.sentinel.annotation.SentinelResource; import com.alibaba.csp.sentinel.slots.block.BlockException; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; RestController public class HelloController { GetMapping(/hello) SentinelResource(value hello, blockHandler helloBlockHandler) public String hello(RequestParam(value name, defaultValue sentinel) String name) { return Hello, name; } /** * 限流或熔断后触发的兜底方法 */ public String helloBlockHandler(String name, BlockException ex) { return 系统繁忙请稍后重试: ex.getClass().getSimpleName(); } }这里的关键是SentinelResource注解。value hello表示给这个资源起了一个名字后续配置限流规则时可以通过这个名字来指定。blockHandler指定了当请求被 Sentinel 拦截时执行的兜底方法。这里有一个新手容易踩的坑blockHandler方法的签名必须和被保护方法保持一致并额外追加一个BlockException参数。如果没有严格按照这个签名写Sentinel 在运行时会找不到对应的 handler导致兜底逻辑不生效。7.2 通过代码定义限流规则规则可以通过 Dashboard 在线配置也可以直接在应用启动时通过代码加载。先用代码的方式演示因为这种方式更容易理解规则的结构。// 文件路径src/main/java/com/example/sentinel/config/SentinelRuleConfig.java package com.example.sentinel.config; import com.alibaba.csp.sentinel.slots.block.RuleConstant; import com.alibaba.csp.sentinel.slots.block.flow.FlowRule; import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager; import org.springframework.context.annotation.Configuration; import javax.annotation.PostConstruct; import java.util.ArrayList; import java.util.List; Configuration public class SentinelRuleConfig { PostConstruct public void initFlowRules() { ListFlowRule rules new ArrayList(); FlowRule rule new FlowRule(); rule.setResource(hello); rule.setGrade(RuleConstant.FLOW_GRADE_QPS); rule.setCount(1); rules.add(rule); FlowRuleManager.loadRules(rules); } }这段代码定义了一条限流规则hello资源每秒最多通过 1 个请求。超过的请求会直接触发blockHandler。注意这里用了PostConstruct意味着规则会在 Spring 容器启动后立即加载。如果在同一个应用里既通过代码加载规则又从 Dashboard 动态修改规则那 Dashboard 的修改会覆盖代码加载的规则但是应用重启后又会重置回代码里的规则。生产环境推荐使用 Nacos 或 Apollo 做规则持久化避免应用重启后规则丢失。7.3 通过 Dashboard 配置限流规则除了代码方式也可以在 Sentinel Dashboard 上直接配置。在控制台左侧菜单找到“流控规则”点击“新增流控规则”填写资源名hello针对来源default阈值类型QPS单机阈值1保存后规则会实时推送到对应的应用中。Dashboard 的优势是可以实时查看监控数据方便压测时观察接口的通过和拒绝情况。两种配置方式没有优劣之分代码方式适合在 CI/CD 里做规则版本管理DashBoard 方式适合测试环境快速调整。生产环境建议把规则托管到配置中心如 Nacos通过spring.cloud.sentinel.datasource配置动态刷新。8. 熔断降级规则三个维度与配置示例限流规则解决了流量过大问题熔断规则解决的是下游故障问题。Sentinel 支持三种熔断策略慢调用比例当请求的响应时间超过设定的最大 RT 时认为是慢调用。在统计时间窗口内如果慢调用比例超过阈值则熔断。异常比例统计时间窗口内如果请求的异常比例超过阈值则熔断。异常数统计时间窗口内如果请求的异常数超过阈值则熔断。熔断器打开后在熔断时长内请求会直接走blockHandler不再进入业务代码。熔断时长结束后进入半开状态放行部分请求探测下游是否恢复。下面通过代码定义一条熔断规则当hello资源的异常比例超过 50% 时熔断 10 秒。// 文件路径src/main/java/com/example/sentinel/config/SentinelDegradeRuleConfig.java package com.example.sentinel.config; import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule; import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager; import org.springframework.context.annotation.Configuration; import javax.annotation.PostConstruct; import java.util.ArrayList; import java.util.List; Configuration public class SentinelDegradeRuleConfig { PostConstruct public void initDegradeRules() { ListDegradeRule rules new ArrayList(); DegradeRule rule new DegradeRule(); rule.setResource(hello); rule.setGrade(com.alibaba.csp.sentinel.slots.block.RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO); rule.setCount(0.5); rule.setTimeWindow(10); rule.setMinRequestAmount(5); rules.add(rule); DegradeRuleManager.loadRules(rules); } }参数解释setGrade熔断策略这里用的是异常比例。setCount(0.5)异常比例阈值 50%。setTimeWindow(10)熔断持续 10 秒。setMinRequestAmount(5)触发熔断的最小请求数避免只有一两个请求出错就误熔断。这里有一个容易被忽略的设计minRequestAmount很重要。如果只有一次请求且刚好抛异常异常比例是 100%此时立刻熔断可能导致正常的服务被误伤。加上最小请求数可以让熔断决策更稳健。9. 运行验证怎样判断规则是否生效启动应用后用下面的命令连续快速访问接口curl http://localhost:8080/hello?namezhangsan由于限流规则设置的是 QPS 为 1第一次请求会正常返回Hello, zhangsan紧接着快速访问第二次应该会触发限流兜底系统繁忙请稍后重试: FlowException说明限流规则已经生效。此时打开 Sentinel Dashboard在“实时监控”页面可以看到hello资源的 QPS 曲线其中通过的请求记为“p”标记的绿色点被拦截的请求记为“b”标记的红色点。如果验证失败优先检查以下几点应用是否成功连接 Dashboard看启动日志里有没有Sentinel rule loaded或成功连接到 Dashboard 的记录。资源名是否匹配代码里SentinelResource(value hello)和规则里的setResource(hello)必须完全一致。是否走了本地的规则管理器如果既用代码加载了规则又配置了 Nacos 动态数据源需要确认最终生效的规则到底是哪一份。10. 生产环境常见问题与排查方法问题现象可能原因排查方式解决方案应用启动报错Sentinel not found或NoSuchMethodErrorSpring Cloud Alibaba 版本与 Sentinel 版本不匹配查看 Maven 依赖树mvn dependency:tree使用官方文档推荐的版本组合统一 BOM 管理Dashboard 看不到应用列表应用懒加载没有请求进来或者端口 8719 被防火墙拦截先访问一个接口再刷新 Dashboard查看启动日志确认连接信息设置spring.cloud.sentinel.eagertrue检查防火墙限流规则不生效资源名不一致或规则加载时机早于资源注册检查资源名确认规则管理器加载成功确保访问接口后资源已注册再配置规则兜底方法没有触发报BlockException未处理blockHandler方法签名错误查看应用日志中是否有 handler 找不到的告警严格按照方法签名参数一致 追加BlockException参数Dashboard 动态修改规则后应用重启规则丢失规则存储在内存中未做持久化确认是否配置了datasource数据源接入 Nacos 或 Apollo 作为规则配置中心熔断恢复后马上又熔断下游服务恢复需要时间半开探测请求仍然失败查看熔断监控曲线确认下游服务健康状态适当调大timeWindow结合健康检查做更稳妥的恢复11. 最佳实践与工程落地建议11.1 正确选择限流维度不是所有接口都适合用 QPS 限流。QPS 适合读多写少、响应时间稳定的接口对耗时波动较大的接口按并发线程数限流可能更合理。比如一个导入接口单次耗时可能从 1 秒到 10 秒不等QPS 限流难以判断“到底多少并发才是安全的”而线程数限流可以直接限制“同时最多只有 10 个线程在处理这个接口”。11.2 资源命名规范开发前期最容易埋雷的是资源名乱写。有人用接口路径GET:/hello有人用方法名hello还有人用完整类名加方法名到后期配置规则时根本对不上。建议在项目启动时统一约定资源名与接口的语义绑定如order:create、order:query、inventory:deduct这样在 Dashboard 上看到资源名就能知道是哪个业务场景。11.3 规则持久化必须做默认情况下Sentinel Dashboard 配置的规则只存在于应用内存中应用重启后规则会丢失。生产环境一定要把规则放到 Nacos、Apollo 或 ZooKeeper 等配置中心通过spring.cloud.sentinel.datasource配置动态推送。这样规则的变更可以走配置中心审批流应用无需重启也不用手动在每台机器上重新配置。11.4 兜底逻辑不要只返回一句“系统繁忙”blockHandler需要有业务语义。比如查询商品详情被限流兜底可以返回本地缓存的旧数据下单接口被限流兜底可以引导用户稍后重试支付回调被熔断兜底应该记录消息到 MQ事后补偿。降级不是应付了事而是给用户一个可接受的响应同时确保数据最终一致。11.5 Sentinel 与网关限流的配合Sentinel 也可以用于微服务网关Spring Cloud Gateway的流量控制主要是针对路由维度进行限流。实践中不建议把所有限流精细策略都放在网关层网关适合做粗粒度的泛化限流服务层做更精细的业务限流这样职责更清晰。11.6 压测先行限流阈值不是拍脑袋定的。上线前建议使用压测工具如 JMeter、wrk、Locust确定每个核心接口的“安全 QPS”和“最大 RT”再据此设置限流阈值。阈值设置过小会导致正常业务被误伤设置过大会失去保护意义。压测时注意观察 Dashboard 上的实时监控结合线程数、CPU、接口 RT 综合判断。12. 总结与后续学习方向通过这篇文章我们从“服务雪崩”的真实事故出发理解了限流和熔断在微服务体系里各自承担的责任然后从原理上拆解了 Sentinel 的滑动窗口统计和责任链机制最后在一个 Spring Cloud 项目中完整演示了依赖引入、规则配置、接口保护和验证过程。Sentinel 是一个学习曲线相对平缓、但生产落地细节很多的组件。建议你再往下走这几个方向阅读官方文档中的簇点链路和热点参数限流章节这两个功能在实际项目中使用频率很高尝试把 Sentinel 规则接入 Nacos模拟一次“配置中心改规则应用实时生效”的完整流程压测时重点观察 Dashboard 的实时监控理解 QPS、线程数、RT 三者之间的联动关系如果你的团队在用 Kubernetes可以进一步学习 Sentinel 的 Kubernetes 部署方式和基于 OpenSergo 的流量治理标准。系统的稳定性不是上线后靠运气换来的而是靠一个个规则、一次次压测和一条条兜底逻辑提前设计出来的。把 Sentinel 这类流量治理组件用好是每一套微服务系统走向生产环境的重要一步。
返回列表