SkyWalking 在国内 Java 技术圈里基本属于 APM 领域的标配了,但很多人搭好环境、接入探针之后,面对控制台上那一堆图表和数字,经常是一脸懵:这个 CPM 是什么意思?SLA 为什么是百分比?那个 P99 又是怎么算出来的?JVM 那一栏的 GC 时间到底看哪个值?
这篇文章不说搭建,也不讲源码,纯粹讲指标本身。我把自己在实际项目中总结的 SkyWalking 指标体系整理成一份能直接对照查阅的说明,看完你至少能做到:打开 UI 就知道每张图在说什么,配置告警的时候知道该拿哪个指标出来用。
1. 指标体系的整体结构:先搞懂 SkyWalking 是怎么组织数据的
1.1 三个层级的核心抽象:服务、实例、端点
在用 SkyWalking 的时候,你遇到的几乎所有指标都归属于三个层级:服务(Service)、服务实例(Service Instance)、端点(Endpoint)。
- 服务:就是你对外提供的一个业务系统,比如"订单服务""用户服务"。在接入探针时,通过
service_name配置项指定,通常对应一个微服务模块。 - 服务实例:同一个服务部署了多个副本,每个副本就是一个实例。实例的标识是
实例IP@进程号之类的组合。这里要特别注意,实例是随时可能变化的,扩容缩容、发布重启都会导致实例列表变动。 - 端点:一个服务对外暴露的具体接口路径,比如
/api/order/create。在 HTTP 场景下通常就是 URL 的匹配规则,在 gRPC 场景下是方法名。
为什么要先理清这三层?因为SkyWalking 的存储模型和查询逻辑都是围绕这三层展开的。你在仪表盘上看到的一张图,本质上就是选了某个层级、某个时间范围、某种聚合方式之后,从底层存储里查出来的一组时序数据。搞清楚层级关系,后面看指标就不会乱。
1.2 指标数据的收集链路与聚合逻辑
探针(Agent)在应用进程内采集数据,通过 gRPC/HTTP 上报到 OAP Server(Observability Analysis Platform),OAP 做聚合计算后写入存储(默认 H2,生产环境一般用 Elasticsearch 或 MySQL)。UI 再从 Storage 查询并渲染。
这个链路里最容易忽略的一点是:OAP 默认按分钟级做指标聚合。也就是说,你在图上看到的"每分钟请求数",不是实时精确值,而是这一分钟内所有实例上报数据聚合后的结果。聚合的维度包括时间桶(分钟)、服务、实例、端点等。
注意:如果应用实例特别多,或者接口调用量特别大,OAP 的聚合压力会成为瓶颈。我在实战中遇到过一个服务峰值 QPS 过万、实例数超 50 个的项目,OAP 默认的
core.default线程池配置明显不够用,后来调大了 JVM 堆内存和聚合线程数才稳住。
1.3 为什么了解指标结构比记住某个数值更重要
很多人问"SkyWalking 里哪个指标最重要",其实这是一个伪问题。不同角色关心的指标完全不同——开发关注端点延迟和错误堆栈,运维关注实例的 CPU、GC 和内存,架构师关注服务间的调用拓扑和依赖关系。
所以与其死记硬背某个指标,不如先理解 SkyWalking 的指标命名规范和数据组织方式。指标名称通常由三部分组成:层级前缀(service/instance/endpoint)+ 指标类型(cpm/sla/percentile)+ 具体修饰符。掌握了这个规律,即使遇到没见过的指标,你也能猜个八九不离十。
2. 核心业务指标详解:CPM、响应时间、SLA 与吞吐量
2.1 CPM(每分钟请求数)——最直观的流量指标
CPM 全称 Calls Per Minute,表示每分钟的调用次数。在 SkyWalking UI 中,你看到的大部分"流量"、"吞吐"图表,用的就是 CPM 这个指标。
实际操作时要注意三点:
- CPM 是按分钟聚合的均值,不是瞬时 QPS。如果你需要看秒级流量,SkyWalking 默认并不直接提供,需要结合其他监控系统(如 Prometheus)来看。
- 在端点维度看 CPM,可以快速判断某个接口是否有异常流量突增。我曾经通过端点 CPM 曲线发现某个下游回调接口在凌晨 3 点准时飙高,排查后发现是某个定时任务在批量补推数据,并非故障。
- CPM 是一个"绝对值"指标,做告警时要结合历史基线来配置阈值,否则很容易误报。比如一个新上线的接口,刚开始 CPM 只有个位数,后来业务推广涨到几百,如果你还按原来的阈值告警,那天天都要被打扰。
2.2 响应时间:平均响应时间与 P50/P95/P99
响应时间指标在 SkyWalking 里分两类:
第一类,平均响应时间(Avg Latency):所有调用耗时的算术平均值。这个指标有个明显缺陷——容易被极端值拉高。比如接口 99% 的请求都在 50ms 内返回,但只要 1% 的请求超时到 5 秒,平均响应时间就会变得很难看。所以平均响应时间适合看整体趋势,不适合做精细分析。
第二类,百分位响应时间(P50/P75/P90/P95/P99):这是 SkyWalking 里最有价值的一组指标。P99 的含义是"99% 的请求耗时都小于这个值"。它比平均值更能反映真实体验——尤其是面向终端用户的接口,P99 才是用户能感知到的延迟。
我个人的经验是:日常监控看 P95,故障定位看 P99,优化对比看 P50。
- P50 反映典型用户的实际体验;
- P95 反映大多数用户在最差情况下的体验;
- P99 用来发现极端慢请求,往往是内存 GC、锁竞争、外部依赖抖动的信号。
举个例子:某个下单接口平均响应时间显示 200ms,看着还行,但 P99 已经到了 3.5 秒。点进慢查询的 Trace 一看,发现是下游支付回调在高峰期超时重试导致的——如果不看 P99,这个问题在平均值图表上几乎不可见。
2.3 SLA/Success Rate:成功率指标的两种计算口径
SkyWalking 中的 SLA 指标(也叫 Success Rate)表示请求成功率,计算口径是:成功请求数 / 总请求数 × 100%。如果一个请求的响应状态码为 5xx,或者抛出了未捕获的异常,就会被记为失败请求。
但这里的坑在于:"失败"的定义是可配置的。SkyWalking 探针会依据 HTTP 状态码来判断请求成功与否,默认情况下 400 和 500 都算错。不过在实际业务中,有些接口在业务逻辑里会主动返回错误码(比如"库存不足"返回 200 + 业务码 10001),这类请求在 SkyWalking 眼里是"成功"的,但业务上其实是失败的。所以 SLA 指标只能反映"技术层面"的成功率,不能完全等同于业务成功率。
还有一种常见误区:直接拿 SLA 做告警阈值。SLA 低于 99.9% 就告警,这个配置对流量较小的系统不太友好——一个接口一天只有 100 次调用,只要失败 1 次,SLA 就掉到 99%,触发告警,但实际影响几乎为零。正确做法是给 SLA 告警加上最小请求量门槛,比如"CPM 大于 10 且 SLA 低于 99.9%"才触发。
2.4 Apdex 指标:用户满意度评分
Apdex(Application Performance Index)是一个国际上通用的应用性能满意度指标,SkyWalking 也内置了它。它的算法很简单:
- 满意(Satisfied):响应时间 ≤ T 毫秒;
- 可容忍(Tolerating):T < 响应时间 ≤ 4T;
- 失望(Frustrated):响应时间 > 4T。
Apdex 分数 = (满意请求数 + 可容忍请求数 × 0.5) / 总请求数,取值范围 0~1。
T 值一般取 200ms 或 500ms,但要针对不同业务设置不同 T 值。像查询类的接口,200ms 以内是合理的;但涉及大量计算或文件操作的接口,T 值设在 500ms 甚至 1000ms 也不过分。SkyWalking 的 Apdex 指标默认按服务维度计算,如果你想按端点维度配置不同的 T 值,需要修改 OAP 的配置文件(application.yml中的apdex-threshold部分)。
我实际使用下来,Apdex 更适合做趋势观察,比如每周对比 Apdex 变化,看系统整体体验是否在恶化。单独拿它做告警,误报率也不低。
3. JVM 指标与基础设施指标:把"应用状态"和"运行环境"结合起来看
3.1 JVM GC 指标:最常见的排查抓手
SkyWalking 对 Java 应用会自动采集 JVM 指标,包括 GC 次数、GC 耗时、堆内存使用等。其中我在排查问题时会重点关注GC Young Gen Count(新生代 GC 次数)和GC Old Gen Count(老年代 GC 次数),以及对应的GC 耗时。
几个实战判断思路:
- Young GC 频繁但耗时短:说明应用在频繁创建短生命周期对象,可能有循环创建对象的代码,或者数据库操作没有走批处理。这种情况通常不会立刻出问题,但会持续消耗 CPU。
- Old GC 频繁且耗时长:说明老年代在持续增长,大概率是内存泄漏或者缓存设置过大。这时候去配合看堆内存使用曲线,如果曲线是"阶梯式上升且不回落",十有八九是泄漏。
- Full GC(CMS/G1 的 Mixed GC)耗时飙升:先看是否是堆内存过小导致的,再看是否有大对象直接进入老年代。我在一个报表服务上遇到过 GC 耗时从 50ms 飙升到 3 秒的情况,排查下来是因为有个接口把一个月的数据一次性加载进内存做聚合,直接把老年代塞爆了。
小技巧:在 SkyWalking 的仪表盘里,把 GC 耗时曲线和响应时间曲线叠加到同一时间轴对比,你会发现慢请求经常出现在 GC 耗时的峰值之后。这个关联观察法能帮你快速确认"延迟高是因为 GC"还是"GC 只是结果"。
3.2 JVM 内存指标:堆内存与非堆内存的解读
SkyWalking 的 JVM 内存指标分为 Heap(堆)和非堆两部分。堆内存里又分Used(已使用)和Committed(已提交)。Committed 是 JVM 已经申请到的内存大小,Used 是实际使用的大小。
看堆内存时我习惯看曲线形态,不看绝对值:
- 锯齿状波动:正常现象,说明对象创建和回收处于动态平衡;
- 持续增长,峰值不断抬高:内存泄漏的典型特征。配合 GC 指标里的老年代增长,基本可以实锤;
- Committed 和 Used 长期接近:说明堆内存压力大,GC 会很频繁,考虑调整
-Xmx或优化对象的生命周期。
非堆内存(Non-Heap)里包含 Metaspace(元空间),在 Java 8+ 里存的是类元数据。如果应用动态生成大量类(比如 CGLIB 代理、反射频繁),Metaspace 会一直涨。SkyWalking 里能看到JVM Non-Heap Committed和JVM Non-Heap Used。我在实际项目中遇到过不停发布新版本、Metaspace 空间没回收导致 OOM 的案例,最后靠分析这个指标定位到是自定义 ClassLoader 没办法卸载旧的类。
3.3 线程指标与服务实例 CPU 指标
SkyWalking 采集的线程指标相对基础:线程总数、活跃线程数。这个指标不像 JVM 内存和 GC 那么高频使用,但在排查"线程池耗尽"类问题时有奇效。
举个真实的排查经历:某个服务的错误率突然升高,接口报错信息是RejectedExecutionException。打开 SkyWalking 实例维度的线程指标,发现活跃线程数长期贴着线程池上限,再看对应的 GC 指标,Old Gen 也一直在高位。结论是:GC 阻塞了业务线程的执行,导致请求在队列里积压,线程池被打满后直接拒绝新请求。这个案例里,线程指标 + GC 指标 + 端点延迟三个维度互相印证,定位过程非常快。
服务实例的 CPU 指标在 SkyWalking 中默认不采集。你需要在探针配置中开启相应选项(不同语言探针支持程度不同)。如果你已经有一套 Prometheus + Grafana 的基础设施监控,CPU、内存这类基础设施指标其实没必要在 SkyWalking 里重复看,把职责划分清楚:SkyWalking 看应用和业务链路,Prometheus 看系统和容器。
3.4 数据库指标与外部依赖指标
SkyWalking 还能采集数据库连接池(HikariCP、Druid 等)的指标,包括活跃连接数、空闲连接数、等待获取连接的时间等。这些指标对判断"数据库连接池是否成为瓶颈"非常直接。我排查过一个偶发性的接口超时问题,所有响应时间指标看起来都正常,唯独连接池的等待时间曲线出现了规律性的尖峰,顺藤摸瓜发现是某个批处理任务在整点抢占全部连接,导致业务高峰期连接不足。
对于外部依赖,SkyWalking 会通过 Trace 数据自动生成依赖服务的指标(调用量、延迟、成功率)。你在"拓扑图"上看到的每条连线背后,就是这些指标在支撑。这些指标对排查下游服务依赖问题是关键——毕竟很多系统故障不是自己出问题,而是被下游拖垮的。
4. 告警规则与指标联动:怎么用指标配置不误报、不漏报的告警
4.1 告警规则的组成结构
SkyWalking 的告警规则写在alarm-settings.yml里,核心元素包括:规则名称、指标名称、阈值、周期、条件、静默窗口、通知钩子。下面是一个典型配置示例(注意指标名称的格式):
rules: # 端点平均响应时间超过 1000ms 且持续 3 分钟时告警 - rule-key: endpoint-avg-response-time metric-name: endpoint_avg_latency threshold: 1000 op: ">" period: 3 count: 3 message: "Endpoint response time exceeds 1000ms"这段配置的含义是:在连续 3 个周期(分钟)内,如果端点平均响应时间都大于 1000ms,就触发告警。period和count的组合是减少误报的关键——如果只配置period: 1, count: 1,任何瞬时抖动都会告警,生产环境根本吃不消。
4.2 常用告警指标与推荐阈值
根据我的实战经验,以下几组指标配置告警的性价比最高:
| 指标名称 | 告警场景 | 建议阈值 | 备注 |
|---|---|---|---|
endpoint_cpm | 流量异常下降(服务挂了一半实例) | 低于历史基线的 50%,持续 5 分钟 | 需要自己维护基线,SkyWalking 没有内置动态基线 |
endpoint_sla | 成功率下降 | 小于 99.9%,持续 3 分钟 | 建议加 CPM 不低于 N 的前置条件,避免低流量误报 |
endpoint_percentile | P99 延迟超标 | 大于 1500ms,持续 5 分钟 | 比平均响应时间更适合做延迟告警 |
service_cpm | 整体流量异常 | 突增/突降超过 100%,持续 5 分钟 | 突增常见于被攻击或流量重放,突降常见于服务不可用 |
jvm_gc_time | GC 耗时过长 | 大于 1000ms,持续 3 次 | 配合内存指标一起看,避免 GC 告警成为噪音 |
jvm_old_memory_used | 老年代持续增长 | 大于堆最大值的 85%,持续 10 分钟 | 内存泄漏的早期信号 |
实践下来需要特别提醒一点:SLA 告警的坑最多。SkyWalking 的endpoint_sla是一个百分比数值(比如 99.9),但在告警配置里的threshold类型需要写成整数还是小数,取决于 OAP 的版本。我在某个版本上升级后,原来的 SLA 告警从"正常告警"变成"永不触发",查了很久才发现是阈值类型问题。遇到这类诡异情况,优先去 OAP 日志里看告警引擎的评估记录,能省很多时间。
4.3 组合告警:减少无效告警的有效手段
单一指标告警容易误报,组合条件会更可靠。SkyWalking 的告警规则目前支持一条规则里配置多个条件(不同版本支持程度略有差异),比如:
- 同一端点:
endpoint_sla < 99%且endpoint_cpm > 50,持续 3 分钟,才算故障; - 服务级别:
service_sla < 99.99%且service_cpm 在最近 30 分钟内的下降幅度 > 50%,才算重大故障。
这种组合能有效过滤掉低流量接口偶发失败、单实例抖动等不必要的告警。我接手过一个项目,接手时告警一天能发 200 多条,值班同学基本都屏蔽了。后来挨个规则调整,加上各种组合条件,把告警量降到一天 5 条以内,真正做到了"每条告警都值得看"。
5. 指标排查实战:从图表异常到问题定位的完整路径
5.1 场景:接口响应时间突然变慢,如何利用指标快速定位
这里分享一个我在生产环境实际处理的案例。
某天接到业务反馈,说订单查询接口变慢了。我打开 SkyWalking 的操作路径是:
第一步,看服务级指标。订单服务的 CPM 没有明显变化,SLA 仍然 100%,但平均响应时间从 100ms 涨到了 800ms,P99 更是从 300ms 飙到了 2 秒。这说明整体流量没变,但单个请求处理时间长了,问题大概率在应用内部或下游。
第二步,看端点级指标。进入端点列表,发现变慢的不止/order/query一个接口,订单服务下的所有端点都变慢了。这个信息很关键——如果是某个接口的代码逻辑问题,应该只有对应端点恶化;所有端点一起变慢,说明是公共链路出了问题。
第三步,看实例维度。发现订单服务有三个实例,其中两个实例的响应时间正常,只有一个实例的 P99 非常高。锁定到单个实例后,再看 JVM 指标,发现这个实例的 GC 次数和耗时是其他实例的 5 倍,老年代内存也明显偏高。
分析结论:该实例可能发生了内存泄漏,或者被分配了不均衡的流量。再看部署记录,确认这个实例是上一次发布后新加入的节点,由于宿主机内存配置错误,导致堆内存比其他实例小,GC 压力大,响应时间自然就上去了。
这个案例里,我用的全部是 SkyWalking 自带指标,没有写一行额外代码。加粗强调一下排查路径的关键逻辑:从服务 → 端点 → 实例 → JVM,逐层下钻,每一步都在排除干扰项,这是 SkyWalking 指标使用的核心方法论。
5.2 场景:拓扑图中出现不认识的依赖,怎么辨别
SkyWalking 的拓扑图会展示服务之间的调用关系。有时候你会看到某个服务调用了一个你并不认识的"外部服务"——这通常是因为探针采集到了对 MQ、Redis 或者外部 HTTP API 的调用。
这时候不要急着把它当成故障。正确的做法是:先看这个依赖的 CPM 和响应时间。如果 CPM 很低(比如每分钟几次)且延迟正常,基本可以判断是探针识别出了某个间接依赖,比如通过 HTTP Client 调用了外部接口。如果 CPM 很高且持续增长,则需要追查代码里是否有未收敛的循环调用,或者是探针本身配置导致的重复追踪。
我在一个项目中就发现过离谱的情况:某个服务的拓扑图上出现了 30 多个"外部依赖",排查发现是代码里封装了一个 HttpUtil 工具类,每次请求都会自动携带 Trace Header,导致外部系统也把自己的调用链路传了回来。这个现象就是Trace 上下文传播过度的典型表现,需要通过配置探针的过滤规则来收敛。
5.3 场景:UI 图表没有数据,先别慌着重启
这个问题几乎每个人都会遇到:探针配置好了,应用也重启了,但 UI 上就是没有数据。我的排查顺序是:
- 探针日志:先看探针是否正常连接 OAP,日志里有没有
gRPC connection refused之类的报错,这是最基础的排查点。 - OAP 日志:查看 OAP Server 是否接收到数据,如果 OAP 日志里有 decode 失败、索引异常等错误,就要看是协议版本不匹配还是存储出问题。
- 时间对齐:检查 OAP 和应用服务器的时间是否一致。时间偏差超过几分钟,时序数据就会出现"查询不到"的情况,这个问题最容易忽略。
- 存储索引:如果用的是 Elasticsearch,确认索引模板是否创建成功,索引是否存在且可查询。ES 集群磁盘满了或者索引被误删都会导致 UI 无数据。
我记得有个项目的 OAP 一直报IndexNotFoundException,原因是 ES 的索引命名规则升级后,旧索引没有自动迁移。这些经验说明:SkyWalking 的指标展示是一个完整的链路,从探针采集到 OAP 聚合再到存储查询,任何一个环节出问题,UI 都可能是空白。掌握排查顺序,比盲目重启高效得多。
6. 关于指标落地实践的几点个人体会
用了几年 SkyWalking 下来,我最大的感触是:指标体系的建设不是"开箱即用"就能完成的,它需要结合你的业务特点持续调优。很多团队把 SkyWalking 部署好之后就不再管了,顶多偶尔看一眼拓扑图,这是很可惜的——SkyWalking 真正价值在于日常的指标观察、对比和告警,而不是当应急排查工具。
从操作层面,我有几个小建议:
- 建立指标基线:在系统稳定运行一段时间后,记录各核心接口的 CPM、响应时间、SLA 基线值,后续做告警阈值配置有据可依;
- 定期审视告警规则:每季度清理一次低效告警规则,检查那些"从来没有触发过"的规则是否还有存在必要,那些"天天触发"的规则是否要调整阈值;
- 把 SkyWalking 指标接入企业监控大屏:通过 SkyWalking 的 GraphQL API 或者将指标同步到 Prometheus(OAP 支持暴露 Prometheus 格式指标),让团队日常能看到系统健康状态。
SkyWalking 的指标体系说复杂也复杂,说简单也简单。抓住服务、实例、端点三个层级,理解 CPM、响应时间、SLA、百分位这几个核心指标,再配合 JVM 和告警配置,基本就能覆盖日常绝大部分的监控需求。希望这份基于实操经验的指标说明,能让你下一次打开 SkyWalking 面板时更从容一些。