Spring Cloud Gateway路由规则详解与502 Bad Gateway排查实战

发布时间:2026/9/9 12:09:15
Spring Cloud Gateway路由规则详解与502 Bad Gateway排查实战 接手网关项目那会儿我最早啃的就是 Spring Cloud Gateway 的路由规则。说实话路由规则写起来真不难难的是写完以后线上突然给你抛一堆 502 Bad Gateway你根本分不清是路由没匹配上、上游服务挂了还是注册中心没实例。尤其是最近不少人反馈类似unexpected status 502 bad gateway: unknown error, url: http://127.0.0.1:15721/v1/responses这种报错一看就是网关把请求转发到了一个本机端口结果对端根本没服务在监听。这篇东西我打算把 Spring Cloud Gateway 路由规则彻底拆开讲从 Route、Predicate、Filter 三个核心概念讲起再给一套能直接抄的配置方案最后重点复盘 502、405、路由不生效这些高频问题。适合刚接手网关、被路由规则搞到头大、或者已经在线上被 502 折磨过的同学把文章看完基本能自己定位大部分问题。1. 路由规则的整体设计与配置思路网关这东西说白了就是所有流量的总入口。Spring Cloud Gateway 整条链路的核心就三个词Route、Predicate、Filter。很多人一开始觉得路由规则复杂其实是被这三个概念的组合方式绕晕了。1.1 核心三件套Route、Predicate、Filter我习惯拿快递站做类比。Route路由一条快递线路。一个网关里可以配很多条 Route每条路由都有唯一的id代表“什么请求走哪条线路”。Predicate谓词/断言快递面单上的收件条件。它负责判断“这个请求满不满足条件”比如路径是不是/api/user/**、请求方法是不是 GET、Header 里有没有指定 token。满足条件才会走这条路由。Filter过滤器运输途中的处理工序。请求匹配到路由后会经过一系列过滤器比如把前缀/api剥掉、给请求头加东西、做鉴权、限流都在这一层完成。一次完整的请求流程是这样客户端请求进来 - 遍历所有 Route - 用 Predicate 依次匹配 - 匹配成功则走该 Route - 执行 Filter 链 - 转发到下游服务 - 响应原路返回。理解了这个流程后面写路由就不会乱。你只需要明确三件事这个请求长什么样Predicate、转发到哪里去URI、中间要做什么处理Filter。1.2 为什么用 Spring Cloud Gateway 而不是 Zuul 1.x很多人会纠结选型。Zuul 1.x 是基于 Servlet 的同步阻塞模型每个请求占用一个线程线程池一满整个网关就卡死。Spring Cloud Gateway 基于 Spring WebFlux 和 Netty是异步非阻塞模型用少量线程撑住大量并发连接性能上限高得多。单从路由规则的角度看Gateway 的配置也更灵活。它的 Predicate 和 Filter 都是独立的、可组合的组件你想加一个“每周一上午 10 点且 Header 里带 versionv2 的请求才走灰度路由”用几行配置就能拼出来。Zuul 1.x 做这种组合判断要写不少自定义代码。另外 Spring Cloud Gateway 原生支持与注册中心Nacos、Eureka集成URI 用lb://service-name就能按服务名负载均衡不用手写服务发现逻辑这对微服务架构来说太重要了。注意Spring Cloud Gateway 基于 WebFlux项目里不要同时引入spring-boot-starter-web否则会冲突启动时各种莫名其妙的报错。我见过不少同事在这里翻车。1.3 静态配置与 Java DSL 两种方式怎么选路由规则的配置方式主要有两种。第一种是application.yml 静态配置适合路由规则相对固定的场景。上线后如果改了路由需要重启网关才能生效优点是直观、好维护、新人上手快。第二种是Java DSL 动态配置通过RouteLocatorBuilder编写Bean方法来定义路由。这种方式的好处是可以结合配置中心Nacos Config、Apollo把路由规则做成可动态刷新的配置网关不用重启就能调整路由。适合路由规则频繁变化、或者需要按环境灵活调整的场景。实际项目里我通常是两个混用固定的、公共的路由走 yml 配置业务迭代快的、需要灰度控制的走 Java DSL配合配置中心做动态刷新。Bean public RouteLocator customRouteLocator(RouteLocatorBuilder builder) { return builder.routes() .route(order-service-route, r - r .path(/api/order/**) .filters(f - f.stripPrefix(1)) .uri(lb://order-service)) .build(); }这段代码的意思是所有/api/order/**的请求先剥掉第一级前缀/api再负载均衡转发到order-service。2. 核心细节解析Predicate 与 Filter 的实操要点配置语法好背真正决定路由规则好不好用的是对 Predicate 和 Filter 细节的理解。下面这几个点是我用得最多、也最容易出错的。2.1 高频路由谓词逐个拆解先看一张我整理的常用谓词对照表后面会有详细示例。Predicate作用示例Path按请求路径匹配支持通配符**和占位符{}Path/api/user/**Method按 HTTP 方法匹配MethodGET,POSTHeader按请求头匹配支持正则HeaderX-Request-Id, \dQuery按查询参数匹配支持正则Querypage, \dCookie按 Cookie 匹配支持正则CookiesessionId, [a-f0-9]Host按 Host 请求头匹配Host**.example.comRemoteAddr按客户端 IP 匹配RemoteAddr192.168.1.1/24After/Before/Between按时间匹配After2025-01-01T00:00:0008:00[Asia/Shanghai]Weight按权重分流用于灰度发布Weightgroup1, 8Path 谓词是最常用的。/api/user/**能匹配/api/user、/api/user/1、/api/user/100/orders但匹配不了/api/users因为**匹配的是零个或多个路径段而不是模糊匹配任意字符串。这个细节很多人搞混。Header 谓词常用于版本控制。比如我想让只有带X-Version: v2的请求走新服务predicates: - Path/api/product/** - HeaderX-Version, v2这里多个谓词之间是AND 关系必须全部满足才会匹配这条路由。Weight 谓词做灰度分流很好用。比如 80% 流量走稳定版20% 走灰度版两条路由用同一个 group权重不同- id: product-route-v1 uri: lb://product-service-v1 predicates: - Path/api/product/** - Weightproduct-group, 8 - id: product-route-v2 uri: lb://product-service-v2 predicates: - Path/api/product/** - Weightproduct-group, 22.2 谓词组合顺序的坑配错直接路由不生效路由规则的匹配是从上到下按顺序执行的第一个匹配到的 Route 生效后面的就不再看了。这个特性既是优势也是坑。举例说明假设我配了两个路由spring: cloud: gateway: routes: - id: user-route uri: lb://user-service predicates: - Path/api/user/** - id: user-detail-route uri: lb://user-detail-service predicates: - Path/api/user/detail/**如果user-route写在前面/api/user/detail这个请求会被user-route匹配到转发到user-service而你想走精细化接口的user-detail-route永远不会生效因为请求根本走不到它。所以写路由规则的铁律是精确路径的 Route 放在前面宽泛路径的 Route 放在后面。把/api/user/detail/**提到/api/user/**前面才符合预期。另外还要注意 yml 里路由的id必须唯一重复id会导致启动时路由注册异常或者后面配置覆盖前面的路由这种问题排查起来特别隐蔽。2.3 全局过滤器与局部过滤器怎么选Filter 分为两种GatewayFilter局部过滤器和GlobalFilter全局过滤器。局部过滤器只对某一条路由生效常见的有StripPrefix1转发前剥掉路径里的第一级前缀RewritePath用正则重写路径AddRequestHeader给请求头添加参数AddResponseHeader给响应头添加参数Retry下游失败时自动重试RequestRateLimiter限流全局过滤器对所有路由生效适合做统一鉴权、日志记录、灰度标记。自定义一个全局过滤器很简单Component public class AuthGlobalFilter implements GlobalFilter, Ordered { Override public MonoVoid filter(ServerWebExchange exchange, GatewayFilterChain chain) { String token exchange.getRequest().getHeaders().getFirst(Authorization); if (token null || token.isEmpty()) { exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); return exchange.getResponse().setComplete(); } return chain.filter(exchange); } Override public int getOrder() { return -100; } }getOrder()返回值越小过滤器越先执行。做鉴权的过滤器 Order 要设得比转发类过滤器小确保请求在转发前就被拦截。这里返回 401 的场景正好对应很多人遇到的 token missing / unauthorized 问题。2.4 StripPrefix 与 RewritePath路径转换的常规操作写路由规则时最容易出错的就是路径前缀处理。比如前端请求/api/user/1你希望网关剥掉/api再把/user/1转发给 user-service那就用StripPrefix1。filters: - StripPrefix1StripPrefix1表示去掉第一级路径段。如果请求是/api/v1/user/1你想去掉/api/v1就用StripPrefix2。RewritePath更灵活可以按正则重写。比如把/api/user/1重写成/user/1filters: - RewritePath/api/(?segment.*), /$\{segment}注意$\{segment}里的$要加反斜杠转义否则 yml 会把它当变量解析写错以后路径重写不生效报错还特别难查。我自己的习惯是只要路径前缀规则统一优先用StripPrefix如果不同服务的路径规范不一致再用RewritePath做精细化改写。两者不要混用不然路由逻辑会变得很难维护。3. 实操过程从零配置一个带路由规则的网关理论说完了直接上实操。我用 Spring Boot 2.6.13 Spring Cloud 2021.0.5 Spring Cloud Alibaba 2021.0.5.0 Nacos 2.x 这套组合演示。3.1 版本选型与环境准备版本匹配是网关项目的第一道坎。Spring Cloud Gateway 的版本跟着 Spring Cloud 走Spring Boot 和 Spring Cloud 的版本必须对应乱配版本会导致启动直接失败。我这里给一个经过验证的组合组件版本JDK1.8 以上Spring Boot2.6.13Spring Cloud2021.0.5Spring Cloud Alibaba2021.0.5.0Nacos Server2.2.0 以上注意这个组合只做参考实际项目请根据你现有的技术栈统一规划。升级版本前先查官方版本对应关系不要凭感觉升级。3.2 核心依赖配置创建项目后pom.xml里核心依赖如下dependency groupIdorg.springframework.cloud/groupId artifactIdspring-cloud-starter-gateway/artifactId /dependency dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-starter-alibaba-nacos-discovery/artifactId /dependency关键点不要加spring-boot-starter-web。Gateway 基于 WebFlux加了 web starter WebApplicationType 会冲突典型报错是Spring MVC found on classpath, which is incompatible with Spring Cloud Gateway。需要动态刷路由的话再加dependency groupIdcom.alibaba.cloud/groupId artifactIdspring-cloud-starter-alibaba-nacos-config/artifactId /dependency3.3 一份可直接复制的完整路由配置下面是application.yml核心片段。这个配置包含了服务发现、路由规则、过滤器和超时设置能覆盖大多数项目的需求。server: port: 8080 spring: application: name: api-gateway cloud: nacos: discovery: server-addr: 127.0.0.1:8848 gateway: routes: - id: user-service-route uri: lb://user-service predicates: - Path/api/user/** filters: - StripPrefix1 - id: order-service-route uri: lb://order-service predicates: - Path/api/order/** filters: - StripPrefix1 globalcors: cors-configurations: [/**]: allowedOrigins: * allowedMethods: - GET - POST - PUT - DELETE allowedHeaders: * httpclient: connect-timeout: 3000 response-timeout: 5suri: lb://user-service是关键。它表示从注册中心按服务名user-service获取实例再做负载均衡。如果你没有注册中心直接用uri: http://127.0.0.1:8081这种写死地址的方式也行但这样就没法享受服务发现的弹性扩缩容了。httpclient超时配置也要重视默认连接超时是 45 秒对很多接口来说太长了。一旦下游服务假死请求会长时间挂住拖垮整个网关。我一般把连接超时和响应超时都调小再配合重试策略。3.4 与 Nacos 注册中心结合的路由规则路由规则里lb://前缀是 Spring Cloud Gateway 内置的负载均衡协议。它会把服务名解析成注册中心里的实例列表然后按负载均衡策略选择一个实例进行转发。实际操作中要检查两件事Nacos 服务列表里有没有目标服务服务名必须和lb://后面的名字完全一致大小写敏感。目标服务的实例状态是否健康Nacos 控制台能直接看到实例的健康状态。如果这两个条件不满足路由规则写得再漂亮也没用请求转发时必然报错最常见的就是 503 Service Unavailable 或者 502 Bad Gateway。3.5 启动验证与网关未启动的区分技巧配置写完启动网关后先用最简单的请求验证curl http://localhost:8080/api/user/1如果返回了 user-service 的数据说明路由规则生效了。这里要特别提醒很多人遇到本机连不上网关先别急着怀疑路由规则。先确认网关进程到底有没有起来curl http://localhost:8080/actuator/health如果连接被拒绝那是网关没启动不是路由问题。这是两个完全不同的排查方向前者看进程后者看配置。网上很多报错就是“gateway 未启动”或者“端口连接失败”这种跟 502 完全不同别混在一起查。4. 502 Bad Gateway 排查实录从路由规则到服务实例的完整链路502 Bad Gateway 是网关场景里出现频率最高的报错也是最容易让人误判的。我单独拿一章来讲毕竟这个坑实在太多。4.1 502 的本质请求到网关了但上游没接住502 的语义是网关已经收到了客户端的请求也根据路由规则找到了目标地址但在转发时没有从上游服务拿到有效响应。简单说请求死在从网关到下游服务的那一段路上。常见的报错形式主要有两种。一种是摘要式的unexpected status 502 bad gateway另一种带具体转发地址比如unknown error, url: http://127.0.0.1:15721/v1/responses。后一种其实已经指明了转发目标排查范围直接缩小到这台机器和这个端口。这时候最直接的验证手段是curl -v http://127.0.0.1:15721/v1/responses如果本机 curl 也报连接失败说明下游端口根本没有服务监听如果本机能通但网关转发 502那就要查网络隔离、防火墙或者网关所在节点到目标节点的连通性。4.2 最容易踩的坑URI 写死 localhost 或固定 IP我见过最多的 502 案例根源都在 URI 配错。有人把uri写成了http://localhost:8081这在网关部署在容器里时特别致命——容器里的 localhost 指向的是容器自己不是宿主机更不是下游服务所在的机器。还有一种是把uri写成某个固定 IP比如http://127.0.0.1:15721。如果下游服务是多实例部署或者服务迁移过机器这个 IP 就是错的。这也是为什么生产环境强烈建议用lb://service-name代替手写地址。排查思路是拿到报错里的 url先确认这个地址是不是当前合理的服务地址再确认这个地址从网关所在节点能否访问。三步走# 1. 看端口是否在监听 netstat -an | grep 15721 # 2. 测端口连通性 telnet 127.0.0.1 15721 # 3. 直接请求目标接口 curl -v http://127.0.0.1:15721/v1/responses4.3 服务发现链路导致的 502/503如果用了lb://方式502 的排查重点就转向服务发现链路。常见问题有下游服务没注册到 Nacos、服务名写错注意大小写、服务实例全部下线、注册中心网络隔离导致网关拉不到最新实例列表。Nacos 控制台的“服务列表”页面能直接看到服务名和实例健康状态。另外要留意如果下游服务启动时注册失败但进程没退出网关可能拿到一个不可用的实例地址转发过去就是连接超时或拒绝连接最终表现为 502。还有一个隐蔽场景服务注册了但端口配错。服务实际监听 8081注册到 Nacos 的端口却是 8082网关拿到的地址就是错的。这种问题在多人协作、环境变量不一致时经常出现。4.4 超时导致 502 的定位方法超时也会表现为 502。如果下游服务处理很慢超过网关配置的response-timeout网关就会主动断开并返回 502。判断是不是超时问题看日志最直接。Gateway 的日志里通常会有TimeoutException: null或者Read timed out之类的字样。解决办法分两层网关层调大超时时间但这是治标不治本。下游服务优化接口性能这才是根本。如果接口确实需要长时间处理建议改成异步任务 轮询结果而不是让网关干等。网关这个位置很特殊长连接会占用线程和连接池资源超时调得越大资源被拖死的风险越高。4.5 连接池耗尽导致的 502Spring Cloud Gateway 使用 HttpWebClient 发起转发底层是 Netty 连接池。高并发场景下如果连接池被耗尽了新的请求无法获取连接也会报 502。连接池耗尽的典型现象是网关 CPU 和内存都不高但大量请求 502日志里有Connection pool exhausted或者Pool acquired timeout。处理方法调大spring.cloud.gateway.httpclient.pool.max-connections默认是几百可以根据压测结果调大。调大max-idle-time让空闲连接存活更久减少频繁建立连接的损耗。优化下游服务的响应速度这是最根本的。4.6 502 排查清单速查表这个表我打印出来贴在工位上过排查时挨个查效率很高。排查项命令/工具判断标准网关进程是否正常curl http://localhost:8080/actuator/health返回 JSON 且 statusUP路由是否匹配GET /actuator/gateway/routes能看到当前生效的所有 Route目标服务端口监听netstat -an | grep 15721端口处于 LISTEN 状态路径连通性curl -v http://127.0.0.1:15721/v1/responses能正常返回业务响应注册中心状态Nacos 控制台服务列表实例健康服务名与 lb 后一致超时配置application.yml 中 httpclient 配置超时时间是否过小连接池网关日志有无Connection pool exhausted防火墙/网络策略telnet IP 端口连接能建立成功5. 高频报错速查与路由不生效的排查技巧除了 502网关项目里还有几个报错频率极高我也一起整理出来尤其是 405 和路由不生效遇到的人特别多。5.1 405 Method Not Allowed请求到了但方法不对405 的意思是请求已经到达了目标服务但目标服务不允许这个 HTTP 方法。典型场景是下游接口只支持 POST你用 GET 去调或者网关把 GET 转发过去了下游 Spring MVC 接口没有对应的 RequestMapping。对接 SAP 网关时尤其常见。SAP 的服务接口对 HTTP 方法和 Content-Type 要求很严格方法不对、请求头不对直接返回 405。排查方法分三步确认请求方法和下游接口方法定义是否一致。确认加了StripPrefix或RewritePath后实际转发给下游的路径是否正确。方法没变但路径变了可能打到一个根本不存在的接口返回 404 或 405。直接绕过网关用 curl 请求下游服务原地址看是否同样 405。如果直连正常、走网关才 405问题在网关的转发逻辑或过滤器比如某个过滤器改写了请求方法或 Content-Type。5.2 路由不生效的 5 个常见原因路由规则写了但请求没按预期走这类问题我总结了 5 个原因。第一Route 顺序错误。宽泛路径放在精确路径前面导致精确路由永远匹配不到。调整顺序即可。第二StripPrefix 参数不对。前端路径是/api/user/1下游接口是/user/1如果 StripPrefix 配成 0 或 2转发路径就不对。第三YAML 缩进错误。Spring Cloud Gateway 的配置层级很深routes要在spring.cloud.gateway下面多一个空格或少一个空格都可能导致配置没被加载。判断方法是用/actuator/gateway/routes看实际生效的路由列表。第四配置缓存没刷新。尤其在 Nacos Config 动态配置场景下改了配置但没触发刷新网关还是一直用旧路由。第五谓词条件太严格。比如配置了HeaderX-Version, v2但请求里没带这个 Header路由就匹配不上。可以用 curl 加上对应 Header 验证。curl -H X-Version: v2 http://localhost:8080/api/product/15.3 网关统一鉴权与 401/token missing 问题前面全局过滤器里写过如果网关做了统一鉴权请求没有携带 token 或者 token 校验失败网关会直接返回 401。很多人把这种报错归结为“路由问题”其实方向偏了。排查顺序应该是请求有没有带Authorization头。网关的鉴权过滤器有没有生效它返回的响应体是不是你预期的 401。如果直连下游服务正常、走网关 401那 100% 是网关的全局过滤器在拦截。这里要特别注意GlobalFilter的getOrder()。如果你的鉴权过滤器 Order 大于转发过滤器可能先转发了后面才校验顺序就乱了。建议鉴权过滤器 Order 设为负数优先级最高。5.4 用 Actuator 端点快速排查网关状态排查网关问题时Actuator 是我最先用的工具。先在pom.xml引入依赖dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-actuator/artifactId /dependency然后在application.yml开放 gateway 端点management: endpoints: web: exposure: include: gateway,health,info之后三个端点很关键# 查看当前所有路由规则 curl http://localhost:8080/actuator/gateway/routes # 查看全局过滤器 curl http://localhost:8080/actuator/gateway/globalfilters # 查看路由过滤器工厂列表 curl http://localhost:8080/actuator/gateway/routefilters/actuator/gateway/routes是排查路由不生效的第一利器。如果配置了但这里看不到说明配置根本没加载如果这里能看到但是转发错误说明问题在后面的 URI 或 Filter。5.5 几个实用排查命令最后分享几个我日常用的命令都是压箱底的。# 查看网关日志中与路由相关的信息 tail -f gateway.log | grep RouteToRequestUrlFilter # 查看日志中实际转发的目标地址 tail -f gateway.log | grep outbound # 查看注册中心实例列表 curl http://127.0.0.1:8848/nacos/v1/ns/instance/list?serviceNameuser-service日志里outbound开头的关键字会打印网关实际转发的地址配合这个能快速确认转发目标是否符合预期。路由规则排障我总结下来就是一句话先确认路由有没有匹配到再确认转发地址对不对最后看下游服务接没接住按这个顺序走大部分问题十分钟内能定位。我个人在实际操作中还有一个习惯每个路由规则写完都要用 curl 带相关请求头跑一遍确认通路之后再上线。网关是流量的总闸口一点马虎都可能酿成线上事故。把路由规则当成一门严谨的手艺来对待你会在后续的排障中省下大量时间。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询