Spring Cloud Gateway启用Actuator:路由与过滤器管理端点实战
前两天一个同事在排查网关路由问题规则明明配了请求就是过不去。从日志看到注册中心最后发现是网关本体的 Actuator 管理端点没有打开——我们根本没法实时查看网关当前加载了哪些 route、哪些 filter 在执行只能靠猜。这个场景做 Spring Cloud Gateway 的同学应该都不陌生。这篇文章就把“Gateway 启用 Actuator、暴露路由和过滤器等管理端点”这件事完整梳理一遍。从为什么需要这些端点到怎么引入依赖、怎么配置、怎么安全地暴露再到生产环境里动态刷新路由、查看过滤器链路的实际用法最后附上一些我踩过几次坑之后总结出来的排查技巧。适合谁看正在做 Spring Cloud Gateway 网关开发或运维的人尤其是不满足于“路由能通就行”、想知道网关内部到底发生了什么的人。不用太高的门槛带一点 Spring Boot 基础就能跟着操作。1. 为什么要在网关里打开 Actuator 这扇“管理后门”1.1 从一次故障排查说起看不到内部状态是真难受先说我同事那次问题。他配置了一个新路由指向后端的订单服务。配置发布之后怎么访问都是 404后来发现是 route 压根没被加载进网关。当时我们想确认“网关当前到底有哪些路由”但网关的 /actuator 接口一律 404。没办法只能把配置服务里的源数据拉出来人工对照效率极低。如果当时 Actuator 开着一条命令就能看到答案curl http://gateway-address:9000/actuator/gateway/routes返回的 JSON 里会列出所有已加载的路由定义包括 routeId、匹配的 Path、转发 URI、过滤器列表等。到底有没有加载一下就看清楚了。而且不只是路由连过滤器工厂、全局过滤器的执行顺序都能通过 Actuator 端点查出来。也就是说Actuator 在网关场景下不只是“健康检查”那个小功能而是一扇能看到网关内部结构的窗户。1.2 Actuator 在网关中的定位比普通服务的价值高得多普通业务服务启 Actuator主要是看健康状态、线程池、指标。到了网关这一层Actuator 的定位就特殊了——网关是流量的总入口路由和过滤器都长在网关进程内部出了问题你没法像查普通业务一样快速定位。网关的 Actuator 端点至少能做这几件关键事情查看当前已加载的全部路由定义。查看某个具体路由的前后置过滤器组合情况。查看全局过滤器GlobalFilter的注册与顺序。手动触发路由缓存刷新让配置中心里的最新配置马上生效。结合/actuator/metrics查看网关的请求量、耗时、异常数等运行指标。这些能力对冲在一线的开发、运维来说都是无线索排查时的第一手资料。1.3 什么项目需要它什么项目可以先不开我个人的判断是只要网关在测试环境出现过“路由不生效”“过滤器行为异常”就值得开。如果网关只接一两条固定路由、几乎没有变更频率可以先不开因为开了还需要考虑安全问题。但如果网关是流量的统一入口路由规则会经常调整或者接入了配置中心做动态路由那么 Actuator 基本是刚需——你总得知道网关当前实际状态和配置中心的源数据是否一致。2. 启用前的核心准备与依赖梳理2.1 版本选型别让依赖版本拖后腿Spring Cloud Gateway 的 Actuator 端点行为会随 Spring Boot 和 Spring Cloud 的版本变化而变化。我先给出一套我实测比较稳的组合组件推荐版本关键点JDK17 或 21Spring Boot 3.x 必须 17Spring Boot2.7.x 或 3.2.x / 3.3.x3.x 使用 jakarta 命名空间Spring Cloud2021.0.x / 2022.0.x / 2023.0.x需与 Boot 版本严格匹配如果你是 Spring Boot 2.7 的老项目直接在这个基础上加 spring-boot-starter-actuator 即可。如果你是 Spring Boot 3.x注意端点的包路径从javax迁到了jakarta但 Actuator 的 HTTP 访问路径基本不变。我的经验是升级网关版本时优先看 Spring Cloud 的 Release Train 对应表不要只盯着 Gateway 自身的版本。Gateway 与 Actuator 的集成逻辑在上层框架里版本错位经常导致端点不生效。2.2 引入 Actuator 依赖并检查是否真正生效在 pom.xml 里加入dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-actuator/artifactId /dependency如果你是 Gradleimplementation org.springframework.boot:spring-boot-starter-actuator加完依赖后先别急着配暴露策略直接重启服务访问一个最普通的端点curl http://localhost:9000/actuator/health如果能返回{status:UP}说明 Actuator 已经注册到 Spring MVC 的处理器链路上。如果这里都 404那问题多半在依赖冲突或启动未生效上。这里有个关键点光加依赖还不够Spring Cloud Gateway 特有的 gateway 端点组还需要单独开启与暴露。普通服务的 Actuator 默认只暴露 health 和 info 端点网关特有的路由管理端点必须你显式地在配置里把它拉出来。2.3 通过配置暴露 gateway 端点组别一股脑全开Spring Boot 的 Actuator 默认暴露策略非常保守默认只有 health 和 info。如果想看到路由相关端点核心配置是management: endpoints: web: exposure: include: gateway,health,info而且为了让 gateway 分组完全可用还需要management: endpoint: gateway: enabled: true注意这个include: gateway它代表的是整个 gateway 端点组包含 routes、routefilters、globalfilters、refresh 等子端点。有些旧资料会让你逐个 include比如routes,routefilters,globalfilters在新版本里直接用gateway一个词就能把整个组暴露出来简单很多。还要提醒一点千万别图省事写include: *。在网关这种核心入口上把所有端点都裸露出来非常危险后面我会专门讲安全问题。2.4 配置独立端口与管理路径生产经验告诉我管理端点最好和网关业务流量走两个端口不要让外部流量有机会碰到管理接口。下面是一组我常用的配置management: server: port: 19090 endpoints: web: base-path: /manage exposure: include: gateway,health,info,metrics,prometheus endpoint: gateway: enabled: true这时管理端点地址就变成了http://localhost:19090/manage/gateway/routes。独立的端口 独立的 base-path可以在云环境的安全组规则里单独对外开放避免和业务端口混在一起。如果网关部署在 Kubernetes 里不要把这个管理端口暴露到集群外只需要允许 Prometheus 或运维平台访问即可。3. 路由和过滤器相关端点逐个拆解3.1 查看当前网关已加载的路由表这是我认为最常用的端点。执行curl http://localhost:19090/manage/gateway/routes能拿到类似这样的结果[ { route_id: order-service-route, uri: lb://order-service, predicate: Paths: [/api/order/**], match trailing slash: true, metadata: {}, order: 0 } ]这里能看到每个路由的 ID、目标 URI、断言条件。很多“路由没生效”的问题看一眼这里就清楚了——如果列表里压根没有这条路由说明配置没被加载或更新如果列表里有但请求仍不通那就要往注册中心、负载均衡、过滤器方向查了。我们之前排查过一个案例路由在配置中心里已经调整了 URI但网关还在往旧地址转发。执行一下 routes 端点果然是旧的 URI。根源是路由缓存没有刷新手动调 refresh 后立刻恢复。3.2 查看具体某个路由的过滤器组合只看到断言的还不够你还需要知道这个路由实际会经过哪些过滤器。用下面的接口curl http://localhost:19090/manage/gateway/routes/order-service-route会返回这个路由的完整定义包括 Filter 列表{ route_id: order-service-route, route_definition: { id: order-service-route, predicates: [ { name: Path, args: { pattern: /api/order/** } } ], filters: [ { name: StripPrefix, args: { parts: 1 } }, { name: AddRequestHeader, args: { name: X-Gateway, value: order } } ] }, route: { ... } }这个接口在生产排查中价值极高。比如前端请求 /api/order/xxx 转发到后端后路径不对多半是路由里 StripPrefix 配错或者某些过滤器没挂上去。而过滤器顺序对不对直接看这里就能确认。3.3 查看过滤器的整体链路全局过滤器与路由过滤器网关里有两类过滤器全局过滤器GlobalFilter和路由过滤器GatewayFilter。排查时经常要看它们的执行顺序和实现类。查看全局过滤器curl http://localhost:19090/manage/gateway/globalfilters查看路由过滤器工厂curl http://localhost:19090/manage/gateway/routefiltersglobalfilters 返回的结果会带上 order 信息比如{ org.springframework.cloud.gateway.filter.ReactiveLoadBalancerClientFilter: 10150, org.springframework.cloud.gateway.filter.WebsocketRoutingFilter: 2147483647, org.springframework.cloud.gateway.filter.NettyRoutingFilter: 2147483647, org.springframework.cloud.gateway.filter.ForwardRoutingFilter: 2147483647, org.springframework.cloud.gateway.filter.GatewayMetricsFilter: 2147483647 }这些 order 值决定了过滤器的执行顺序。如果你自己写了一个 GlobalFilter但发现它没按预期时机执行来这个端点看它到底排在哪、是否注册成功效率远高于加日志重启。routefilters 则列出了所有可用的 GatewayFilter 工厂例如 AddRequestHeader、StripPrefix、Retry 等。它更多是帮助你确认当前版本支持哪些过滤器工厂排查“某个过滤器名字是否拼写正确”时特别有用。3.4 动态刷新路由不重启让配置即时生效这是 Actuator 在网关里最实用的能力之一。当路由配置来自配置中心时配置中心的改动不会自动同步到网关进程内的路由缓存需要通过 refresh 端点来触发刷新curl -X POST http://localhost:19090/manage/gateway/refresh这个接口内部会重新从 RouteDefinitionRepository 读取路由定义并刷新内存中的路由缓存。我在实际项目里测试过Nacos 配置中心里的路由 YAML 更新后如果不 refresh网关仍按旧路由转发调用一次 refresh新的路由定义马上生效。整个过程不需要重启网关也没有请求中断。需要补充的是有些团队用 Spring Cloud Gateway 的spring.cloud.gateway.discovery.locator.enabledtrue自动发现服务路由这种方式生成的路由不建议靠 refresh 去管理因为它本身是和注册中心联动的。refresh 主要面向的是显式定义的 RouteDefinition。4. 从“查看”到“控制”生产环境里的实用玩法4.1 不重启服务实现临时路由下线有时候某个下游服务出问题你想快速把入口流量停掉而不是去改注册中心。使用 Actuator 的 DELETE 端点可以临时删除某条路由curl -X DELETE http://localhost:19090/manage/gateway/routes/order-service-route删除后这条路由会从网关中消失流量自然就导不过去了。但这里要搞清楚这个操作只对内存中的路由定义有效不会改到配置中心的源数据。如果网关进程重启路由又会从配置中心加载回来。所以生产环境的建议是临时下线可以用这个接口救急但问题的根因修复后还是要通过正常的配置发布流程把路由恢复回来。要是想彻底下线改配置中心才是一劳永逸的正路。4.2 定时刷新路由应对固定时间窗口的路由变更Refresh 手动执行没问题但如果你经常在固定时间窗口需要更新一批路由规则可以考虑做定时自动刷新。比如用 Spring Boot 自带Scheduled注解写一个定时任务每 30 秒调一次 refresh 端点Component EnableScheduling public class RouteRefreshTask { private final RestTemplate restTemplate; public RouteRefreshTask(RestTemplate restTemplate) { this.restTemplate restTemplate; } Scheduled(fixedDelay 30000) public void refreshRoutes() { String url http://localhost:19090/manage/gateway/refresh; restTemplate.postForEntity(url, null, Void.class); } }不过这块我不太建议盲目上。如果路由变更不频繁定时任务会白白增加网关的管理请求压力并且容易掩盖“配置没有自动生效”的真相。更推荐的做法是在配置中心推送配置时手动触发一次 refresh保持人工可控。4.3 用 Actuator 的指标端点做网关监控开 Actuator 的另一个重要收益是可以接 Micrometer把网关的关键指标暴露给 Prometheus。在依赖里加dependency groupIdio.micrometer/groupId artifactIdmicrometer-registry-prometheus/artifactId /dependency然后配置里把 prometheus 端点也暴露出来management: endpoints: web: exposure: include: gateway,health,info,prometheus这时候访问/manage/prometheus就能拉到网关的 JVM、HTTP 请求数等指标。同时 Spring Cloud Gateway 自身也会通过 Micrometer 记录路由指标例如每个路由的请求计数、耗时等配合 Grafana 可以做一个很直观的网关监控面板。我之前负责的网关在接入 Prometheus 后有一次线上流量异常上涨就是通过路由维度的请求指标第一时间发现是某个服务路由的流量占比突然飙升而不是靠用户投诉才感知到。4.4 运维脚本一行命令输出路由总览在实际运维中我习惯把一个封装好的查询命令放在网关机器上要排查问题时直接敲不用再去翻官方文档拼参数。下面这个 shell 脚本可以直接抄#!/bin/bash GATEWAY_ADDRhttp://127.0.0.1:19090/manage if [ -z $1 ]; then echo Usage: $0 [routes|routefilters|globalfilters|refresh] [routeId] exit 1 fi case $1 in routes) curl -s $GATEWAY_ADDR/gateway/routes | jq . ;; route) curl -s $GATEWAY_ADDR/gateway/routes/$2 | jq . ;; routefilters) curl -s $GATEWAY_ADDR/gateway/routefilters | jq . ;; globalfilters) curl -s $GATEWAY_ADDR/gateway/globalfilters | jq . ;; refresh) curl -s -X POST $GATEWAY_ADDR/gateway/refresh | jq . ;; *) echo Unknown command: $1 ;; esac比如要刷新路由就执行./gateway-tool.sh refresh。这个脚本依赖 jq在网关机器上装一下即可。我用了挺久排查效率提升非常明显。5. 常见问题与避坑指南5.1 端点一直 404 的可能原因依赖没加最常见检查spring-boot-starter-actuator是否在 pom 里。暴露配置没有 include gateway只配了management.endpoints.web.exposure.includehealth,info那网关端点肯定访问不到。访问路径不对如果你设置了management.endpoints.web.base-path要记得路径已经改了。服务不是网关Spring Cloud Gateway 的 gateway 端点组只有在网关项目里才会注册。在普通业务服务里配了也白搭。过滤器优先级冲突导致启动失败有极少数场景下安全过滤器和 Actuator 路径冲突会让端点被拦这时检查项目里是否有 Spring Security 或自定义 WebFilter。5.2 关于 Actuator 安全千万别裸奔你可能也见过spring boot actuator 漏洞相关的新闻。这类问题几乎都是因为 Actuator 端点暴露过度、又没加认证导致的。比如把/actuator/env、/actuator/heapdump这种敏感端点全暴露出来外部访问者能看到环境变量、配置信息和内存快照这是实打实的安全风险。所以网关的 Actuator 必须遵守几条铁律不要用include: *暴露全部端点。管理端口与业务端口分开。只在可信网络内开放管理端口尽量通过 Kubernetes NetworkPolicy 或云安全组限制来源 IP。如果必须走通用端口一定要在网关前面加一层认证比如 Spring Security或者只允许内网访问。对 refresh、DELETE、POST 这类写操作必须做更严格的控制最好只允许运维跳板机调用。我自己在测试环境里习惯用最简单的方式——只监听 127.0.0.1通过 SSH 隧道或跳板机访问management: server: address: 127.0.0.1 port: 19090这样外网无论如何都摸不到管理端口生态最干净。5.3 端点暴露后路由表看起来是空的如果你用spring.cloud.gateway.discovery.locator.enabledtrue做服务自动发现路由路由 id 通常是大写服务名不会出现在 predicates 里。如果你发现 routes 端点返回了路由但没有你预期的 Path 断言那多半就是 discovery locator 自动生成的路由。这类路由的匹配规则是基于服务名的再配合lb://service-id转发和 YAML 里显式声明的 Path 路由模式不一样排查时别搞混。5.4 动态刷新不生效的坑refresh 接口调用成功但路由没变我先总结一下我遇到过的几种情况配置中心没有真正更新数据源refresh 只是把旧配置又读了一遍。使用了自定义 RouteDefinitionRepository但没有实现刷新逻辑或者实现内部缓存了旧数据。调错了地址暴露了一台还没更新的网关实例。建议在脚本里加一个“刷新后拉路由表”的动作确认目标网关实例当前的路由版本。我在生产环境遇到过一次“刷新不生效”的问题最后发现是那个实例已经处于半死状态配置的刷新事件没被真正处理只有重启才能恢复。所以刷新之后务必再查一次路由表形成闭环验证。5.5 关于端点配置变更不生效的问题有些老项目把management.endpoints.gateway.enabled放在bootstrap.yml里但 Spring Boot 2.4 之后配置初始化顺序有变化导致网关端点没有被正常启用。我建议把 Actuator 相关配置放在application.yml或配置中心的application-{profile}.yml里而不是 bootstrap 里。实测下来能避免不少诡异问题。最后再分享一个小技巧在网关启动日志里加上一段话把当前管理端点的地址打印出来例如“Management endpoints available at http://localhost:19090/manage”。排查问题时一眼就能看到自己连的是哪个环境、哪个端口。这个习惯我保持了很久省过很多次不必要的来回确认。