3. 创建第一个Spring Cloud Gateway工程

前两节我们已经理解了 Spring Cloud Gateway 在微服务体系中的位置:它基于 Spring WebFlux 与 Reactor Netty 构建,是一个响应式、非阻塞的 API 网关。本节不再停留在概念层面,而是从零开始,动手创建一个可以真正转发请求的网关工程。整个过程包含六个关键动作:用 Spring Initializr 生成骨架、引入 spring-cloud-starter-gateway 依赖、编写启动类、配置基础路由、启动服务并验证转发、观察 Netty 服务端启动日志。走完这一遍,你会得到一个最小可运行的网关,它也是后续学习断言、过滤器、限流、熔断等能力的基础载体。

一、用 Spring Initializr 生成项目骨架

推荐使用 Spring Initializr(start.spring.io)生成工程,它能自动处理 Spring Boot 与 Spring Cloud 的版本对应关系,避免手工拼装依赖时出现版本冲突。生成时选择以下参数:

  • Project:Maven(也可选 Gradle,本节以 Maven 为例)
  • Language:Java
  • Spring Boot:选择与目标 Spring Cloud 版本匹配的稳定版,例如 3.2.x
  • Group:com.example,Artifact:gateway-demo
  • Packaging:Jar,Java:17 或 21
  • Dependencies:先只勾选 Gateway,它会自动带入 WebFlux 与 Reactor Netty

下载解压后,工程目录结构大致如下:

gateway-demo
├── pom.xml
└── src
    └── main
        ├── java/com/example/gatewaydemo
        │   └── GatewayDemoApplication.java
        └── resources
            └── application.yml

这里有一个容易踩的坑:网关工程不要再引入 spring-boot-starter-web。因为 Gateway 基于 WebFlux,而 spring-boot-starter-web 会引入 Servlet 容器(Tomcat),两者同时存在会导致应用启动时因 Web 应用类型冲突而失败。Initializr 勾选 Gateway 时不会带入 web starter,这一点是正确的默认行为。

二、确认并理解 gateway 依赖

打开 pom.xml,确认存在如下依赖(版本由 Spring Cloud BOM 统一管理):

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

这个 starter 内部聚合了 spring-boot-starter-webflux、reactor-netty、spring-cloud-starter 等模块。也就是说,引入它之后,网关天然运行在 Reactor Netty 之上,而不是 Tomcat。理解这一点,对后面观察启动日志非常关键。

三、编写启动类

启动类本身非常朴素,与普通 Spring Boot 应用没有区别:

package com.example.gatewaydemo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class GatewayDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(GatewayDemoApplication.class, args);
    }
}

网关的核心能力由自动配置类 GatewayAutoConfiguration 在启动时装配,因此启动类不需要额外注解。真正决定“请求怎么转发”的,是接下来的路由配置。

四、配置基础路由

在 application.yml 中配置一条最简单的路由,把发往网关的 /baidu/** 请求转发到外部站点,用于验证转发链路是否打通:

server:
  port: 9000

spring:
  application:
    name: gateway-demo
  cloud:
    gateway:
      routes:
        - id: baidu_route
          uri: https://www.baidu.com
          predicates:
            - Path=/baidu/**

这条配置包含路由的三个必备要素:id 是路由唯一标识;uri 是目标地址;predicates 是匹配条件,这里用 Path 断言匹配路径。当请求 http://localhost:9000/baidu 到达网关时,断言命中,网关就会把请求转发到 https://www.baidu.com/baidu。注意默认情况下路径会原样拼接,若希望去掉前缀,需要额外配置 StripPrefix 过滤器,这属于后续内容。

五、启动并验证请求转发

在工程根目录执行:

mvn spring-boot:run

或者打包后运行:

mvn clean package -DskipTests
java -jar target/gateway-demo-0.0.1-SNAPSHOT.jar

启动成功后,用浏览器或 curl 访问网关:

curl -i http://localhost:9000/baidu

如果返回 HTTP 200 且响应体是百度首页内容,说明请求已经成功经过网关转发到目标站点。若返回 404,通常是断言未命中,需要检查请求路径是否与 Path 规则一致;若返回 502 或连接超时,则要检查 uri 是否可达、网络是否放行。

六、观察 Netty 服务端启动日志

启动过程中,控制台会输出关键日志。最值得关注的是 Netty 相关行:

Netty started on port 9000
Started GatewayDemoApplication in 2.315 seconds

看到 Netty started on port 9000,就证明网关确实运行在 Reactor Netty 之上,而不是 Tomcat。如果日志里出现 Tomcat started on port,说明工程里混入了 Servlet 容器依赖,需要回到 pom.xml 排查。此外,还可以通过 --debug 参数启动,观察 GatewayAutoConfiguration 是否被加载,进一步确认路由自动配置生效。

Spring Cloud Gateway 首个工程:请求转发链路 客户端 curl / 浏览器 Gateway 应用 (端口 9000) Reactor Netty Server Route: baidu_route Predicate: Path=/baidu/** 目标服务 https://www.baidu.com GET /baidu 转发 响应 200 返回内容 启动日志关键行 Netty started on port 9000 Started GatewayDemoApplication in 2.315 seconds ✔ 出现 Netty 行 → 网关运行在 Reactor Netty 上 ✘ 出现 Tomcat 行 → 混入了 spring-boot-starter-web

七、常见问题与判断依据

第一次搭建网关工程时,最容易遇到三类问题。第一类是启动即失败,报 Spring MVC found on classpath 或 Web 应用类型冲突,原因是引入了 spring-boot-starter-web,解决方式是移除该依赖。第二类是启动成功但访问返回 404,说明断言没有命中,需要核对请求路径与 Path 规则是否匹配,注意 /baidu/** 能匹配 /baidu 及其子路径。第三类是返回 502 或超时,说明目标地址不可达,需要检查 uri 是否正确、网络是否放行、目标站点是否可访问。

判断网关是否真正生效,最直接的依据是启动日志中的 Netty started on port 9000,以及访问网关端口后能拿到目标服务的响应内容。两者同时满足,说明路由配置与转发链路都已打通。

到这里,一个最小可运行的 Spring Cloud Gateway 工程就完成了。它虽然只有一条路由,但已经完整覆盖了“生成工程—引入依赖—编写启动类—配置路由—启动验证—观察日志”的闭环。后续的断言工厂、过滤器、限流、熔断、动态路由等能力,都是在这个骨架上逐步叠加的。