尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Sentinel实战:从流控规则到熔断降级,守护微服务稳定性

Sentinel实战:从流控规则到熔断降级,守护微服务稳定性 作为一个天天跟线上流量、服务稳定性打交道的开发者我对Sentinel这个词可以说又爱又恨。爱的是它在关键时刻真的能救命恨的是如果只停留在“会用”层面一旦遇到流量突刺、接口互相拖垮、依赖超时堆积这些问题还是会手忙脚乱。这些年我见过太多团队把Sentinel控制台装上、配几条流控规则就以为万事大吉结果线上一次真实的洪峰流量过来照样被打穿。这篇总结不是官方文档的复读而是把我自己从安装、接入、配规则、踩坑到最终真正理解它设计思路的过程整理出来。无论你是刚接触微服务的初级开发还是已经在用Sentinel但总觉得差点意思的进阶用户我都建议按顺序看完尤其是后面规则参数和网关集成的部分很多细节是你在控制台上点鼠标时根本不会注意到的。1. Sentinel到底解决什么问题从服务稳定性说起1.1 没有防护时线上会发生什么先讲一个我真实经历过的场景。某个促销活动开始前团队把所有服务的副本数都扩了一倍压测也通过了本以为万事大吉。结果活动刚开始五分钟订单服务的一个下游库存接口响应从 50ms 慢慢涨到 3 秒接着订单服务的 Tomcat 线程池被打满紧接着依赖订单服务的支付回调、物流查询全部开始堆积超时最后整条链路雪崩。这里面有个很容易被忽略的细节即便你做了超时控制只要线程池里的线程还在傻等下游返回新的请求进来依然会继续占用新线程。线程不会被“超时”释放它只是被挂起等待直到真正收到响应或者触发 socket 超时。所以一旦下游变慢你的服务会在极短时间内被这种“等待型占用”拖垮这时候再想去扩容、重启基本来不及了。Sentinel这类组件解决的核心问题就是在这类场景发生之前或者发生的瞬间用流控、熔断、系统保护等手段把风险挡在外面而不是等资源耗尽之后再被动处理。1.2 Sentinel的核心定位与适用场景阿里巴巴开源的Sentinel官方叫法是“面向分布式服务架构的流量控制组件”。它的定位非常明确以“资源”为最小粒度对进入资源的流量进行实时统计和控制同时在资源不稳定时主动熔断降级保证整个系统在极端流量下依然能留有余力。我习惯把它理解成两件事流量入口管控限制单位时间内的请求量超出的请求直接拒绝或排队避免系统被瞬间流量打崩。资源稳定性兜底监控资源的调用RT、异常比例等指标当指标恶化到阈值时自动切断对下游的依赖调用给下游留出恢复时间。所以它特别适合这几类场景核心接口出现突发流量需要快速拒绝多余请求下游依赖不稳定需要熔断降级保护主链路秒杀、抢购等流量峰值明显的业务需要细粒度控制某个热点参数比如同一个商品ID、同一个用户ID的访问频率。还有一个容易被忽略的场景系统资源保护。比如 CPU 或负载已经很高时Sentinel可以自动降低入口流量避免整机彻底卡死。这一点在容器化部署和混部环境下尤其有用。1.3 Sentinel和Hystrix/Resilience4j的差异很多人会拿Sentinel和Hystrix对比。简单说Hystrix的核心是线程池隔离和熔断通过给每个依赖分配独立线程池来实现故障隔离。但线程池隔离会带来明显的线程切换开销而且每个依赖都要独占线程资源对高并发场景不太友好。Sentinel默认采用信号量隔离不额外创建线程只是对并发调用数进行计数控制。这样性能损耗极小官方数据显示在同等条件下Sentinel的开销远低于Hystrix。Resilience4j设计上更轻量适合新项目按需引入但它本身不提供控制台这种可视化运维能力很多能力需要自己组装。Sentinel最突出的优势其实是“规则配置与实时可见性”。你可以在控制台上动态调整规则不需要重启应用规则推送后秒级生效同时能看到实时的调用链路和指标曲线。这一点在线上排障时价值极高。2. 安装与启动最快跑起来一版2.1 控制台下载与启动Sentinel分为控制台Dashboard和客户端 SDK 两部分。控制台是一个独立的 Spring Boot 应用用于可视化查看实时数据、配置规则客户端是嵌入到你的业务应用里的 jar 包。启动控制台最简单的方式是直接下载官方发布的 release 包。我比较推荐用源码构建因为可以顺便看一些内部实现但如果你只想要一个能用的环境直接去 GitHub 仓库的 Releases 页面下载sentinel-dashboard-xxx.jar就行。下载之后执行java -Dserver.port8080 -Dcsp.sentinel.dashboard.serverlocalhost:8080 -Dproject.namesentinel-dashboard -jar sentinel-dashboard-1.8.6.jar启动参数里-Dserver.port指定控制台端口-Dcsp.sentinel.dashboard.server是控制台自身地址这个会在接下来的客户端接入时用到。启动完成后浏览器访问http://localhost:8080默认登录账号密码都是sentinel。这里有一个容易踩的坑如果你本机已经启动过别的服务占用了 8080 端口启动会失败。所以端口最好提前确认一下或者指定一个不常用的端口比如 8858。2.2 客户端接入的基本配置客户端接入分为两步。第一步是在项目的pom.xml里引入依赖dependency groupIdcom.alibaba.csp/groupId artifactIdsentinel-core/artifactId version1.8.6/version /dependency如果用的 Spring Cloud Alibaba通常只需要引入spring-cloud-starter-alibaba-sentinel这一个依赖它会自动传递依赖到核心包dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-starter-alibaba-sentinel/artifactId version2021.0.5.0/version /dependency第二步是在application.yml中配置控制台地址和应用名spring: application: name: order-service cloud: sentinel: transport: dashboard: localhost:8080 port: 8719 eager: true关键参数说明transport.dashboard控制台地址客户端会通过这个地址向控制台注册心跳。transport.port客户端与控制台通信的端口默认 8719。如果本机多个应用共用端口会冲突此时会自动寻找下一个可用端口但为了避免意外最好显式指定。eager: true让应用启动时就主动注册到控制台而不是等到第一次调用发生再注册。如果不设这个参数你会发现控制台上等了很久都没有服务列表直到某个接口被调用后才出现。启动业务应用后回到控制台在“机器列表”里能看到你的服务实例同时Sentinel会为一些默认的 URL 资源自动生成调用量数据。到这里一个最基础的Sentinel环境就算跑通了。2.3 初次访问控制台的注意事项控制台首次登录后是空荡荡的这是正常现象。你需要先让业务接口被调用几次控制台上才会出现对应的资源名和链路信息。另一个常见问题是控制台里看不到链路数据但应用日志里明明有访问。这种情况十有八九是transport.port被防火墙挡住了或者应用与控制台不在同一个网络段心跳报文发不过来。还有一点Sentinel控制台本身不提供认证以外的更多安全能力不要把控制台裸暴露到公网。最好放到内网环境或者通过网关加一层访问控制。默认口令是sentinel/sentinel上线前记得改掉。3. 核心概念与基础规则3.1 资源与规则Sentinel的一切操作都围绕“资源”展开。资源可以是一个方法、一个接口、一段代码甚至可以是一段 SQL 的执行路径。你通过SphU.entry(资源名)包住需要保护的代码块Sentinel就会统计这段代码的调用量、RT、异常等信息并按你配置的规则进行拦截。规则就是对资源行为的约束。同一个资源可以挂多条规则Sentinel会按优先级匹配并执行。规则分为五类流控规则、熔断降级规则、系统保护规则、热点参数规则、授权规则。日常用得最多的是前两类。这里我要特别强调一个理解上的误区Sentinel官方内置的SentinelResource注解和“资源名”的概念要分清。注解里的value属性才是资源名称控制台上看到的是这个名字而不是方法名。如果你不指定那默认资源名是类名方法名但那样维护起来很痛苦。我一般建议在核心接口上显式指定资源名比如SentinelResource(value createOrder, blockHandler createOrderBlockHandler) public Order createOrder(OrderRequest request) { // 业务逻辑 }3.2 五种规则速览这五种规则我用一个通俗的说法给你捋一遍规则类型保护对象核心作用流控规则资源访问量限制 QPS 或并发线程数防止流量突刺打垮服务熔断降级规则资源稳定性当慢调用比例、异常比例或异常数超阈值时直接熔断系统保护规则整机负载当 CPU、load、RT 等系统指标过高时限制入口流量热点参数规则特定参数值针对某个参数值如商品ID做精细化限流授权规则调用来源基于调用方名单白名单或黑名单控制是否放行初次接触不用急着全部理解先把流控和熔断玩明白后面的系统保护和热点参数是进阶。3.3 规则持久化问题这部分是新手最容易踩的大坑。默认情况下你在控制台上配置的规则全部保存在内存里一旦应用重启规则全部丢失。生产环境必须做规则持久化。但Sentinel官方控制台本身的推送逻辑并不完美它采用“控制台推给客户端”的原始 API 模式规则变更保存在各客户端进程内存里。如果你直接在生产环境用官方控制台改规则应用一重启就回到解放前。所以较大规模落地时一般要引入nacos或apollo等配置中心来做规则持久化。用nacos的做法大概是在客户端引入sentinel-datasource-nacos然后在配置中心维护规则 JSON客户端监听配置变更并动态刷新规则。这块内容展开说又是一篇文章建议后面单独实践。4. 流控规则拆解从参数到效果4.1 阈值类型与流控模式流控规则是整个Sentinel里最常用也是参数最丰富的规则。先看控制台里新增流控规则时的几个核心选项。阈值类型有两种QPS每秒允许通过的请求数量。适合大多数 HTTP 接口或 RPC 接口的入口限流。并发线程数同一时刻允许在资源上执行的线程数。适合保护线程池型资源避免下游变慢时线程被大量占用。关于这两个怎么选我的经验是如果你的资源是纯 CPU 计算型用 QPS 限制就够了如果资源涉及到远程调用、IO 操作最好关注并发线程数因为它直接反映“同时有多少请求在等待下游”比 QPS 更能体现资源占用。流控模式有三种直接只统计当前资源的流量超过阈值就触发。关联统计关联资源的流量当关联资源达到阈值时限制当前资源。典型场景是“读写分离”写接口流量过高时限制读接口避免数据库压力过大。链路根据调用入口来区分流量。比如同一个资源getUserInfo被两个入口调用一个来自订单服务一个来自用户中心你可以只对其中一条链路限流。这里我需要补充一个容易被忽略的点链路模式默认不生效。因为Sentinel默认把所有调用都聚合到default入口上需要调用ContextUtil.enter(入口名)才能区分不同调用来源而且还要在控制台勾选“是否从调用链入口流量控制”。不搞清楚这点你配了链路模式但完全没效果别慌先检查入口上下文。4.2 流控效果流控效果决定流量超过阈值后怎么处理。有三种快速失败直接抛异常默认行为。适合对丢弃请求容忍度较高的场景。Warm Up预热模式。阈值从初始值逐步增长到设定值防止冷启动时系统被瞬时大流量打崩。比如缓存刚构建、连接池刚建立时系统处理能力是逐步恢复的此时把流量慢慢放开更合理。排队等待让超出的请求排队进入按固定速率通过本质上是“削峰填谷”。适合需要尽量不丢弃请求但对延时有容忍度的场景比如定时任务、消息拉取类接口。用表格对比更直观流控效果超出后行为适用场景快速失败立即拒绝秒杀、强校验接口Warm Up阈值缓慢上升冷启动、缓存预热排队等待请求排队匀速执行削峰填谷、低峰补数据排队等待这个效果有个细节它依赖maxQueueingTimeMs参数表示请求在队列里能等多长时间。超过这个时间还没轮到执行请求也会被拒绝。所以线上配置排队等待时一定要评估接口的正常 RT 和可容忍的最大等待时间不能无脑配置。4.3 一个完整的流控配置案例假设订单服务的createOrder接口压测最大 QPS 是 2000超过 2400 时 RT 明显上升我们决定限制在 2000 QPS超出直接拒绝。在控制台“新增流控规则”里这样填资源名createOrder针对来源default不区分来源阈值类型QPS单机阈值2000流控模式直接流控效果快速失败配置完成后可以用wrk或JMeter压一下观察控制台的实时监控曲线。如果曲线在 2000 处保持一条平线超出部分全部被拦截说明规则生效。这里我建议你用代码方式也配一条同样规则方便局部调试private void initFlowRule() { ListFlowRule rules new ArrayList(); FlowRule rule new FlowRule(); rule.setResource(createOrder); rule.setGrade(RuleConstant.FLOW_GRADE_QPS); rule.setCount(2000); rule.setLimitApp(default); rules.add(rule); FlowRuleManager.loadRules(rules); }通过代码配置的规则在应用重启后同样会丢失所以这只能用来调试生产还是要走配置中心持久化。5. 熔断降级与系统保护实战5.1 熔断策略慢调用比例、异常比例、异常数流控解决的是“流量太大怎么办”熔断解决的是“依赖已经坏了怎么办”。Sentinel的熔断降级规则有三种策略。慢调用比例统计单位时间窗口内RT 大于设定阈值的请求占比如果占比超过设定比例则进入熔断状态。例如maxRt200比例阈值0.5最小请求数10统计时长1000ms意思是 1 秒内至少 10 个请求其中超过一半 RT 大于 200ms就熔断。异常比例统计单位时间窗口内异常请求数占总请求数的比例。当比例超过阈值时熔断。这个策略比慢调用更直接适合依赖直接抛错而不是变慢的场景。异常数统计单位时间窗口内异常请求总数达到阈值就熔断。注意它统计的是“总数”而不是比例在低流量场景下更容易触发所以配置时要结合 QPS 估算合理的异常数阈值。这里有个重要的状态机概念熔断器有三个状态CLOSED关闭、OPEN打开、HALF_OPEN半开。熔断触发后进入OPEN状态经过timeWindow指定的时间后进入HALF_OPEN放行少量探测请求如果这些请求成功则恢复CLOSED否则再次打开。我实际使用中的体会是慢调用比例策略最能反映真实问题因为它直接就“慢”这个现象进行降级不管慢是依赖本身的问题还是网络抖动造成的。异常比例则适合那些“快速失败但错误率很高”的服务。异常数策略要谨慎流量小的时候几个零星异常就会触发熔断可能造成误伤。5.2 系统自适应保护如果说流控和熔断是“点”级别的保护那系统规则就是“面”级别的保护。它不看单个资源而是看整个系统的 CPU、Load、RT、线程数、QPS 等指标一旦超标就限制整体入口流量。系统规则有五个阈值维度highestSystemLoad系统 Load 阈值。Load 是 CPU 排队长度的近似值Load 过高说明系统已经处理不过来了。avgRt所有入口流量的平均 RT 阈值。maxThread入口流量的并发线程数阈值。qps所有入口流量的 QPS 阈值。highestCpuUsageCPU 使用率阈值。使用系统保护规则要注意它的实现原理Sentinel会自动统计当前系统每分钟的负载情况并根据阈值计算出一个“允许通过的最大 QPS”然后作用到所有入口流量上。所以系统规则适合作为最后一道防线不建议单独依赖它做业务限流。我遇到过的问题是在容器环境下Linux的 load 统计跟宿主机混在一起比如你部署在 K8s 的 Pod 里看到的 load 可能是宿主机整机负载而不是当前容器的负载。这时候如果用 load 做系统保护阈值很容易误触发。容器环境下我更推荐用highestCpuUsage或者在接入层做流量限制而不是完全依赖系统规则。5.3 热点参数限流热点参数限流是对“某一个参数值”进行限流。典型的场景是商品详情接口整体 QPS 并不高但双十一时某个爆款商品的 QPS 特别高其他商品正常。如果你只配一个总 QPS 限流要么限不住爆款要么误伤其他商品。热点参数限流就能精确地只对那个商品ID做限制。举一个配置示例资源getProductDetail参数索引0是商品ID设置该参数的 QPS 阈值为 100。这时候无论整体 QPS 多高只要单个商品 ID 的 QPS 超过 100就会被拦截。还可以配置参数例外项比如对某个核心爆款商品ID单独设置更高的阈值或者给某些内部测试ID设置更低的阈值。热点参数限流在实现上跟普通流控有区别它需要单独引入sentinel-parameter-flow-control扩展包并且在代码里用SphU.entry(getProductDetail, EntryType.IN, 1, productId)传入参数。不传参数规则是无法匹配到的。6. 集成Spring Cloud Gateway6.1 网关接入Sentinel的两种方式Spring Cloud Gateway 是目前微服务网关的主流选择。先把话说明白网关本身就是一个天然适合限流的入口层所有外部请求都要经过它所以在这里做流控能保护后端所有服务。集成Sentinel有两种方式。第一种是使用spring-cloud-alibaba-sentinel-gateway提供的适配器它已经内置了对 Spring Cloud Gateway 路由的适配可以针对routeId和自定义 API 分组做流控。这种方式配置简单适合大多数场景。第二种是自己在 Gateway 的全局过滤器里调用SentinelAPI 做自定义限流。这种灵活度最高但工作量也大一般用在对现有网关有大量自定义逻辑的场景。我建议先从第一种入手。依赖引入dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-starter-alibaba-sentinel/artifactId /dependency dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-alibaba-sentinel-gateway/artifactId /dependency6.2 网关流控规则的配置引入适配器之后你可以在Sentinel控制台上看到网关类型的资源route资源比如order-service和自定义API分组资源。给路由配置流控规则和给普通资源配置类似但要注意route资源名必须与网关配置文件里的路由ID保持一致。举个例子网关配置了spring: cloud: gateway: routes: - id: order-service uri: lb://order-service predicates: - Path/order/**控制台上新增流控规则时资源名就写order-service针对这条路由下所有请求做 QPS 限制。如果你想针对某个具体路径组合限流比如/order/create和/order/query分开控制可以用自定义 API 分组。在控制台“网关流控规则”里创建 API 分组配置匹配模式为Path匹配值填/order/create然后针对这个分组配置规则。这里有个细节网关适配器默认会把Route资源作为入口但如果你还需要在网关层面对原始请求参数做热点限流那就要自己包一层SphU.entry。官方网关适配器不支持从请求参数里提取热点维度需要自己写 GlobalFilter这也是很多人集成网关后觉得“怎么热点限流没效果”的原因。6.3 网关接入的常见坑网关接入Sentinel最常见的坑有三个。第一个控制台看不到网关服务。大部分原因跟普通服务一样是心跳注册失败。但网关服务比较特殊因为它可能部署在 Kubernetes 集群里从Pod里发心跳到控制台时如果控制台不在同一网络需要配置spring.cloud.sentinel.transport.dashboard为控制台的可达地址而且要注意跨网络时8719端口是否对外开放。第二个规则不生效。这个大概率是资源名对不上。看清楚你配置的资源名到底是路由ID还是 API 分组名不要混用。另外网关路由默认是async场景Sentinel的入口类型要使用EntryType.IN如果自定义适配器里设置错误流控也不会触发。第三个Sentinel和Gateway的fallback配置冲突。网关默认的限流返回是 429但如果你自定义了全局异常处理返回格式可能不符合前端约定。建议统一在网关的BlockRequestHandler里处理限流响应返回统一的 JSON 结构避免前端拿到一堆乱七八糟的错误体。7. 常见问题与排查技巧实录7.1 控制台看不到服务这个问题的排查路径我建议按顺序来确认客户端pom.xml里有没有引入sentinel-transport-simple-http或 Spring Cloud Alibaba 的 transport 依赖。确认spring.cloud.sentinel.transport.dashboard配置的是控制台地址客户端能 ping 通。确认spring.cloud.sentinel.eager已设置成true。或者手动访问一次业务接口让资源被触发。查看客户端日志里有没有类似[Sentinel] register to dashboard success的提示。如果没看到说明心跳注册失败。检查控制台所在机器的防火墙8719端口要能收到客户端的心跳报文。另外要注意Sentinel控制台只展示“最近一段时间内活跃”的服务如果服务长时间没有流量机器列表里可能会看不到这是正常的。7.2 规则不生效规则不生效的原因通常集中在几个点上。先确认资源名是否一致。控制台配置的资源名必须跟代码里的SentinelResource的value或SphU.entry(xxx)里的名字一致。很多人配置的时候随手写了个英文名代码里又是另一个自然不生效。再确认规则加载方式。如果是通过控制台下发的规则注意客户端有没有成功拉取。如果客户端当前处于“游离状态”连不上控制台规则就只存在控制台内存里客户端根本不知道。还有一类隐蔽问题同一个资源在多个地方被配置了规则新旧规则互相覆盖。尤其是在有配置中心持久化的情况下控制台手工配置和配置中心推送的规则可能同时存在后者往往会把前者顶掉。排查时可以先调出当前资源实际生效的规则ListFlowRule rules FlowRuleManager.getRules();把rules打出来看看到底是什么规则在起作用。7.3 性能影响与线程模型很多人在选型时会担心Sentinel对性能的影响。我实测下来的感受是在普通 QPS 不是特别离谱的情况下Sentinel的额外开销基本可以忽略因为它默认统计是基于LongAdder和滑窗计数不是加锁那种笨重的方案。但它并不是完全没有代价每个被Sentinel保护的资源都会占用一定内存用于维护计数器和时间窗口。如果规则非常多或者每个请求都要经过多次entry开销会线性增长。控制台推送规则时如果频繁变更也会引起客户端内部规则的重新构建。所以我通常建议只在核心链路和入口资源上加Sentinel保护不要每个方法都套一遍。网格内部被大量调用的中间层方法如果也加性能损耗就会比较明显。另一个容易踩坑的点是Sentinel的线程模型。默认情况下被流控拦截的请求是直接抛异常返回而不是像Hystrix那样换个线程池执行。所以如果你在业务代码里写try-catch把BlockException吞了限流就等于白配。正确的做法是在blockHandler里处理被拦截的逻辑或者显式捕获BlockException并返回对应的降级结果。7.4 控制台访问链接与版本兼容最后聊一下访问链接和版本匹配的问题。Sentinel控制台访问地址是http://ip:8080这个大家都知道。但如果你用的是某个项目内部的定制版控制台端口和路径都可能不一样一定以自己部署的为准。版本兼容是我吃过亏的地方。Sentinel1.8.6 的客户端接入 1.8.1 的控制台基本功能没问题但有些新特性比如热点参数规则、网关规则可能不显示或报错。反过来老客户端接入新控制台也可能出现规则推送失败。我的习惯是控制台和客户端保持同一个大版本最好都是同一个精确版本号。如果你用的是 Spring Cloud Alibaba它会通过 BOM 统一管理 Sentine 版本不要手动去覆盖除非你知道自己在干什么。跟“Sentinel”这个名字还有一桩容易混淆的事网上搜索时会出现Sentinel LDK License Manager缺失、haspusersetup.exe下载之类的词。这里要提醒大家一下那是SafeNet的软件授权加密工具跟阿里巴巴的流量防护组件完全是两个东西。如果你是在装加密狗驱动别走错片场。最后的实操体会我自己从最早只是“为了用而用”地装个控制台、配几条规则到现在可以比较熟练地设计整套降级方案中间走了不少弯路。最深的体会是Sentinel不是一个配置完就能放心的组件它需要你持续观察线上指标不断调整阈值和规则组合。流控阈值不是拍脑袋定的一定要结合压测数据、容量预估、业务容忍度来定。熔断的时间窗口也不是越短越好太短会导致下游还没恢复就反复被打进熔断态太长又会让恢复太慢。另外规则持久化一定不能省。哪怕你现在只是个小项目也应该从一开始就接配置中心否则等你真的上了生产再想补这层改动成本会翻好几倍。最后分享一个我一直保留的小习惯每次新服务接入Sentinel后我会写一段简单的自测代码用SphU.entry手动触发一次受保护资源然后故意把流控阈值调到 1验证一下拦截逻辑和降级返回是否符合预期。这个动作看起来不起眼但能帮你在一开始就发现资源名配错、blockHandler没生效、异常被吞掉这类极其隐蔽的问题。等线上出事了再去验证代价就太大了。
返回列表