1. 这个需求是怎么冒出来的:健康检查与静态资源正在污染你的链路数据
先聊一个我碰到过很多次的场景。
接入SkyWalking之后,打开拓扑图和链路查询页面,发现请求量最大的不是业务接口,而是GET /actuator/health、GET /favicon.ico、GET /js/app.js这一串。在Kubernetes环境里更夸张,Pod的readinessProbe和livenessProbe默认几秒就探一次,多个副本叠加下来,健康检查的trace数量直接占据整个链路的30%以上。加上前端静态资源在未做单独域名拆分时也会走应用容器,SkyWalking上报的数据量很快就上去了。
这时候最尴尬的是,当你想排查一个真实的慢接口时,搜索链路列表,前几页全是health check的记录。虽然SkyWalking提供了端点维度筛选,但在“所有端点”视角下噪音量还是太大了。更现实的问题是,上报数据量直接关联存储成本和ES查询性能,大量无效trace会拖慢查询响应。
所以“忽略特定接口”这个需求,本质上不是为了节省那一点点探针开销,而是解决三个问题:
- 数据清洗:让链路数据聚焦到业务接口,避免健康检查掩盖真实异常
- 存储成本:无效trace持续写入ES,索引膨胀之后查询性能和磁盘占用都会出问题
- 排障效率:保留有效链路样本,避免搜索全链路时被无效请求刷屏
这个需求在Spring Boot应用里最常见,因为Spring Boot Actuator默认暴露health、info端点,而且每年都有团队在没有任何防护的情况下把静态资源也放进了监控范围。但在非Spring生态里,比如Kong网关、Go服务、Node.js应用,一样会碰到这个问题——只要有心跳检测、健康探测、资源请求这类高频低价值流量,你就需要一套统一的忽略策略。
下面的内容主要围绕SkyWalking Java Agent来展开,同时会补充一些通用处理思路,适配Kong、Spring Cloud Gateway等场景。你不需要看完整个SkyWalking源码,跟着实操就能把“忽略特定接口”这件事做干净。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 清楚要过滤什么,才能真正把规则写对
很多人的第一反应是:直接用trace.ignore_path把所有健康检查的URL写进去,搞定了。但这个思路如果只做到这一步,后面大概率会踩坑。要先想清楚两个问题:忽略哪些请求是合理的,以及忽略请求和忽略端点有什么区别。
先给一个通用的判断标准:一个接口如果满足“高频调用、低信息量、无业务语义”,就值得被排除。高频意味着它会把trace数据量拉起来;低信息量意味着即使出问题,你也无法通过这些链路定位业务故障;无业务语义意味着它不代表用户的实际操作路径。
最常见的几类:
- 健康检查类:/health、/healthz、/actuator/health、/ping、/ready、/live,Kubernetes探针里面配置的那些路径,路径还可能是自定义的
- 静态资源类:/static/、/js/、/css/、/images/、/favicon.ico、/.js、/.css、/.png、/.jpg
- 系统信息类:/info、/metrics、/actuator/prometheus(如果是抓取指标用的,也应该忽略)
- 定时任务/后台批处理:这类不一定全部忽略,但要考虑是否需要采样而不是全量上报
关于“忽略请求”和“忽略端点”的区别,这里要特别说明。SkyWalking的链路追踪是以Segment(一段分布式链路在单个服务内的执行片段)为基本单位的,一个HTTP请求从进入到响应结束,会形成一个Segment。默认情况下,SkyWalking的采样器是100%采样——每个Segment都会被采集并上报,除非你配置了采样率。
trace.ignore_path这个配置项的作用是:当Agent收到一个HTTP请求时,在创建Segment之前先检查请求路径是否匹配ignore规则。如果匹配,则整条Segment都不会被创建,也就不会产生任何上报数据。也就是说,它做的是“源头忽略”,不是“上报时过滤”——这一点很关键,因为如果你试图用后置过滤的方式做这件事(比如写个脚本定期清理ES里的无效trace),成本反而会更高。
端点(Endpoint)是SkyWalking在Segment之上抽象出的概念,一个HTTP请求的URI对应一个端点。如果你只是某些端点不想展示在拓扑和端点列表里,又不想完全不收集trace,那就不应该用trace.ignore_path,而是考虑端点检测规则或者在上报端做别名/删除处理。
理清这层之后,我们再来看具体的配置方法。
3. 配置文件里的关键参数:trace.ignore_path完整拆解
我先说结论:Java Agent场景下,忽略特定接口最核心的配置项就是trace.ignore_path。
这个配置项在agent/config/agent.config文件里,对应的属性名是:
code复制trace.ignore_path=${SW_TRACE_IGNORE_PATH:}
默认值是空的,也就是不忽略任何接口。配置它要遵循一套匹配规则,我当时第一次配置的时候也被绕了一下,把规则单独整理出来:
- 路径匹配支持PathMatcher模式,格式上必须以/开头
- 如果路径末尾带号,表示匹配该路径前缀,例如/health会匹配/health、/health/、/health/detail,但不匹配/actuator/health
- 如果不带*号,则是精确匹配,例如/health只匹配/health这一个路径
- 多个路径之间用英文逗号分隔
- 匹配时使用的是请求的URI路径部分,不包含query string和host
举个实际例子。假设我有一个订单服务,健康检查路径是/actuator/health,静态资源放在/static目录下,还有接口/ping专门给网关做心跳检测,那配置就是:
code复制trace.ignore_path=${SW_TRACE_IGNORE_PATH:/actuator/health,/static/*,/ping}
但是要注意一个细节:/actuator/health这个写法只匹配GET /actuator/health,如果你的框架还会自动加个斜杠变成/actuator/health/,那就匹配不上了。我建议在配置的时候,凡是有可能带尾斜杠的路径,要么精确配置两份(一个带斜杠一个不带),要么用前缀匹配。精确匹配是一种写法:
code复制trace.ignore_path=${SW_TRACE_IGNORE_PATH:/actuator/health,/actuator/health/,/static/*,/ping}
或者更省事一点,如果你确认不会有其他/actuator下的端点需要追踪,可以直接写:
code复制trace.ignore_path=${SW_TRACE_IGNORE_PATH:/actuator/*,/static/*,/ping}
/actuator/*会匹配/actuator/health、/actuator/info、/actuator/metrics、/actuator/prometheus。如果你的应用把Actuator端点全部暴露了,这个做法在配置上更简洁。但前提是你想把这些全部排除,如果还有某个Actuator端点是你需要追踪的,就得精确配置。
再强调一个踩过的坑:HTTP方法不会被匹配。SkyWalking的trace.ignore_path只匹配路径,不区分GET还是POST。也就是说,如果有一个业务接口恰好是POST /ping,你配置了/ping,那么POST /ping也会被忽略。所以写配置时要检查一下是不是有同名路径的业务接口。更精确的做法是通过后续要讲的插件扩展来判断方法,但大多数场景下路径匹配已经够了。
配置文件的生效方式也要说明一下:修改agent.config之后,需要重启应用进程才能生效。如果你想改配置而不重启JVM,可以借助SkyWalking的动态配置中心功能,通过Apollo、Nacos或者ZooKeeper来动态下发trace.ignore_path,但这需要额外开启配置中心插件,而且复杂度会上来。生产环境一步到位的方法还是把配置放在镜像或环境变量里,用SW_TRACE_IGNORE_PATH这个环境变量名直接注入,这样K8s部署时改yaml就行,不需要每次重新build镜像。
4. 不只是Java Agent:Kong、Spring Cloud Gateway等场景的忽略思路
前面主要围绕Java Agent展开,但实际项目里网关层往往是健康检查和静态资源流量的第一站。我尝试过在Kong 3.4上做健康检查时被SkyWalking跟踪的情况,这里把非Java场景怎么处理一并说明。
Kong作为API网关,在Kubernetes里通常以Deployment方式部署,每个节点上的健康检查会定时访问/status或自定义的健康检查路径。如果SkyWalking Agent是挂在Kong容器里的(Kong的官方镜像支持lua-resty-skywalking或通过外部Agent接入),那么在网关层忽略特定路径同样可以配置。
Kong场景下有一个额外的复杂点:健康检查可能是由Kong自身的health check模块发起,也可能是由K8s probe发起。如果是前者,请求不会通过你定义的Route,而是直接打到Kong的Admin API或Status API,这时候需要在SkyWalking的Lua插件或Agent侧做路径过滤。如果是后者,请求会经历正常的Route匹配,走的是任意一个服务对应的上游,那就可以在Agent通用层面处理。
Spring Cloud Gateway这类基于WebFlux的网关,本质还是Java应用,因此trace.ignore_path依然有效。但要注意网关场景下的路径是包含上下文路径的,比如你的网关前缀是/api/v1,健康检查配置是/actuator/health,实际匹配时你看到的是网关转发的目标路径还是网关接收时的原始路径,取决于插件对URI的读取时机。我的经验是:想让规则稳定可靠,配置时尽量把网关接收的原始路径和转发路径都考虑进去。如果使用了StripPrefix之类的Filter,原始路径和转发路径不同,可以先让Agent上报一天数据,在SkyWalking UI里查看端点列表,把所有需要过滤的路径都摊开看一遍,再一次性配置,这样最稳妥。
Node.js、Go、Python这些语言的Agent也有相应的trace.ignore_path或ignore配置,原理大同小异,都是在上报前根据请求路径做过滤。具体写法可以查阅对应语言的SkyWalking官方文档。核心思路是一致的:路径匹配规则、支持通配符、在创建Segment前拦截。
5. 用Agent插件做更精细的控制:Optional Plugins和自定义扩展
如果trace.ignore_path满足不了你的场景,比如你需要按HTTP方法忽略、按请求头判断、按Cookie判断,那就需要往Agent插件层面走一步了。
SkyWalking Java Agent的插件机制提供了多种扩展点。最常用的是“Optional Plugins”,在agent/plugins目录之外还有一个agent/optional-plugins目录,里面有一些默认不生效的插件,需要手动拷贝到plugins目录才会启用。其中和路径过滤比较相关的是apm-spring-cloud-gateway-xx-plugin这类网关插件,它们可以动态适配网关的路径处理逻辑,但要说真正对“忽略特定接口”有直接帮助的,其实是下面这个思路。
真正灵活的方式是用SkyWalking的“自定义追踪忽略”机制,或者直接基于ByteBuddy写一个简单插件。但这对大多数人来说太重了,折中方案是:用采样率和端点合并来配合trace.ignore_path。举个例子,如果有一个接口/order/list调用量非常大,你不能完全忽略它,因为它是核心业务链路,但你又不想每条都全量上报,那可以在agent.config里调整采样率。SkyWalking默认是100%采样,你可以改成:
code复制agent.sample_n_per_3_secs=${SW_AGENT_SAMPLE_N_PER_3_SECS:1}
这个参数表示每3秒只采样一条trace,适合超大规模调用且不需要全量追踪的场景。注意这个参数是全局限流,不区分接口,所以只有在所有接口都能接受降采样时才适合。JVM系统属性方式也可以:
code复制-Dskywalking.agent.sample_n_per_3_secs=1
实际操作中,一个很常见的组合是:健康检查、静态资源用trace.ignore_path全部忽略;核心业务接口保持全量采样;一些高QPS但低价值接口通过采样率控制总量。这样数据质量和存储成本能达到一个平衡。
再讲一下自定义扩展。如果公司内部有统一网关,所有服务的健康检查都由网关注入特定header头,那么你可以在Agent的HTTP Filter层做判断:请求携带了X-Health-Check: true,就跳过Segment创建。这个可以通过SkyWalking的增强插件机制实现,或者简单一点,在顶层Filter直接返回“忽略”的逻辑。但这个方案需要懂一点Agent插件开发和字节码增强知识,非Java背景的同学可以先跳过。
其实还有一种更偏门但非常有效的方式:在业务代码层面提前短路。既然SkyWalking是基于Servlet或WebFlux拦截器的,那你可以在业务框架里先对请求做一次路由判断,如果是健康检查或静态资源请求,直接返回一个标记,让SkyWalking的忽略机制自动触发。但本质上这绕了一圈,配置层面能解决的事不建议在业务代码里做。
6. 从“忽略路径”升级到“优化端点”:让数据更干净的两个进阶招数
配置trace.ignore_path之后,你会发现在SkyWalking UI的“端点”列表里那些过滤掉的路径确实消失了。但很多时候你会发现另一个问题:动态路径把端点列表撑得很大。比如有一个接口/order/{id},实际请求是/order/123、/order/456,在SkyWalking里会被识别成两个不同端点,看起来数据很杂乱。
SkyWalking默认不做端点归一化时,它就是按照实际URI路径来统计端点的。此时有两种优化方式。
第一种:使用Endpoint Name规则进行合并。SkyWalking Java Agent的HTTP插件提供了endpoint-name-forming机制,你可以通过配置把带参数的动态路径归一化成同一个端点名。例如把/order/123和/order/456都归一到/order/{id}。具体做法是在agent.conf里增加相关插件配置,或者在agent.config中配置:
code复制plugin.microservice.http.endpoint-name-forming=${SW_ENDPOINT_NAME_FORMING:true}
那要看版本支持情况。不同版本的插件支持的配置不一样,8.x版本里Servlet插件对端点的处理已经比较完善,你可以通过在SkyWalking UI里查看端点列表,确认哪些Endpoint是动态路径,然后通过插件的URI聚合规则统一。
第二种:在网关层做路径规范化。如果服务前面有Gateway,把/order/{id}这类路径在进入应用之前转换为/order/{id}的格式。这个不一定是好方案,因为会丢掉真实路径参数,但确实能减少端点数量。
顺带说一句:忽略路径并不是唯一的数据清洗方式。如果你的目的是“在导出或展示时排除某些端点”,而不是“不在源头采集”,那SkyWalking在后端也提供了一些处理能力,比如在OAP Server侧配置endpoint过滤规则。但这需要改OAP的配置,而且属于链路数据后置处理,适合你有数据已经误采、不想重新发版的情况。对于大部分团队,我仍然建议前端Agent侧解决,因为最省资源、最彻底。
7. 实战:一次完整配置的从0到1过程
我以一个模拟的Spring Boot应用作为演示,应用名order-service,端口8080,使用SkyWalking Java Agent 8.9.1版本。
第一步,确认Agent已经正确接入。启动命令大概是:
bash复制java -javaagent:/opt/skywalking-agent/skywalking-agent.jar \
-Dskywalking.agent.service_name=order-service \
-Dskywalking.collector.backend_service=127.0.0.1:11800 \
-jar order-service.jar
先不加任何忽略配置跑10分钟,然后在SkyWalking UI的“端点”列表里观察,你会发现类似/actuator/health、/static/js/chunk.js、/favicon.ico等路径已经出现了。记录下来。
第二步,修改agent/config/agent.config,找到如下行:
code复制# Exclude the data of a specified path
trace.ignore_path=${SW_TRACE_IGNORE_PATH:}
改成:
code复制trace.ignore_path=${SW_TRACE_IGNORE_PATH:/actuator/health,/actuator/health/,/actuator/info,/static/*,/favicon.ico,/ping}
注意我故意把/actuator/health和/actuator/health/都写了,就是为了避免尾斜杠问题。
第三步,重启应用。重启之后故意请求一次/actuator/health,再到SkyWalking UI的“链路追踪”页面搜索这段请求路径,你会发现找不到对应的trace。如果还能找到,大概率是配置没生效,或者路径没匹配上。这时可以直接查看Agent的日志,在logs/skywalking-api.log里能看到加载的配置信息,确认ignore_path已经生效。
第四步,如果是Kubernetes环境,建议直接用环境变量注入,而不改agent.config。yaml里这样写:
yaml复制env:
- name: SW_AGENT_SERVICE_NAME
value: "order-service"
- name: SW_AGENT_COLLECTOR_BACKEND_SERVICES
value: "skywalking-oap:11800"
- name: SW_TRACE_IGNORE_PATH
value: "/actuator/health,/actuator/health/,/actuator/info,/static/*,/favicon.ico,/ping"
这样每次调整忽略规则都不需要重新build镜像,只需更新Deployment的环境变量,然后滚动重启。我推荐生产环境一律用环境变量管理agent配置,而不是把agent.config打到镜像里。原因很简单:镜像一旦构建好了,改配置就要重新走CI流程,时间成本完全没必要。
第五步,观察半天到一天数据,确认端点和拓扑图干净了,再去看存储。ES里skywalking的segment索引增长速率会明显下降。如果你之前已经跑了很久且数据膨胀严重,可能还需要在OAP侧手动做一次索引清理或者数据保留窗口调整。
8. 常见问题与排查技巧实录
我在配置trace.ignore_path的过程中遇到过不少奇怪的问题,有些问题会直接让人觉得“是不是SkyWalking不支持忽略”,其实多半是细节没处理好。这里把常见问题整理成速查表,都是我亲身踩过的,价值比文档高不少。
首个高频问题是:配置文件改了并重启,但请求还在被追踪。这种时候先确认你改的是不是Agent实际读取的配置路径。如果你用-Dskywalking.agent.service_name指定了服务名,但没有指定配置目录,Agent会默认读取agent.jar所在目录的config/agent.config。如果你在IDE本地调试时用了相对路径,或者在容器里打了多个Agent副本,很可能读到的是旧的配置文件。用日志确认最靠谱:启动时skywalking-api.log会显示config loaded from xxx。
第二个高频问题:精确路径匹配没问题,但加了就不生效。trace.ignore_path的通配符和Spring MVC的AntPathMatcher不一样,它不是完全的任意匹配。比如你写/static/**,在老版本里未必生效,官方要求的是/static/这样单层通配,或者/static这样前缀匹配。所以要小心通配符层级,不确定时先写简单前缀,比如/static,但它只精确匹配/static这个路径,不会匹配/static/a.js,所以正确写法是/static/和/static//*。
第三个高频问题:请求路径有URL编码或特殊字符。比如一些网关会把请求路径encode成/health%20check,或者带分号;/jsessionid,这会导致匹配失败。这种情况下要么后端统一处理URL解码,要么你把常见变形都写进忽略列表里,避免在别的地方浪费太多时间。还有一个更隐蔽的坑:假如你的Spring Boot应用context-path配置了/api前缀,那么健康检查实际路径可能是/api/actuator/health而不是/actuator/health。所以配置之前一定要看你应用真实的端点列表,不要凭经验猜。
第四个高频问题:基于HTTP方法的过滤需求。前面说了trace.ignore_path不支持方法维度。如果你有一个路径既是健康检查又承担业务回调,那你只能换插件方案,或者把业务路径改掉。更实际的做法是在网关层面对健康检查请求加独立前缀,比如统一改成/internal/health,这样忽略规则就很容易覆盖了。
第五个问题:忽略之后找不到任何数据,怀疑是不是把业务接口也误伤了。这种情况先排查自己写的路径是不是过于宽泛,比如写成了/*,那就等于把整个服务所有请求都忽略了。我在一个环境里看到有人配置了/,连根路径都忽略了,导致整个服务的trace全部消失,拓扑图直接空掉。配置完成后应该做一轮冒烟验证,对比几个核心业务接口的链路还在不在。
再补充一个看法:不要过分自信一次性配置到位。第一次配置忽略规则后,我会等到第二天再复查一次端点列表,看看有没有漏掉的健康检查路径、或者新版本的框架自动加了新的端点。这种复查频率不需要太高,但每次发版涉及框架升级、网关路由调整时,顺手看一眼端点列表,能避免无效trace悄悄回流。
9. 一点收尾心得
处理完这个问题之后,我最大的体会是:SkyWalking的配置本身不复杂,难的是想清楚自己到底要什么数据。如果你把健康检查和静态资源全部忽略,你确实得到了干净的链路,但同时也意味着一旦健康检查本身变慢,你是看不到那次慢请求的——而健康检查变慢往往是系统出问题的前兆。所以我的建议是:健康检查可以做“降频”而不是“全忽略”,比如保留慢请求的trace,或者通过采样率控制,只采极小比例作为体检样本。静态资源则可以全忽略,因为静态资源几乎没有业务价值。这样既控制了数据量,又保留了足够的可观测性。
最后再分享一个小技巧:如果团队有多个服务,忽略路径清单最好维护成一份团队级的标准配置,放在统一的Agent配置文件模板或K8s公共环境变量里,而不是每个服务各自为政。否则你会看到同一个/actuator/health,有的服务忽略了,有的没忽略,排查问题时数据口径完全不一致。这个细节看似不起眼,但在多服务团队里能省掉很多沟通成本。
