Sentinel 从 0 到 1 接入生产:范围收敛、Dashboard 运维和 7 天 QPS 基线制定

Sentinel 从 0 到 1 接入生产:范围收敛、Dashboard 运维和 7 天 QPS 基线制定

前言

Sentinel 接入本身的技术门槛不高——加依赖、配置 Dashboard 地址、给关键接口标注资源名,半天就能跑起来。真正决定这次接入是"能落地的治理工具"还是"上线后就没人管的摆设",在于三件接入文档通常不会重点讲的事:监控范围要不要收敛、Dashboard 怎么用在日常运维里、限流阈值怎么定而不是拍脑袋写一个数字。

这篇文章记录的是一次真实的从 0 到 1 接入过程,按时间顺序讲清楚每一步踩到的问题和处理方式。

flowchart LR
    A[接入 SDK] --> B[资源命名与范围收敛]
    B --> C[Dashboard 部署与监控配置]
    C --> D[7 天观察期采集真实 QPS]
    D --> E[基于基线设定限流阈值]
    E --> F[上线观察 + 持续调优]

第一步:接入本身很快,但资源范围要先想清楚

Sentinel 的资源(Resource)概念很灵活——理论上可以给任意一段代码打标,细到每个 Service 方法都能单独限流。但"能做到"不代表"应该这么做"。

遇到的问题:资源数量爆炸

第一版接入,团队里几个人各自给自己负责的接口都加了 @SentinelResource,标注粒度也不统一——有人标注到 Controller 方法,有人标注到 Service 内部的某个私有方法,还有人给每个下游 RPC 调用都单独标了资源名。上线一周后,Dashboard 里的资源列表滚了几百条,而且大部分资源没有配置任何规则,纯粹是"标了但没人管"。

// 问题写法: 粒度不统一, 命名随意
@SentinelResource(value = "getUserInfo")           // 有人标 Controller
@SentinelResource(value = "checkDeviceStatusInner") // 有人标内部私有方法
@SentinelResource(value = "rpc-call-1")             // 有人标 RPC 调用, 命名没有语义

更实际的影响是 Prometheus 抓取 Sentinel 指标时的开销——Sentinel 默认会把每个资源的实时指标暴露给监控系统,资源数量一旦超过某个量级(这次实测在 1000+ 左右开始出现明显影响),Prometheus 抓取的耗时和存储量都会明显上涨,Sentinel 官方对这种情况有一个 __overflow__ 聚合机制,超出上限的资源会被归并到一个统一的 __overflow__ 桶里统计,这本身是一种保护措施,但代价是这些资源各自的监控数据就丢失了细粒度——相当于治理工具本身先变成了需要被治理的对象。

处理方案:收敛到"值得单独治理"的粒度

重新规划资源命名和标注范围,原则是只给真正需要独立限流/熔断决策的地方打资源标,不是所有方法都需要:

flowchart TD
    A[某段代码要不要标 SentinelResource?] --> B{是不是外部入口, 如 Controller/MQ 消费者?}
    B -->|是| C[标注, 这是限流的第一道防线]
    B -->|否| D{是不是调用了不稳定的下游, 如第三方 API/慢查询?}
    D -->|是| E[标注, 这是熔断的关注点]
    D -->|否| F[不标注, 交给上层资源统一管控]

按这个原则重新梳理后,资源数量从几百条收敛到 60 多条,命名也统一成 {业务域}-{动作} 的格式(比如 device-reportdevice-query),Dashboard 里终于能一眼看完所有资源,而不是要翻好几页去找自己关心的那个。

第二步:把 Dashboard 用成日常运维工具,而不是只在出问题时才打开

Sentinel Dashboard 默认的部署方式是单实例、数据存内存——这个默认配置对"测试一下功能"够用,但对生产运维不够用,原因是:

  • Dashboard 重启后,之前配置的规则会丢失(除非配合了持久化数据源,比如 Nacos/ZooKeeper)
  • 单实例部署意味着 Dashboard 本身也是个单点,如果它挂了,虽然不影响已生效的限流规则(规则在客户端内存里持续生效),但会失去实时监控和动态调整规则的能力

处理方案:规则持久化到 Nacos

把规则源从"Dashboard 内存"换成 Nacos,Dashboard 变成一个"编辑器",真正的规则存储和推送交给 Nacos:

@Configuration
public class SentinelNacosConfig {

    @PostConstruct
    public void initFlowRules() {
        ReadableDataSource<String, List<FlowRule>> flowRuleDataSource =
            new NacosDataSource<>(nacosProperties, "SENTINEL_GROUP",
                "device-service-flow-rules",
                source -> JSON.parseArray(source, FlowRule.class));
        FlowRuleManager.register2Property(flowRuleDataSource.getProperty());
    }
}

改完之后,Dashboard 重启不再丢规则,而且规则变更历史可以在 Nacos 的配置历史里追溯——这一点在后续排查"这个限流值是什么时候改的、谁改的"这类问题时很有用,纯内存模式完全没有这个能力。

日常运维怎么用

把 Dashboard 纳入日常巡检的一部分,具体做的是:

  • 每天上班第一件事看一眼实时监控里的通过 QPS/拒绝 QPS 曲线,判断昨晚有没有异常的限流触发
  • 每次大促/活动前,提前把相关资源的阈值临时上调(活动结束后再调回基线值),而不是等触发限流了才手忙脚乱去改

第三步:限流阈值怎么定——7 天基线观察期

这是最容易被跳过、但最关键的一步。很多团队接入 Sentinel 之后,阈值是"随便写一个感觉安全的数字",比如"这个接口应该不会超过 1000 QPS 吧,就设 1000"——这种拍脑袋的数字大概率是错的:设太低会误伤正常流量,设太高等于没设。

做法:先观察,不设限,采集真实数据

接入的前 7 天,先只做监控,不配置任何限流规则(FlowRule 全部留空或设成一个明显不会触发的极高值),纯粹用 Dashboard 和 Prometheus 采集这段时间的真实 QPS 分布:

flowchart LR
    A[Day 1-7: 只监控不限流] --> B[采集真实 QPS 分布]
    B --> C[计算 P95/P99 QPS 及峰值]
    C --> D[结合业务预期设定阈值]
    D --> E[Day 8: 灰度开启限流规则]
    E --> F[观察限流触发是否符合预期, 持续微调]

7 天的窗口是为了覆盖一周内的完整业务周期(工作日/周末的流量模式往往不同,某些定时任务是按周触发的),只观察 1 天容易被单日的异常波动带偏。

采集到的数据大概是这样(某核心查询接口的示例):

指标 数值
日常 P50 QPS 320
日常 P95 QPS 780
日常 P99 QPS 1,150
7 天内峰值 QPS(周一上午高峰) 2,400

阈值怎么定:不是直接照抄峰值

拿到这组数据之后,阈值不是直接设成峰值 2400——那等于允许历史上最坏的情况随时重现,没有给系统留任何缓冲。实际定阈值的逻辑是:

  • 参考压测得到的这个接口在保持 P99 响应时间稳定的前提下能承受的最大 QPS(这次压测结果是 3200),而不是单纯照抄线上观察到的流量
  • 在压测上限和线上峰值之间取一个留有余量的值,这次定的是 2800(压测上限的 87.5%),既留了安全边际,又不会离线上真实峰值太远导致误伤
FlowRule rule = new FlowRule();
rule.setResource("device-query");
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule.setCount(2800);
rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_WARM_UP); // 冷启动模式, 避免刚超过阈值就硬拒绝

WARM_UP 冷启动模式是这次特意选的控制策略——直接硬拒绝(CONTROL_BEHAVIOR_DEFAULT)在真正触发限流的瞬间体验比较差(QPS 刚超过阈值就直接拒绝一部分请求),冷启动模式会在系统刚启动或者刚从低水位恢复时,逐步放开阈值到设定值,避免了流量突增瞬间的"陡坡式"拒绝。

灰度开启:先告警不拦截,再拦截

即使有了基于真实数据算出来的阈值,也没有直接一步到位设成"拦截模式"。中间加了一步——先设成"只告警不拦截"(通过自定义的 RequestOriginParser 加日志埋点,模拟规则触发但不真正调用 SphU.entry() 的拦截逻辑),观察一周确认没有误伤,再切换成真正拦截。

这一步在这次接入里确实拦到了一个问题——有个接口的阈值定低了(当时没考虑到月初有一个批量对账任务会集中调用这个接口),如果直接上线拦截模式,会误伤这个本身合理的批量任务。灰度期发现之后单独给这个来源加了白名单例外规则。

效果

接入 3 个月后的运行情况:

指标 数值
纳入监控的资源数 62 个(收敛后)
配置了限流规则的资源数 24 个(只对真正有风险的资源配规则)
3 个月内真实触发限流的次数 7 次,均对应到真实的异常流量(爬虫/客户端重试风暴等)
误伤(限流规则拦截了正常业务流量)次数 1 次(灰度期发现,上线拦截前已修正)

总结

  • ❌ 给所有方法都标 @SentinelResource 看起来更全面,实际会导致资源爆炸,反而让治理工具本身变成负担
  • ✅ 只对"外部入口"和"不稳定下游调用"两类地方做资源标注,把范围收敛到真正需要治理的粒度
  • ✅ Dashboard 要配合持久化数据源(Nacos/ZooKeeper)才能真正用于日常运维,纯内存模式经不起重启
  • ✅ 限流阈值来自 7 天真实流量基线 + 压测上限,不是拍脑袋写一个"应该够用"的数字,上线前先灰度观察再真正拦截

相关文章

参考资料