深度解析:Spring Cloud Gateway 从入门到实战
一文吃透 Spring Cloud Gateway:微服务网关从入门到实战
在微服务架构日益普及的今天,随着业务规模的不断扩大,后端服务被拆分为多个独立的微服务模块,每个模块负责特定的业务功能,这种架构虽然提升了开发效率、降低了模块间的耦合度,但也带来了接口管理、访问控制等一系列新问题。而API 网关 作为分布式系统中不可或缺的核心组件,恰好能解决这些痛点。它作为后端所有微服务的统一入口,集中承担着路由转发、权限校验、流量控制、监控统计等关键职责,是微服务架构的"流量中枢"。本文将结合 Spring Cloud 官方推荐的网关方案 ------Spring Cloud Gateway,从核心原理、核心组件、快速上手到实战部署,层层拆解,带你快速掌握微服务网关的设计思路与落地方法,轻松应对实际项目中的网关应用场景。
一、为什么需要 API 网关?
在微服务架构兴起之前,单体应用是主流的开发模式,所有业务逻辑、接口都集中部署在一个应用中,权限校验、日志统计、异常处理等公共逻辑只需实现一次,维护起来相对简单。
但当业务发展到一定规模,单体应用逐渐暴露出部署繁琐、扩展性差、故障影响范围大等问题,此时微服务拆分成为必然选择。然而,拆分为微服务后,每个服务独立暴露接口、独立部署,随之带来一系列棘手问题:
-
代码冗余且维护成本高:每个微服务都需要重复实现鉴权、限流、日志记录、跨域处理等公共逻辑,一旦这些逻辑需要修改,就必须逐个修改所有微服务,不仅增加了开发人员的工作量,还容易出现修改不一致的问题。
-
调用链路复杂且不安全:外部客户端(如前端应用、第三方服务)需要记住多个微服务的地址,直接访问各个服务接口,不仅增加了客户端的调用复杂度,还会导致服务接口直接暴露在公网中,存在数据泄露、非法访问等安全隐患。
-
服务耦合度高且扩展性差:服务升级、扩缩容或地址变更时,需要同步修改客户端的调用配置,一旦遗漏就会导致调用失败,严重影响系统的稳定性和扩展性。
这一问题可以用一个生活化的场景来理解:就像企业办事需要前台统一核验身份、引导部门,如果没有前台,来访人员需要逐个找到对应部门,每个部门还要单独核验身份,效率低下且容易出现混乱。而 API 网关就相当于微服务架构的"前台",所有外部请求都必须先经过网关,由网关完成统一的身份校验、路径匹配后,再转发到对应的微服务,既简化了调用流程,又提升了系统的安全性和可维护性。
二、API 网关核心能力
API 网关本质上是一个独立的微服务,它采用设计模式中的门面模式,作为后端所有微服务的唯一入口,屏蔽了后端服务的复杂性,为客户端提供统一、简洁的调用方式。其核心功能围绕"请求管控"和"服务保护"展开,具体包括以下5点:
-
权限控制:作为系统的第一道安全屏障,网关会对所有进入的请求进行统一的身份认证(如 Token 校验、OAuth2 授权)和接口权限校验,拦截非法请求、未授权请求,防止恶意访问,保护后端服务的安全。
-
动态路由:这是网关最核心的功能之一,网关会根据预设的规则(如请求路径、请求参数、请求头、请求时间等),将请求动态转发到对应的微服务。这种动态路由能力支持服务的动态扩缩容、灰度发布等场景,无需修改客户端配置。
-
负载均衡:当某个微服务部署了多个实例时,网关会结合注册中心(如 Nacos、Eureka)获取服务实例列表,通过负载均衡算法(如轮询、随机、权重)将请求分发到不同的实例,实现服务压力的分担,提升系统的并发处理能力。
-
限流熔断:为了保护后端微服务不被突发流量击垮,网关会对请求流量进行监控,当流量超过预设阈值时,会触发限流机制(如返回 429 状态码),拒绝部分请求;同时,当后端服务出现故障时,网关会触发熔断机制,避免大量请求堆积导致服务雪崩,保障系统的稳定性。
-
日志监控:网关会统一记录所有请求的详细信息,包括请求路径、请求参数、响应时间、响应状态等,同时收集接口性能指标(如吞吐量、响应延迟),为系统的运维监控、问题排查提供数据支持。
三、主流网关方案对比
目前业界开源的网关方案众多,不同的网关组件在维护状态、性能、生态适配等方面各有优劣,在 Spring Cloud 生态中,最常用的三种网关方案如下,可根据项目需求选择合适的方案:
| 网关组件 | 维护状态 | 性能 | 生态适配 |
|---|---|---|---|
| Zuul 1.x | 停止更新 | 一般 | 适配早期 Spring Cloud 版本,基于 Servlet 同步阻塞模型,高并发场景下性能瓶颈明显 |
| Spring Cloud Gateway | 活跃更新 | 是 Zuul 1.x 的 1.6 倍 | 原生支持 Spring Cloud 生态,基于 Spring Boot、WebFlux 异步非阻塞模型,配置灵活、扩展性强 |
| Nginx + Lua | 稳定 | 高 | 基于 Nginx 高性能内核,需通过 Lua 脚本进行二次开发,适配微服务生态的成本较高 |
Spring Cloud Gateway 是 Spring 官方主推的网关方案,它完美解决了 Zuul 1.x 的性能瓶颈,基于 WebFlux 实现异步非阻塞处理,能更好地应对高并发场景;同时,它与 Spring Cloud 生态深度融合,支持 Nacos、Eureka 等注册中心,配置简单、扩展性强,是当前微服务项目中网关的首选,本文将重点讲解其使用方法与实战技巧。
四、Spring Cloud Gateway 快速上手
1. 项目创建与依赖引入
Spring Cloud Gateway 本身是一个独立的 Spring Boot 应用,因此我们需要新建一个网关模块,然后在 pom.xml 文件中引入核心依赖,确保网关能够正常运行并与注册中心、微服务进行交互。核心依赖如下:
xml
<!-- Spring Cloud Gateway 核心依赖,提供网关核心功能(路由、过滤等) -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- Nacos 服务发现依赖,用于从 Nacos 注册中心获取微服务实例信息 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- 负载均衡依赖,用于对微服务实例进行负载分发,Spring Cloud Gateway 需单独引入 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
2. 启动类编写
网关本质上就是一个普通的 Spring Boot 应用,无需额外添加特殊注解,只需编写标准的 Spring Boot 启动类,即可启动网关服务。启动类代码如下:
java
@SpringBootApplication
public class GatewayApplication {
public static void main(String[] args) {
// 启动网关应用,参数可根据实际需求传递
SpringApplication.run(GatewayApplication.class, args);
}
}
3. 核心配置:路由规则
路由规则是 Spring Cloud Gateway 的核心配置,它决定了请求如何被转发到对应的微服务。我们需要在 application.yml 配置文件中,配置网关端口、服务名称、注册中心地址以及具体的路由规则,具体配置如下:
yaml
server:
port: 10030 # 网关服务的端口,自定义即可,注意避免端口冲突
spring:
application:
name: gateway # 网关服务的名称,用于在注册中心注册
cloud:
nacos:
discovery:
server-addr: 110.41.51.65:10020 # Nacos 注册中心的地址和端口
gateway:
routes:
# 商品服务路由:用于转发商品相关的请求
- id: product-service # 路由唯一标识,自定义即可,需保证全局唯一
uri: lb://product-service # 目标服务地址,lb:// 表示启用负载均衡,后面跟微服务名称
predicates:
- Path=/product/** # 路由断言:匹配路径以 /product 开头的所有请求
# 订单服务路由:用于转发订单相关的请求
- id: order-service
uri: lb://order-service
predicates:
- Path=/order/**
配置说明:
-
id:路由的唯一标识,自定义即可,主要用于区分不同的路由规则,需保证全局唯一,避免冲突。 -
uri:目标微服务的地址,支持两种格式:一是普通的 HTTP 地址(如 http://127.0.0.1:9090),二是lb://服务名(如 lb://product-service),后者表示从注册中心获取该服务的实例列表,并启用负载均衡。 -
predicates:路由断言,本质是一组条件判断规则,只有当请求满足所有断言条件时,网关才会将该请求转发到对应的微服务。这里使用的 Path 断言是最常用的断言类型,用于匹配请求路径。
4. 测试验证
配置完成后,我们需要启动相关服务进行测试,确保网关能够正常转发请求。测试步骤如下:首先启动 Nacos 注册中心,然后分别启动商品服务(product-service)、订单服务(order-service),最后启动网关服务(gateway)。所有服务启动成功后,访问以下网关地址进行测试:
Plain
# 访问商品服务(通过网关转发),请求会被转发到 product-service 微服务
http://127.0.0.1:10030/product/1001
# 访问订单服务(通过网关转发),请求会被转发到 order-service 微服务
http://127.0.0.1:10030/order/1
正常情况下,请求会根据路径匹配规则,自动转发到对应的微服务,并返回微服务的响应结果。若出现"URL拼写可能存在错误,请检查"的报错,需排查路由配置是否正确、微服务是否正常启动、网关是否能从注册中心获取到微服务实例。
五、路由断言(Predicate):精准匹配请求
Predicate 是 Java 8 提供的一个函数式编程接口,它接收一个参数并返回一个布尔值,主要用于条件过滤和请求参数校验。在 Spring Cloud Gateway 中,Predicate 被称为路由断言,它是路由规则的核心组成部分,用于判断请求是否符合当前路由的匹配条件,只有匹配成功的请求才会被转发到目标微服务。Spring Cloud Gateway 内置了多种路由断言工厂,无需额外开发,即可支持按不同规则匹配请求。
常用断言类型
-
Path:按请求路径匹配(最常用),通过配置路径规则(支持通配符),匹配符合规则的请求,如 /product/** 表示匹配所有以 /product 开头的请求。 -
Method:按请求方式匹配,可指定一个或多个请求方式(如 GET、POST),只有请求方式符合的请求才会被转发。 -
After/Before/Between:按请求时间匹配,After 表示匹配指定时间之后的请求,Before 表示匹配指定时间之前的请求,Between 表示匹配两个指定时间之间的请求(需注意第二个时间必须在第一个时间之后)。 -
Header:按请求头参数匹配,需指定请求头名称和对应的正则表达式,只有请求头中包含该参数且值符合正则表达式的请求才会被转发。 -
Cookie:按 Cookie 信息匹配,需指定 Cookie 名称和对应的正则表达式,只有请求中包含该 Cookie 且值符合正则表达式的请求才会被转发。 -
RemoteAddr:按客户端 IP 匹配,可指定 IP 地址或 IP 段,只有客户端 IP 符合条件的请求才会被转发,常用于限制特定 IP 访问。
断言组合使用
Spring Cloud Gateway 支持多断言组合使用,多个断言之间默认是"逻辑与"的关系,即只有同时满足所有断言条件的请求,才会被转发到目标微服务。例如,我们可以同时设置路径匹配、时间匹配和请求方式匹配,具体配置如下:
yaml
predicates:
- Path=/product/** # 匹配路径以 /product 开头的请求
- After=2024-01-01T00:00:00.000+08:00[Asia/Shanghai] # 匹配 2024年1月1日之后的请求
- Method=GET # 只匹配 GET 方式的请求
六、网关过滤器(Filter):请求增强与拦截
过滤器(Filter)是 Spring Cloud Gateway 中另一个核心组件,它用于在请求转发到微服务之前(Pre 类型)或请求执行完成、响应返回给客户端之前(Post 类型),对请求或响应进行增强、拦截或修改,实现诸如参数添加、权限校验、日志记录、响应处理等功能。根据作用范围的不同,过滤器可分为两类:
-
GatewayFilter:局部过滤器,仅作用于单个路由或一组路由,需要在具体的路由配置中指定,常用于对特定路由的请求进行个性化处理。
-
GlobalFilter:全局过滤器,作用于所有路由,无需单独配置,自动对所有请求生效,常用于实现全局的权限校验、日志记录、限流等功能。
1. 常用内置过滤器
Spring Cloud Gateway 内置了大量的过滤器工厂,无需自定义开发,只需在配置文件中简单配置,即可实现对应的功能,常用的内置过滤器如下:
-
AddRequestParameter:为当前请求添加指定的请求参数,方便后端微服务获取固定参数。 -
AddRequestHeader:为当前请求添加指定的请求头,常用于传递身份信息、版本信息等。 -
AddResponseHeader:为响应结果添加指定的响应头,常用于设置跨域相关头信息、响应标识等。 -
RequestRateLimiter:限流过滤器,采用令牌桶算法实现限流功能,可限制单位时间内的请求数量,保护后端服务不被流量过载击垮。 -
Retry:请求重试过滤器,当后端服务返回指定状态码(如 500、404)时,自动发起重试请求,提升接口的可用性。 -
RequestSize:请求体大小限制过滤器,设置允许接收的最大请求体大小,若请求体超过该大小,返回 413 状态码(Payload Too Large)。
2. 过滤器使用示例
下面以 AddRequestParameter 过滤器为例,演示局部过滤器的使用方法。我们给商品服务路由添加一个固定的请求参数 userName=bite,这样所有通过该路由转发的请求,后端商品服务都能直接获取到该参数,具体配置如下:
yaml
routes:
- id: product-service
uri: lb://product-service
predicates:
- Path=/product/**
filters:
- AddRequestParameter=userName, bite # 为请求添加 userName 参数,值为 bite
配置完成后,后端商品服务的接口中,只需添加 userName 参数,即可直接获取到网关传递过来的值,示例代码如下:
java
@RequestMapping("/product/{productId}")
public ProductInfo getProductById(@PathVariable("productId") Integer productId, String userName){
System.out.println("收到请求,Id:"+productId);
System.out.println("网关传递的参数 userName:"+userName); // 输出:bite
return productService.selectProductById(productId);
}
3. 全局默认过滤器
如果需要让某个过滤器对所有路由生效,无需在每个路由中重复配置,只需使用 spring\.cloud\.gateway\.default\-filters 配置全局默认过滤器,该配置接收一个过滤器列表,所有路由都会自动应用这些过滤器。例如,给所有响应添加一个默认的响应头,配置如下:
yaml
spring:
cloud:
gateway:
default-filters:
# 为所有响应添加 X-Response-Default 响应头,值为 Success
- AddResponseHeader=X-Response-Default, Success
七、自定义过滤器:满足业务定制需求
Spring Cloud Gateway 内置的过滤器虽然能满足大部分常见场景,但在实际项目中,往往会有一些个性化的业务需求(如自定义鉴权、自定义日志记录、请求参数加密解密等),此时就需要自定义过滤器。自定义过滤器同样支持 GatewayFilter(局部)和 GlobalFilter(全局)两种类型,下面分别演示其实现方法。
1. 自定义路由过滤器
自定义路由过滤器(局部)需要继承 AbstractGatewayFilterFactory 抽象类,该类已帮我们实现了大部分基础逻辑,我们只需重写 apply 方法,实现自定义的过滤逻辑即可。同时,可通过内部类定义配置参数,用于接收配置文件中的参数。具体实现代码如下:
java
@Slf4j // 用于日志记录
@Service // 交给 Spring 管理,让网关能够扫描到该过滤器
public class CustomGatewayFilterFactory
extends AbstractGatewayFilterFactory<CustomGatewayFilterFactory.CustomConfig>
implements Ordered { // 实现 Ordered 接口,用于指定过滤器优先级
// 构造方法,指定配置类类型
public CustomGatewayFilterFactory() {
super(CustomConfig.class);
}
// 核心方法:实现过滤逻辑,包含 Pre 和 Post 两种过滤时机
@Override
public GatewayFilter apply(CustomConfig config) {
// exchange:封装了 HTTP 请求和响应的上下文信息,可获取请求参数、响应结果等
// chain:过滤器链,用于执行下一个过滤器
return (exchange, chain) -> {
// Pre 过滤器:请求转发到微服务之前执行
log.info("[Pre] 自定义路由过滤器执行,配置参数 name:{}", config.getName());
// 执行下一个过滤器,当所有过滤器执行完成后,执行 then 中的逻辑
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
// Post 过滤器:响应返回给客户端之前执行
log.info("[Post] 自定义路由过滤器响应处理");
}));
};
}
// 指定过滤器的优先级,值越小优先级越高,Ordered.LOWEST_PRECEDENCE 表示最低优先级
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
// 配置类:用于接收配置文件中的参数,与 yml 中的配置对应
@Data // Lombok 注解,自动生成 getter/setter 方法
public static class CustomConfig {
private String name; // 自定义配置参数,可在 yml 中配置
}
}
自定义路由过滤器实现完成后,需要在 application.yml 中配置使用,指定过滤器名称(默认是类名前缀,即 Custom)和配置参数,具体配置如下:
yaml
filters:
- name: Custom # 过滤器名称,对应自定义过滤器类名前缀
args:
name: custom filter # 配置参数,对应 CustomConfig 中的 name 属性
2. 自定义全局过滤器
自定义全局过滤器比路由过滤器更简单,只需实现 GlobalFilter 和 Ordered 两个接口,无需额外配置,Spring 会自动扫描并将其作为全局过滤器,对所有路由生效。具体实现代码如下:
java
@Slf4j
@Service // 交给 Spring 管理
public class CustomGlobalFilter implements GlobalFilter, Ordered {
// 核心过滤方法,对所有请求生效
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// Pre 过滤逻辑:请求进入网关后,转发到微服务之前执行
log.info("[Pre] 自定义全局过滤器进入...");
// 执行下一个过滤器,then 中的逻辑在响应返回前执行
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
// Post 过滤逻辑:响应返回给客户端之前执行
log.info("[Post] 自定义全局过滤器返回...");
}));
}
// 指定全局过滤器的优先级,值越小优先级越高
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
}
过滤器执行顺序
当项目中同时存在 GatewayFilter(局部)和 GlobalFilter(全局)时,网关会将所有过滤器合并到一个过滤器链中,按照一定的顺序依次执行,具体规则如下:
-
所有过滤器都必须指定一个 int 类型的 order 值(通过实现 Order 接口或添加 @Order 注解),order 值越小,优先级越高,执行顺序越靠前。
-
Spring Cloud Gateway 内置的过滤器,其 order 值由 Spring 自动指定;自定义过滤器的 order 值由开发者自行指定。
-
当多个过滤器的 order 值相同时,执行顺序为:
默认过滤器(default\-filters) \> 路由过滤器(GatewayFilter) \> 全局过滤器(GlobalFilter)。
八、微服务网关部署实战
在开发环境中完成网关的配置和测试后,需要将网关服务部署到生产环境,与其他微服务协同工作。部署过程主要分为以下6个步骤,确保服务稳定运行:
-
配置修改:将开发环境的配置修改为生产环境配置,重点更新数据库连接信息、Nacos 注册中心地址(生产环境通常为集群地址)、日志输出路径等,避免使用开发环境的测试配置。
-
项目打包:使用 Maven 或 Gradle 对网关服务(gateway)、商品服务(product-service)、订单服务(order-service)进行打包,生成可执行的 jar 包,确保打包过程中没有依赖缺失。
-
服务器上传:将打包好的三个 jar 包,通过 FTP、SCP 等工具上传至 Linux 生产服务器的指定目录(如 /usr/local/service),方便后续管理和启动。
-
启动依赖:网关服务依赖 Nacos 注册中心,因此需要先启动 Nacos 注册中心(生产环境建议部署 Nacos 集群,提升可用性),启动前建议删除 Nacos 的 data 目录,避免残留开发环境的数据影响生产环境。
-
后台启动服务:在 Linux 服务器上,使用 nohup 命令后台启动三个服务,并将日志输出到指定文件,方便后续排查问题,具体命令如下:
bash
# 启动订单服务,后台运行,日志输出到 logs/order.log
nohup java -jar order-service.jar > logs/order.log 2>&1 &
# 启动商品服务,后台运行,日志输出到 logs/product.log
nohup java -jar product-service.jar > logs/product.log 2>&1 &
# 启动网关服务,后台运行,日志输出到 logs/gateway.log
nohup java -jar gateway.jar > logs/gateway.log 2>&1
- 验证:服务启动完成后,访问网关的公网地址(如 http://服务器IP:10030/product/1001),确认请求能够正常转发到对应的微服务,返回正确的响应结果;同时查看 Nacos 控制台,确认三个服务都已成功注册,无异常状态。
九、总结
Spring Cloud Gateway 作为 Spring 官方原生的微服务网关,完美适配 Spring Cloud 生态,解决了微服务架构中接口管理、安全控制、流量管控等核心痛点,其核心价值主要体现在以下四个方面:
-
统一入口:为所有微服务提供唯一的访问入口,简化了客户端的调用流程,降低了客户端与微服务之间的耦合度,同时也便于对所有请求进行统一管理。
-
逻辑抽离:将鉴权、限流、日志、跨域等公共逻辑下沉到网关,避免在每个微服务中重复实现,减少了代码冗余,降低了维护成本,同时保证了公共逻辑的一致性。
-
高性能:基于 WebFlux 异步非阻塞模型开发,摆脱了传统 Servlet 同步阻塞的性能瓶颈,吞吐量是 Zuul 1.x 的 1.6 倍,能够更好地应对高并发场景。
-
易扩展:内置了丰富的路由断言和过滤器,支持自定义路由规则、自定义过滤器,能够灵活适配各种个性化的业务需求,同时与 Nacos、Eureka 等注册中心无缝集成,部署和扩展都非常便捷。
在实际微服务项目中,网关是流量入口、安全屏障、治理核心,它不仅能够提升系统的安全性、可维护性和扩展性,还能为系统的运维监控提供有力支持。因此,熟练掌握 Spring Cloud Gateway 的使用与定制,是后端开发工程师必备的核心技能之一,也是搭建高可用微服务架构的关键环节。