深度解析:Spring Cloud Gateway 从入门到实战

深度解析:Spring Cloud Gateway 从入门到实战

一文吃透 Spring Cloud Gateway:微服务网关从入门到实战

在微服务架构日益普及的今天,随着业务规模的不断扩大,后端服务被拆分为多个独立的微服务模块,每个模块负责特定的业务功能,这种架构虽然提升了开发效率、降低了模块间的耦合度,但也带来了接口管理、访问控制等一系列新问题。而API 网关 作为分布式系统中不可或缺的核心组件,恰好能解决这些痛点。它作为后端所有微服务的统一入口,集中承担着路由转发、权限校验、流量控制、监控统计等关键职责,是微服务架构的"流量中枢"。本文将结合 Spring Cloud 官方推荐的网关方案 ------Spring Cloud Gateway,从核心原理、核心组件、快速上手到实战部署,层层拆解,带你快速掌握微服务网关的设计思路与落地方法,轻松应对实际项目中的网关应用场景。

一、为什么需要 API 网关?

在微服务架构兴起之前,单体应用是主流的开发模式,所有业务逻辑、接口都集中部署在一个应用中,权限校验、日志统计、异常处理等公共逻辑只需实现一次,维护起来相对简单。

但当业务发展到一定规模,单体应用逐渐暴露出部署繁琐、扩展性差、故障影响范围大等问题,此时微服务拆分成为必然选择。然而,拆分为微服务后,每个服务独立暴露接口、独立部署,随之带来一系列棘手问题:

  • 代码冗余且维护成本高:每个微服务都需要重复实现鉴权、限流、日志记录、跨域处理等公共逻辑,一旦这些逻辑需要修改,就必须逐个修改所有微服务,不仅增加了开发人员的工作量,还容易出现修改不一致的问题。

  • 调用链路复杂且不安全:外部客户端(如前端应用、第三方服务)需要记住多个微服务的地址,直接访问各个服务接口,不仅增加了客户端的调用复杂度,还会导致服务接口直接暴露在公网中,存在数据泄露、非法访问等安全隐患。

  • 服务耦合度高且扩展性差:服务升级、扩缩容或地址变更时,需要同步修改客户端的调用配置,一旦遗漏就会导致调用失败,严重影响系统的稳定性和扩展性。

这一问题可以用一个生活化的场景来理解:就像企业办事需要前台统一核验身份、引导部门,如果没有前台,来访人员需要逐个找到对应部门,每个部门还要单独核验身份,效率低下且容易出现混乱。而 API 网关就相当于微服务架构的"前台",所有外部请求都必须先经过网关,由网关完成统一的身份校验、路径匹配后,再转发到对应的微服务,既简化了调用流程,又提升了系统的安全性和可维护性。

二、API 网关核心能力

API 网关本质上是一个独立的微服务,它采用设计模式中的门面模式,作为后端所有微服务的唯一入口,屏蔽了后端服务的复杂性,为客户端提供统一、简洁的调用方式。其核心功能围绕"请求管控"和"服务保护"展开,具体包括以下5点:

  1. 权限控制:作为系统的第一道安全屏障,网关会对所有进入的请求进行统一的身份认证(如 Token 校验、OAuth2 授权)和接口权限校验,拦截非法请求、未授权请求,防止恶意访问,保护后端服务的安全。

  2. 动态路由:这是网关最核心的功能之一,网关会根据预设的规则(如请求路径、请求参数、请求头、请求时间等),将请求动态转发到对应的微服务。这种动态路由能力支持服务的动态扩缩容、灰度发布等场景,无需修改客户端配置。

  3. 负载均衡:当某个微服务部署了多个实例时,网关会结合注册中心(如 Nacos、Eureka)获取服务实例列表,通过负载均衡算法(如轮询、随机、权重)将请求分发到不同的实例,实现服务压力的分担,提升系统的并发处理能力。

  4. 限流熔断:为了保护后端微服务不被突发流量击垮,网关会对请求流量进行监控,当流量超过预设阈值时,会触发限流机制(如返回 429 状态码),拒绝部分请求;同时,当后端服务出现故障时,网关会触发熔断机制,避免大量请求堆积导致服务雪崩,保障系统的稳定性。

  5. 日志监控:网关会统一记录所有请求的详细信息,包括请求路径、请求参数、响应时间、响应状态等,同时收集接口性能指标(如吞吐量、响应延迟),为系统的运维监控、问题排查提供数据支持。

三、主流网关方案对比

目前业界开源的网关方案众多,不同的网关组件在维护状态、性能、生态适配等方面各有优劣,在 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. 自定义全局过滤器

自定义全局过滤器比路由过滤器更简单,只需实现 GlobalFilterOrdered 两个接口,无需额外配置,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) \&gt; 路由过滤器(GatewayFilter) \&gt; 全局过滤器(GlobalFilter)

八、微服务网关部署实战

在开发环境中完成网关的配置和测试后,需要将网关服务部署到生产环境,与其他微服务协同工作。部署过程主要分为以下6个步骤,确保服务稳定运行:

  1. 配置修改:将开发环境的配置修改为生产环境配置,重点更新数据库连接信息、Nacos 注册中心地址(生产环境通常为集群地址)、日志输出路径等,避免使用开发环境的测试配置。

  2. 项目打包:使用 Maven 或 Gradle 对网关服务(gateway)、商品服务(product-service)、订单服务(order-service)进行打包,生成可执行的 jar 包,确保打包过程中没有依赖缺失。

  3. 服务器上传:将打包好的三个 jar 包,通过 FTP、SCP 等工具上传至 Linux 生产服务器的指定目录(如 /usr/local/service),方便后续管理和启动。

  4. 启动依赖:网关服务依赖 Nacos 注册中心,因此需要先启动 Nacos 注册中心(生产环境建议部署 Nacos 集群,提升可用性),启动前建议删除 Nacos 的 data 目录,避免残留开发环境的数据影响生产环境。

  5. 后台启动服务:在 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 
  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 的使用与定制,是后端开发工程师必备的核心技能之一,也是搭建高可用微服务架构的关键环节。

相关推荐
HAPPY酷1 小时前
从Public到Private:UE5 C++类创建路径差异全解析
java·c++·ue5
AI进化营-智能译站1 小时前
ROS2 C++开发系列08-传感器数据缓存与指令解析方式之数组、向量与字符串实战
开发语言·c++·缓存·ai
许彰午1 小时前
CacheSQL(一):手写数据库的工程化重生
java·数据库·缓存
shjita2 小时前
记录java执行中的一个错误细节
java·开发语言
空中海2 小时前
Docker入门到精通
java·docker·eureka
AI进化营-智能译站2 小时前
ROS2 C++开发系列14-Lambda表达式处理传感器数据流|文件IO保存机器人实验日志
开发语言·c++·ai·机器人
itzixiao2 小时前
L1-067 洛希极限(10分)[java][python]
java·开发语言·算法
java1234_小锋2 小时前
Spring AI 2.0 开发Java Agent智能体 - Spring AI项目调用本地Ollama模型
java·人工智能·spring·spring ai2.0
二哈赛车手2 小时前
新人笔记---多策略搭建策略执行链实现RAG检索后过滤
java·笔记·spring·设计模式·ai·策略模式