用 SkyWalking 排除健康检查和静态资源前,我先替你把账算清楚了
先说个真实场景。Spring Boot 微服务接入 SkyWalking 之后,第一天打开 Trace 查询页面,屏幕前坐着的我整个人是蒙的:满屏的 GET:/actuator/health、GET:/actuator/health/liveness,夹杂着 /static/js/app-xxxx.js、/webjars/bootstrap.js 这类请求,真正要找的下单链路、支付回调,被淹没在几十页数据后面。这不是我一个人的问题,团队里另一个后端搭的网关用 Kong 3.4 做上游健康检查,每 10 秒对每个上游实例探测一次,20 个实例,一天下来健康检查的链路数量比真实业务请求还多。
这篇文章就是来解决这个问题的:怎么让 SkyWalking 忽略特定接口,重点排除健康检查和静态资源这两类“高噪声、低价值”的请求,保持追踪数据的干净,同时不损失对真实链路的可观测性。适合已经在用 SkyWalking、或者正准备规划探针接入的 Java 后端、SRE、平台工程同学。
1. 大量无意义链路从哪来:健康检查与静态资源对追踪数据的污染
1.1 健康检查不是“偶尔一次”,而是“持续高频”
很多人对健康检查有个错觉:觉得它不过是个 /health 接口,一天也就访问几次。实际放到生产环境里,健康检查的访问频率远超你的想象。
以我们线上一个 20 实例的服务为例,健康检查的来源至少有四个:
- Kubernetes 的
readinessProbe和livenessProbe,默认每 10 秒探测一次,20 个实例意味着每秒约 4 次请求。 - Kong 3.4 网关的上游健康检查,如果配置了 active check,每隔几秒就会对每个 upstream target 发起一次 HTTP 探测。
- Spring Boot Actuator 自带的
/actuator/health,很多内部监控平台会定时轮询。 - 运维脚本、负载均衡器的 HTTP 监测,同样会频繁调用。
这些链路本身响应快、逻辑简单,但它们有一个共同点:会完整走完请求进入、Servlet 处理、组件调用采集、Segment 上报这一整套流程。SkyWalking 的 Java Agent 并不会因为请求简单就跳过埋点,每一条健康检查都会被包装成一个 TraceSegment,传到 OAP 后落库。
带来的后果是两方面的。第一,存储浪费。一条健康检查链路即便只有 3-5 个 Span,每天积累下来也是千万级别,磁盘和索引开销都不小。第二,数据失真。你打开拓扑图,会看到服务之间有一条很粗的调用线,点进去发现全是从健康检查发起的 FeignClient 调用,真实业务调用反而被掩盖。链路分析、慢请求排行这类功能,也会被这些“永远成功、永远很快”的假数据带偏。
仔细观察你会发现,这类噪声请求有一个共同特点:它们不是用户发起的业务操作,而是系统自我保护机制产生的。对可观测性系统来说,它们是被动记录的数据,业务价值几乎为零。
1.2 静态资源请求:把端点列表变成“文件列表”
静态资源又是另一类情况。只要你的服务里有 Web 页面,不管是用 Spring Boot 直接托管静态资源,还是前后端分离后由 Nginx 反代到后端,都存在大量 CSS、JavaScript、图片、字体请求。
一个典型的订单管理后台页面,一次整页刷新可能带出 20 到 50 个静态资源请求。前端框架打包后的 JS 文件还会带上 hash,比如 app.e4f8a2b3.js,每次发版文件名都变。在 SkyWalking 的端点列表里,它们会生成大量新的 endpoint,导致 endpoint 基数蹭蹭往上涨。
我看过一个比较极端的上线案例:某服务接入 SkyWalking 后跑了一周,单日 Segment 总量里 65% 来自静态资源。诊断的时候翻 Trace,前端页面的各种 js、css、图片请求把真正报错的接口全顶到后面去了,定位一个 500 错误反而要多翻好多页。
这两种请求不处理,SkyWalking 用起来会非常“脏”。下面这部分我们就来说清楚,排除它们有哪些手段,各自的边界在哪里。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 排除机制梳理:探针端配置、采样降噪与后端端点过滤的取舍
排查问题之前,得先搞清楚手上有什么工具。SkyWalking 从探针采集到后端展示,每一层都有干预手段,但效果和代价完全不同。
2.1 探针端的 ignore_path:从源头掐断
agent.trace.ignore_path 是 Java Agent 提供的一个配置项,作用是在探针端直接跳过对指定路径的追踪。路径命中后,这个请求不会生成 TraceSegment,也不会上报到 OAP,是整个链路里最彻底的排除方式。
它的好处是显而易见的:
- 节省探针到 OAP 的网络开销。
- 减少 OAP 的存储和索引压力。
- 让端点和拓扑数据从源头变得干净。
代价也很明确:配置是进程级的,修改后需要重启应用才能生效(后面我会专门展开验证部分)。另外,它只对进入 Java Agent 采集范围的 HTTP 请求生效,如果前端请求在网关层就被返回了,根本没到后端服务,那探针层自然看不到。
2.2 采样率只能降低概率,不能精准排除
agent.sample_n_per_3_secs 是采样配置,它的作用是控制每 3 秒最多保存多少个 Segment。默认情况下这个值有自动采样策略,但很多团队的用法是手工设置成一个固定值,比如 sample_n_per_3_secs: 1,表示每 3 秒只保留 1 个链路。
采样和忽略是两个维度的东西。采样是按“概率”把多余数据丢掉,它不会区分路径,健康检查和真正的下单请求都会被一视同仁地丢弃。如果你用采样率来压健康检查的噪声,真实业务链路也会受影响。
这里有一个常见误区:有人觉得把采样率调低,健康检查数据自然就少了,就不用做路径忽略。这种思路的问题在于,采样是无差别的。你希望的是“保留 100% 的真实业务请求”,但低采样率会让真正的异常请求也有概率被丢弃。后面第 6 部分我会讲如何组合使用。
2.3 UI 与 OAP 端的端点过滤:展示层的补救
在 SkyWalking UI 的端点视图里,可以按名称搜索、过滤,也可以自己在界面上隐藏某些 endpoint。OAP 后端也有部分配置能对端点做聚合调整。
但这些操作都没有真正解决存储问题。被隐藏的端点数据仍然在数据库里,查询的时候仍然会产生开销。它适合做展示层的临时调整,不适合作为长期方案。
三者对比如下:
| 方式 | 生效层 | 是否节省存储 | 精准度 | 修改代价 |
|---|---|---|---|---|
| 探针端 ignore_path | 探针采集前 | 是 | 按路径精准排除 | 需重启应用 |
| 采样率调整 | 探针采集后 | 是 | 按概率无差别丢 | 可动态调整 |
| UI 端点过滤 | 展示层 | 否 | 仅影响展示 | 即时生效 |
结论很直接:要彻底解决健康检查和静态资源污染,主要精力应该放在探针端的 ignore_path 配置上。
3. 用 trace.ignore_path 排除健康检查:完整配置
3.1 修改 agent/config/agent.yaml
先找到 SkyWalking Java Agent 的配置文件,一般在 agent/config/agent.yaml,核心段如下:
yaml复制agent:
# 每 3 秒最多采集的 segment 数量,默认 -1 表示按 CPU 情况自动采样
sample_n_per_3_secs: -1
trace:
# 需要忽略的请求路径,多个规则之间用英文逗号分隔
ignore_path: ${SW_TRACE_IGNORE_PATH:/actuator/health*,/actuator/health/**,/health*,/readyz,/livez}
这里我配了几类比较典型的健康检查路径。以 Spring Boot Actuator 为例,新版本 health 相关的端点有:
/actuator/health/actuator/health/liveness/actuator/health/readiness
也有不少项目习惯自定义健康检查路径,比如 /health、/ping、/readyz、/livez。这些路径在不同的团队里差异很大,建议你结合自己服务的实际配置来写,而不是照抄模板。
3.2 路径匹配规则:*、**、? 与逗号列表
ignore_path 使用的是 Ant 风格路径匹配,规则本身不复杂,但有几个细节值得注意:
| 匹配符 | 含义 | 示例 |
|---|---|---|
* |
匹配一个路径片段内的零个或多个字符 | /health* 可匹配 /health、/healthz |
** |
匹配任意层级的路径 | /actuator/** 可匹配 /actuator/health、/actuator/health/liveness |
? |
匹配一个字符 | /ready? 可匹配 /readyz 和 /readyx |
多个规则之间用逗号分隔,整体作为一个配置项。有一点需要注意:* 不会跨路径段匹配。例如 /actuator/* 只能匹配 /actuator/health,但无法匹配 /actuator/health/liveness。如果想把 actuator 下的所有子路径都排除掉,要写成 /actuator/**。
基于这个规则,健康检查比较稳的一种写法是:
yaml复制ignore_path: ${SW_TRACE_IGNORE_PATH:/actuator/**,/health*,/readyz,/livez,/ping,/status}
写 /actuator/** 而不是只写 /actuator/health*,是因为健康检查相关的端点可能还有 /actuator/info、/actuator/metrics 等,这些同样属于运维类访问,链路追踪价值也比较低。
3.3 通过环境变量与启动参数注入
配置写死在 agent.yaml 里其实不太利于多环境管理。很多时候测试环境和生产环境的健康检查路径不一样,如果每次改路径都要改文件,很麻烦。
更推荐的方式是用环境变量来覆盖。上面配置里已经写了 ${SW_TRACE_IGNORE_PATH:...},它的意思是:优先读环境变量 SW_TRACE_IGNORE_PATH,如果没设置,则使用后面的默认值。
在 Kubernetes 的 Deployment 里可以这样配:
yaml复制env:
- name: SW_TRACE_IGNORE_PATH
value: "/actuator/**,/health*,/readyz,/livez,/ping"
在启动命令里也可以直接用 JVM 参数覆盖:
bash复制java -javaagent:/path/to/skywalking-agent.jar \
-Dskywalking.trace.ignore_path="/actuator/**" \
-jar my-service.jar
个人经验是:尽量把这类规则放到环境变量里管理,而不是散落在各个服务的启动脚本中。后面我会再讲怎么统一维护。
3.4 配置完成后如何验证
这个部分特别重要。很多同学改完配置,重启服务,然后就去 UI 上找数据,发现还是能看到健康检查请求,以为配置没生效。实际上有几种可能性。
先明确一个前提:ignore_path 修改后需要重启 Java 进程才能生效。Agent 的很多配置在启动时加载,不像采样率那样可以远程动态调整。
验证的正确顺序应该是这样:
- 在 SkyWalking UI 的“追踪”页面,按时间范围查询目标服务的
GET:/actuator/health端点。如果重启前有大量记录,重启后新时间窗口不应该再出现新数据。 - 检查当前时间段的 Segment 总量,看有没有明显下降。健康检查占比高的服务,排除后 Segment 数通常会降一半以上。
- 打开拓扑图,观察调用关系是否发生变化。如果之前健康检查把服务间的实际调用关系掩盖了,这里会变得更清晰。
如果重启后仍然能看到新数据,重点检查三类情况:路径写错、规则没被加载、请求根本没经过 Java Agent。前两种情况可以通过 agent 的日志来确认,启动时 SkyWalking Agent 会打印加载的关键配置。第三种情况常见于请求在 Spring Cloud Gateway 或者 Nginx 层就被处理掉了,压根没进入后端服务,这时候要在入口层面单独处理。
4. 静态资源请求的排除:从路径规则到验证
4.1 先搞清楚 Spring Boot 静态资源挂在哪些路径下
配置静态资源过滤之前,需要先弄明白一个最基础的问题:你的静态资源实际上是通过什么路径访问的。
Spring Boot 的默认约定是把静态资源放在 classpath:/static/、classpath:/public/、classpath:/resources/、classpath:/META-INF/resources/ 这几个目录下,对应的 URL 路径分别是 /static/**、/public/**、/resources/** 等。也可以通过配置自定义:
yaml复制spring:
web:
resources:
static-locations: classpath:/static/,classpath:/public/,file:/data/static/
有一点值得提醒:Spring Boot 对静态资源的处理是由 ResourceHttpRequestHandler 完成的,这个处理器和普通的 Controller 请求在链路追踪里表现不太一样,但只要是 HTTP 请求,探针都能采集到。所以过滤规则的核心还是放在路径匹配上。
除了应用自身托管的静态资源,前后端分离后很多项目会把静态资源放到 Nginx 或 CDN。如果你的页面请求 /assets/index.js,而 Nginx 直接返回本地文件,那个请求根本不会出现在 Java 服务的追踪数据里,那这部分就不用管。需要关注的是那些经过 Spring Boot 处理的静态资源请求。
一个简单判断方法:在 SkyWalking 端点列表里,搜一下 /static、/assets、/webjars 这些关键字,看哪些 endpoint 存在大量采样,那些就是要处理的。
4.2 常见静态资源过滤规则模板
静态资源的路径规则相比健康检查要宽泛一些,因为资源文件类型多、目录结构多样。以下是一个比较通用的配置模板:
yaml复制ignore_path: ${SW_TRACE_IGNORE_PATH:/actuator/**,/health*,/readyz,/livez,/ping,/static/**,/public/**,/resources/**,/webjars/**,/images/**,/css/**,/js/**,/fonts/**,/favicon.ico}
几个细节:
/static/**能覆盖 Spring Boot 默认静态资源目录下的所有文件。/webjars/**是很多项目引入 WebJars 依赖后常用的路径,像 bootstrap、jquery 的 jar 包装资源都会挂在这里。/favicon.ico是浏览器自动发起的请求,虽然量不大,但很脏,建议一并排除。/css/**、/js/**、/images/**、/fonts/**这类规则要谨慎使用,因为有些项目会把后端图片上传接口放在/images/upload路径下,如果直接一个**全排除,可能就把业务接口也排掉了。
4.3 真实部署中路径与规则不一致的三种情形
配置了静态资源过滤之后,最容易出问题的是路径和规则对不上。分享三个我实际踩过的情形。
第一种是网关重写路径。服务端实际收到的路径可能已经变了。比如 Nginx 配置了 location /assets/ { proxy_pass http://backend/static/; },前端请求的是 /assets/app.js,后端看到的是 /static/app.js。这时候后端过滤规则要按 /static/** 配,而不是按前端路径配。
第二种是资源带 hash 文件名。Vue、React 打包后的文件通常叫 app.4f8a2b3.js 之类,但路径前缀是不变的,所以 /js/** 或 /static/** 这类规则依然能覆盖到,不用把 hash 也写进规则。
第三种是前后端同域部署。部分老项目直接用 Spring Boot 托管前端构建产物,静态资源和接口都在同一个域名下,路径可能很杂,除了 /static/**,还有 /dist/**、/vendor/**。这种情况先收集一段时间的 endpoint 列表,再把高频的静态资源路径统一加到规则里。
配置完成后,验证方式与健康检查一致。UI 端点列表里确认相关 endpoint 不再增长,Trace 查询页里搜一下静态资源路径,新时间窗口内应该没有新数据。
5. 漏网之鱼排查:路径编码、大小写与尾部斜杠带来的规则失效
配置规则写完了,也重启了,UI 上看大部分静态资源和健康检查都消失了,但偶尔会发现几条漏网之鱼。这说明路径匹配并没有想象中的那么“铁板一块”,下面这些边界情况值得注意。
5.1 带编码的 URL 为何匹配不上
HTTP 协议允许请求行里携带编码后的字符。比如普通请求 /actuator/health,有人可能发成 /actuator/%68ealth,编码后的 %68 解码后是字母 h。还有更复杂的路径写法,例如把路径片段写成包含编码点部分的串。当这类请求到达 Java Agent 时,探针记录的路径到底是什么,取决于容器和过滤器链对 HttpServletRequest.getRequestURI() 的具体处理。
如果框架在进入 Controller 之前已经做了路径规范化,探针看到的是解码后的路径 /actuator/health,那规则可以正常匹配。如果出于某些原因路径保持原始编码状态,Agent 拿到的可能是 /actuator/%68ealth 这种形态,基于字面路径的 ignore_path 规则自然就失效了。
对于探针配置来说,我们不需要放大这个问题。正常业务场景下,浏览器和 SDK 发起的请求都是规范路径,编码路径出现的频率很低。但要意识到:如果某一天发现某条路径明明在忽略列表里,UI 里却还是有零星的 Trace,可以考虑是不是有人或某个组件经过了 URL 编码重写。这种情况的处理方式不是把规则写成 %68 这种形式,而是建议在网关层做路径规范化统一。
5.2 大小写与尾部斜杠的规则失效场景
第二个容易漏掉的是大小写。默认情况下,Ant 风格路径匹配是大小写敏感的。如果你的健康检查端点配置过大小写混合路径,比如某次手工改成了 /actuator/Health,而规则写的是 /actuator/health*,那就会漏掉。
实践中更常见的是尾部斜杠。/actuator/health 和 /actuator/health/ 在 Servlet 容器里可能会被当作不同路径,也可能被容器自动重定向或归一化,不同容器行为不一致。如果规则写的是 /actuator/health,结果请求实际以 /actuator/health/ 结尾,命中情况就不好说了。
比较省心的做法是规则里直接写 ** 或者同时把带不带斜杠的情况都覆盖到。比如:
yaml复制ignore_path: /actuator/health,/actuator/health/**,/actuator/health/
虽然看起来有点冗余,但在多容器混部的环境里更稳。
5.3 借助 UI 查询对漏网接口做体检
排查漏网之鱼不能靠肉眼翻 Trace,效率太低。我常用的方法是在 SkyWalking UI 的 Trace 查询页里做“体检式”搜索。
第一步:把时间范围设成最近 10 分钟或 1 小时,按服务名过滤,在端点关键字里输入 health,把匹配到的端点全部列出来,看有没有规则没覆盖到的。
第二步:再搜 js、css、png、woff、map 这类静态资源后缀,把所有静态资源端点拉出来。
第三步:把查出来的端点路径和 ignore_path 规则逐条对一遍,把遗漏的路径补进去,重新发布验证。
这一步建议在服务稳定运行一段时间后做一次。因为规则不可能一次性覆盖完整,运行一段时间后,新的静态资源路径、新的健康检查端点类型都会冒出来。每次发版后花 10 分钟看一下端点列表,比攒几个月再处理要轻松得多。
6. 排除与采样的组合设计:既要“忽略”又要“不漏”
6.1 很容易忽略掉的“健康检查告警”
把健康检查和静态资源全部排除掉之后,会有一个隐藏风险:如果某一天健康检查真的开始报错了,比如数据库连接池满了,/actuator/health 返回 503,你还能在 SkyWalking 里看到这个问题吗?
答案是:如果所有健康检查路径都被忽略了,SkyWalking 的 Trace 数据里不会出现任何相关线索。这时候就必须依靠另外一套监控手段来兜底,比如 Prometheus 的 probe_success 指标、Kong 网关层面的健康检查日志、以及应用自身的告警规则。
我的建议是:健康检查路径可以忽略,但一定要确保它们的安全状态在其他链路里可见。一个比较折中的方案是,不把健康检查全部忽略,而是采取采样方式,或者只忽略其中一部分冗余来源(比如 K8s 探针的请求),保留另一部分(比如监控平台轮询的请求),让健康检查在追踪数据里留下少量样本,既能保持数据干净,又保留了一点诊断线索。
6.2 把采样率和 ignore_path 组合使用
这里提供一种更精细的组合策略。
对于健康检查类请求,使用 ignore_path 精准排除,从源头减少数据量。对于静态资源请求,同样用 ignore_path 排除,但如果担心某些静态资源路径是新出现的,可以同时把采样率设置到一个适中水平,作为兜底,防止新路径未被规则覆盖时数据无限制增长。
举个例子:
yaml复制agent:
sample_n_per_3_secs: 3
trace:
ignore_path: ${SW_TRACE_IGNORE_PATH:/actuator/**,/health*,/readyz,/livez,/static/**,/public/**,/webjars/**,/css/**,/js/**,/images/**,/fonts/**,/favicon.ico}
每 3 秒保留 3 条链路作为抽样。如果配置失误导致大量静态资源请求没被忽略,它们最多每 3 秒产生 3 条链路,不会形成数据风暴。这种有兜底的配置方式,比单纯依赖 ignore_path 更稳。
6.3 维护一份路径清单,而不是口头约定
做到这一步,整理一份路径清单会很有价值。这份清单不需要很复杂,建议维护成 Markdown 表格,放在项目的运维文档里,内容包括:服务名、路径、忽略规则、生效状态、备注说明。
| 服务 | 忽略路径 | 原因 | 备注 |
|---|---|---|---|
| order-service | /actuator/** |
健康检查 | K8s 探针使用 |
| order-service | /static/**,/webjars/** |
静态资源 | 前端页面 |
| admin-web | /js/**,/css/**,/images/**,/fonts/** |
静态资源 | 带 hash 文件名 |
这样做的原因很简单:ignore_path 配置在实际项目里往往是逐步迭代的,今天加一个路径,明天改一个路径,如果不把变更过程和原因记录下来,三个月后没人能说清楚当时为什么排除 /ping。这份清单既是对当前配置的审计,也是未来排查问题的依据。
我个人在实际项目里是把清单和配置仓库放在一起维护的,每次修改都走 MR 评审,改完以后顺手更新表格。坚持半年之后,这个动作帮团队省了不少排查时间。
另外还有一个值得养成的习惯:每次接入新服务时,把健康检查和静态资源的忽略规则作为初始化模板直接带上,而不是等服务跑进生产环境、数据污染后再补。前者只需要在创建服务时多花两分钟,后者却要回头翻一整天的 Trace 才能定位问题。两分钟和一天,这笔账怎么算都划算。
最后再分享一个小技巧。如果你的服务很多,每个服务都单独维护一份 ignore_path 环境变量会显得很散。可以在 CI/CD 流水线里维护一个全局的默认规则文件,服务接入时自动注入。遇到特殊路径,再通过服务级配置覆盖。把共性规则下沉到平台层,把个性路径留在服务层,这套玩法对团队协作和后期治理都比较友好。
