1. 从一次 404 说起:微服务统一入口到底难在哪
如果你正在做 SpringCloud 微服务,大概率遇到过这种场景:订单服务、用户服务、商品服务各自跑在不同的端口上,前端同学每次联调都要问你要一堆地址,改一个环境就得重新配一遍。更麻烦的是,认证逻辑散落在每个服务里,加个限流要改五六个工程。这时候就需要一个统一入口,把所有请求先收拢到一处,再按规则分发出去——这就是 SpringCloud Gateway 要解决的问题。
Gateway 是 Spring Cloud 生态里的第二代网关,基于 Spring5 的 WebFlux 实现,走的是响应式编程路线,和早期基于 Servlet 阻塞模型的 Zuul 相比,在高并发场景下资源占用更低。它能做的事情可以概括成三件:把外部请求按规则路由到对应微服务并做负载均衡、在请求进出时执行过滤器链做鉴权或改写、对流量做限流保护后端。适合谁?适合已经拆出多个微服务、需要一个统一入口来收口认证、路由和限流的团队,也适合正在学 SpringCloud 想搞懂网关到底怎么落地的同学。
这篇文章不讲空概念,直接给你能复制的路由断言、过滤器、限流配置片段,然后一步步启动网关,逐条验证路由转发和过滤是否生效。中间会穿插我实际踩过的坑,比如断言路径写错导致 404、全局过滤器 order 值冲突、跨域配置不生效这些。你跟着做一遍,基本就能把统一网关跑起来。
2. 动手前先把 TaoToken 配好:模型调用与网关调试的配合
在正式写 Gateway 配置之前,先说一个容易被忽略但很实用的点:调试网关时经常需要快速验证接口返回、生成测试数据、或者让模型帮你分析一段报错日志。这时候如果本地没有一个顺手的模型调用入口,来回切工具会很打断节奏。我的做法是先把 TaoToken 配好,它提供统一的 API 入口,兼容常见的模型调用格式,配一次就能在多个工具里复用。
TaoToken 的官网入口是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 地址是 https://taotoken.net/api ,注意 API 地址后面不加 UTM 参数。如果你只是想在网页里对话验证模型,可以直接用模型对话入口;如果是要长期写代码、跑 Agent,建议看 Coding Plan;需要管理密钥就去 API Keys 页面;接入细节看接入文档。
这里重点说配置三件套:Base URL、Key、Model ID。不管你用的是 Cline、Codex 还是 Claude Code,核心都是这三样。以 Cline 的 MCP 配置为例,你需要在配置文件里写清楚:
{ "mcpServers": { "taotoken": { "command": "npx", "args": ["-y", "@taotoken/mcp-server"], "env": { "TAOTOKEN_BASE_URL": "https://taotoken.net/api", "TAOTOKEN_API_KEY": "你的Key", "TAOTOKEN_MODEL_ID": "你的模型ID" } } } }如果你用的是 Codex,配置写在auth.json里,结构类似:
{ "base_url": "https://taotoken.net/api", "api_key": "你的Key", "model": "你的模型ID" }Claude Code 的话,在 settings 里配置 Base URL 和 Key,Model ID 按你实际使用的填。这三件套缺一不可,少一个就会出现 401 或者模型找不到的报错。配好之后,你在调试 Gateway 时遇到看不懂的报错,可以直接把日志贴给模型让它帮你定位,效率会高不少。
需要说明的是,TaoToken 在这里的角色是帮你做模型调用和调试辅助,不是替代你的编辑器或网关本身。Gateway 的路由和过滤逻辑还是得你自己在工程里写,TaoToken 只是让你在写和调的过程中有个顺手的工具。
3. 可复制的 Gateway 路由与过滤器配置
这一节是核心,直接给你能粘贴进项目的配置。先建一个 Gateway 服务,pom.xml 里引入两个依赖:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency>启动类加@EnableDiscoveryClient,然后重点在application.yml里写路由。下面这份配置包含路由断言、路由过滤器、默认过滤器和限流,你可以直接改服务名用:
server: port: 10010 spring: application: name: gateway cloud: nacos: server-addr: 127.0.0.1:8848 gateway: routes: - id: user-service-route uri: lb://userservice predicates: - Path=/user/** filters: - AddRequestHeader=X-Request-From, gateway - id: order-service-route uri: lb://orderservice predicates: - Path=/order/** - Method=GET,POST default-filters: - AddResponseHeader=X-Response-From, gateway globalcors: cors-configurations: '[/**]': allowedOrigins: "*" allowedMethods: "*" allowedHeaders: "*"几个关键点解释一下。uri里的lb://userservice表示走负载均衡到 Nacos 里注册的userservice服务,lb就是 loadBalance。predicates下的Path=/user/**是路径断言,意思是/user开头的请求才匹配这条路由。filters下的AddRequestHeader是路由过滤器,只对当前这条路由生效;default-filters下的对所有路由生效。globalcors是跨域配置,[/**]表示对所有路径生效。
如果你要做限流,Gateway 内置了RequestRateLimiter过滤器,配合 Redis 使用。先在 pom 里加 Redis 响应式依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis-reactive</artifactId> </dependency>然后在路由的 filters 里加限流配置:
filters: - name: RequestRateLimiter args: redis-rate-limiter.replenishRate: 10 redis-rate-limiter.burstCapacity: 20 key-resolver: "#{@ipKeyResolver}"replenishRate是每秒补充的令牌数,burstCapacity是令牌桶容量,key-resolver指向你定义的限流键解析器 Bean。在配置类里加一个:
@Bean public KeyResolver ipKeyResolver() { return exchange -> Mono.just( exchange.getRequest().getRemoteAddress().getHostString() ); }这样就是按客户端 IP 限流,每个 IP 每秒 10 个请求,突发最多 20 个。超过的请求会返回 429。
再说全局过滤器。路由过滤器在 yml 里配就行,但要做鉴权这种复杂逻辑,得自己写全局过滤器。实现GlobalFilter和Ordered接口:
@Component public class AuthGlobalFilter implements GlobalFilter, Ordered { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { String token = exchange.getRequest().getQueryParams().getFirst("authorization"); if (!"admin".equals(token)) { exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); return exchange.getResponse().setComplete(); } return chain.filter(exchange); } @Override public int getOrder() { return 0; } }这个过滤器会检查请求参数里authorization是否等于admin,不是就返回 401。getOrder()返回 0,值越小优先级越高。路由过滤器和默认过滤器的 order 由 Spring 按声明顺序从 1 递增,全局过滤器由你自己定。当 order 值相同时,执行顺序是默认过滤器 > 路由过滤器 > 全局过滤器。
4. 启动网关逐条验证:路由转发与过滤生效
配置写完,启动 Nacos、userservice、orderservice 和 Gateway 四个服务。启动顺序建议先 Nacos,再业务服务,最后 Gateway,这样 Gateway 启动时能拉到服务列表。
验证第一条:路由转发。浏览器或 curl 访问http://localhost:10010/user/1,如果 userservice 有对应的接口,应该能正常返回数据。这一步验证的是Path=/user/**断言和lb://userservice负载均衡是否生效。如果返回 404,先检查 userservice 是否注册到 Nacos、服务名是否写对、路径是否匹配。
验证第二条:路由过滤器。访问http://localhost:10010/user/1,在 userservice 的接口里打印请求头,看有没有X-Request-From: gateway。有就说明AddRequestHeader过滤器生效了。这一步很多人会漏,因为过滤器生效了但接口没打印请求头,就以为没生效,其实是业务代码没读。
验证第三条:默认过滤器。看响应头里有没有X-Response-From: gateway,用 curl 的-i参数能看到。这个对所有路由生效,访问/order/**也应该有。
验证第四条:全局过滤器。访问http://localhost:10010/user/1不带参数,应该返回 401;带上?authorization=admin再访问,应该正常返回。这一步验证全局过滤器的鉴权逻辑。
验证第五条:限流。快速连续访问超过 20 次,应该能看到部分请求返回 429。可以用ab或wrk压测,也可以写个循环 curl。
验证第六条:跨域。前端页面发 ajax 请求到网关,浏览器控制台不应该报 CORS 错误。如果报错,检查globalcors配置的路径和 allowedOrigins 是否正确。
我实测下来,最容易出问题的是断言路径。比如你写Path=/user/**,但实际请求是/userservice/user/1,那就匹配不上。断言匹配的是网关收到的原始路径,不是转发后的路径。还有lb://后面的服务名必须和 Nacos 里注册的spring.application.name完全一致,大小写敏感。
5. 常见报错排查:401、404、429 与 OAuth 问题
这一节对照真实报错来排查。先说 401,如果你访问网关返回 401,大概率是全局过滤器拦截了。检查你的AuthGlobalFilter逻辑,看是不是所有请求都被要求带 authorization 参数。如果是调试阶段,可以先把全局过滤器的 order 调大,或者临时注释掉,确认其他功能正常后再加回来。
404 是最常见的。分几种情况:一是路由断言没匹配上,检查 Path 写的是否和请求路径一致;二是服务没注册到 Nacos,去 Nacos 控制台看服务列表;三是lb://后面的服务名写错。还有一种隐蔽情况是 Gateway 和 spring-webmvc 依赖冲突,Gateway 基于 WebFlux,如果项目里同时引入了 spring-boot-starter-web,会导致 Gateway 不工作。检查 pom,把 web 依赖排除掉。
429 是限流触发了。如果你没配限流却出现 429,检查是不是 Redis 里有限流键残留,或者RequestRateLimiter配置的replenishRate太小。调大replenishRate和burstCapacity再试。
local proxy failed这类报错通常出现在你本地通过代理工具访问外部服务时,检查你的网络配置,确保 Gateway 转发目标可达。如果是用 TaoToken 调试模型时遇到,检查 Base URL 是否写成了https://taotoken.net/api,注意不要多加斜杠或路径。
reading choices报错一般出现在模型返回格式解析时,说明返回结构和你预期的不一致。检查 Model ID 是否填对,以及请求体格式是否符合接口要求。
OAuth 相关问题,如果你在网关做 OAuth2 鉴权,常见的是 token 校验失败。检查 token 是否过期、签名密钥是否一致、Authorization头格式是否是Bearer xxx。Gateway 里做 OAuth 一般用TokenRelay过滤器把 token 透传给下游服务,配置:
filters: - TokenRelay如果出现OAuth2AuthorizationRequestRedirectFilter相关报错,检查你的安全配置是否放行了回调路径。
再说一个配置三件套的坑。如果你在 Cline 或 Codex 里配 TaoToken,Base URL、Key、Model ID 任何一个写错都会报错。Base URL 是https://taotoken.net/api,Key 在 API Keys 页面生成,Model ID 按你实际用的模型填。三个都对了才能正常调用。如果报 401,先查 Key;如果报模型不存在,查 Model ID;如果报连接失败,查 Base URL。
6. 把统一网关落到你的项目里
到这里,路由断言、路由过滤器、默认过滤器、全局过滤器、限流、跨域这几块都跑通了。你现在的 Gateway 已经能作为微服务的统一入口,把认证、路由、限流收口到一处。接下来可以做的优化方向:把全局过滤器的鉴权逻辑换成真实的 JWT 校验,把限流键从 IP 换成用户 ID 或接口维度,把跨域配置按环境区分而不是全放开。
如果你在调试过程中需要快速验证接口、分析报错日志,可以配好 TaoToken 的模型对话或 Coding Plan,把日志贴进去让它帮你定位。接入文档里有详细的配置说明,API Keys 页面可以管理你的密钥。需要长期写代码或跑 Agent 的话,Coding Plan 会更合适。
最后留一个我踩过的坑:Gateway 的default-filters和路由下的filters同时配了同名过滤器时,路由下的会覆盖默认的,不是叠加。比如你在 default-filters 里配了AddRequestHeader=X-A, 1,在路由 filters 里又配了AddRequestHeader=X-A, 2,最终请求头里只有X-A: 2。要叠加就写不同名字的请求头。这个细节在官方文档里不明显,但实际配的时候很容易踩。