Spring Cloud Gateway 入门指南 – wiki基地


Spring Cloud Gateway 入门指南:构建现代微服务架构的基石

引言

在当今软件开发的浪潮中,微服务架构已经成为构建复杂、可伸缩系统的首选模式。然而,随着系统中微服务数量的增加,管理它们之间的交互、处理客户端请求以及实现横切关注点(如认证、授权、限流、监控等)变得越来越复杂。客户端不再需要直接与几十甚至几百个微服务交互,而是需要一个统一的入口。

API Gateway(API 网关)应运而生,它作为客户端与后端微服务之间的“门面”,负责接收所有外部请求,并将其路由到相应的微服务。API Gateway 不仅简化了客户端的交互复杂性,还提供了集中处理许多非业务功能的能力。

Spring Cloud Gateway 是 Spring Cloud 生态系统中的一个重要组件,它提供了一种构建高性能、可伸缩的 API Gateway 的方式。它基于 Spring 5 的 Reactor 框架构建,提供了异步非阻塞的编程模型,非常适合处理高并发的网络请求。

本文将带你深入了解 Spring Cloud Gateway 的核心概念、基本用法以及如何将其集成到你的微服务架构中。无论你是 Spring Boot/Cloud 的新手,还是希望从传统的 API Gateway 迁移,本文都将为你提供一个全面的入门指南。

什么是 Spring Cloud Gateway?

Spring Cloud Gateway 是 Spring Cloud 官方推出的 API 网关服务,旨在替代 Netflix Zuul。与基于 Servlet API 构建的 Zuul 1.x 不同,Spring Cloud Gateway 基于 Spring WebFlux 构建,使用的是 Netty 非阻塞服务器,支持异步和响应式编程。这使得它在高并发场景下拥有更好的性能和资源利用率。

其核心设计理念源于 Netflix Zuul 2.x,但 Spring Cloud Gateway 在 Spring 生态系统内提供了更紧密的集成和更丰富的功能。

Spring Cloud Gateway 的主要职责:

  1. 路由(Routing): 将客户端的请求根据一定的规则转发到后端对应的微服务实例。
  2. 过滤(Filtering): 在请求被路由到后端微服务之前或之后,对请求或响应进行修改、增强或其他处理。这包括认证、授权、限流、日志记录、监控、请求转换等。

为什么选择 Spring Cloud Gateway?

相比于其他 API Gateway 解决方案(包括已停止维护的 Zuul 1.x 或商业产品),Spring Cloud Gateway 具有以下显著优势:

  • 高性能和可伸缩性: 基于 Spring WebFlux 和 Netty,采用异步非阻塞模型,能够处理大量并发连接,提高吞吐量和降低延迟。
  • 与 Spring 生态系统深度集成: 作为 Spring Cloud 的一部分,可以无缝集成 Spring Boot、Spring Cloud Discovery(Eureka, Consul, Nacos等)、Spring Cloud LoadBalancer 等组件。
  • 灵活的路由配置: 支持多种方式配置路由规则,包括基于路径、方法、请求头、查询参数、Host 等多种谓词(Predicates)。
  • 强大的过滤器机制: 提供丰富的内置过滤器,并支持自定义全局过滤器和局部过滤器,方便实现各种横切关注点。
  • 易于开发和维护: 使用熟悉的 Spring 编程模型,配置简洁明了。
  • 活跃的社区支持: 作为 Spring 家族的一员,拥有庞大的开发者社区和完善的文档。

Spring Cloud Gateway 的核心概念

理解 Spring Cloud Gateway 的核心概念是掌握其使用的关键:

  1. Route(路由):
    路由是网关的基本构建块。它由一个 ID、一个目标 URI、一组 Predicates(谓词)和一组 Filters(过滤器)组成。

    • ID: 路由的唯一标识符。
    • URI: 目标 URI,指定请求最终应该转发到哪里。可以是具体的 HTTP 地址(如 http://localhost:8080)、服务发现中的服务名称(如 lb://my-service)或其他协议(如 ws://)。
    • Predicates(谓词): 用于匹配 HTTP 请求的条件。如果一个请求与某个路由的所有谓词都匹配,则该路由被选中。
    • Filters(过滤器): 在请求被路由到目标 URI 之前或之后应用的逻辑。过滤器可以修改请求或响应,执行前置/后置处理。
  2. Predicate(谓词):
    Spring Cloud Gateway 将路由匹配抽象为 Predicate。Predicates 是 Java 8 的 java.util.function.Predicate 的一个实例。Spring Cloud Gateway 内置了许多 Route Predicate Factories,用于匹配各种 HTTP 请求属性,例如:

    • After, Before, Between: 基于时间匹配。
    • Cookie: 基于 Cookie 匹配。
    • Header: 基于请求头匹配。
    • Host: 基于 Host 匹配。
    • Method: 基于 HTTP 方法匹配 (GET, POST等)。
    • Path: 基于请求路径匹配。
    • Query: 基于查询参数匹配。
    • RemoteAddr: 基于远程 IP 地址匹配。
    • Weight: 基于权重匹配(用于灰度发布或 A/B 测试)。
  3. Filter(过滤器):
    Gateway Filters 允许你在请求被路由之前或之后修改请求和响应。过滤器的逻辑被组织在 GatewayFilter Factories 中。Spring Cloud Gateway 内置了许多 GatewayFilter Factories,例如:

    • AddRequestHeader, AddResponseHeader: 添加请求/响应头。
    • RemoveRequestHeader, RemoveResponseHeader: 移除请求/响应头。
    • RewritePath: 重写请求路径。
    • StripPrefix: 剥离请求路径的前缀。
    • Retry: 重试请求。
    • RequestRateLimiter: 限流。
    • CircuitBreaker: 集成断路器(如 Resilience4j)。

    过滤器链(Filter Chain):当一个请求与某个路由匹配时,会创建一个过滤器链,该链包含该路由特定的过滤器以及全局过滤器(Global Filters)。请求将依次通过过滤器链,执行前置逻辑,然后被发送到目标服务,服务响应回来后再依次通过过滤器链执行后置逻辑。

  4. Global Filters(全局过滤器):
    全局过滤器应用于所有路由。它们通常用于处理跨越所有请求的通用功能,如日志记录、安全认证等。Spring Cloud Gateway 也提供了一些内置的全局过滤器,例如 NettyRoutingFilterLoadBalancerClientFilter 等。

Spring Cloud Gateway 的工作流程概述

当一个请求到达 Spring Cloud Gateway 时,其处理流程大致如下:

  1. 请求到达: Spring Cloud Gateway 的 DispatcherHandler 接收到请求。
  2. 路由匹配: DispatcherHandler 将请求发送给 HandlerMapping,HandlerMapping 根据请求信息(如路径、方法、头等)和预定义的路由配置,查找匹配的路由。它会遍历所有路由,检查请求是否满足某个路由的所有 Predicates。
  3. 路由选择: 如果找到多个匹配的路由,会根据路由定义的顺序(order)或优先级选择一个最佳匹配的路由。
  4. 构建过滤器链: 一旦确定了匹配的路由,Gateway 会构建一个过滤器链。这个链包含该路由配置中定义的特定 Filters 以及所有 Global Filters。过滤器的顺序由其 order 值决定。
  5. 执行过滤器链(Pre-processing): 请求在被发送到目标服务之前,会按照顺序依次执行过滤器链中的前置逻辑(即过滤器 filter 方法中在 chain.filter(exchange) 之前的部分)。
  6. 转发请求: 当执行到最后一个前置过滤器(通常是某个负责转发请求的全局过滤器,如 NettyRoutingFilterLoadBalancerClientFilter)时,请求会被发送到目标 URI。
  7. 服务响应: 后端服务处理请求并返回响应。
  8. 执行过滤器链(Post-processing): 服务响应回来后,会按照与前置执行相反的顺序依次执行过滤器链中的后置逻辑(即过滤器 filter 方法中在 chain.filter(exchange) 之后的部分)。
  9. 返回响应: 最终的响应被返回给客户端。

搭建第一个 Spring Cloud Gateway

接下来,我们将通过一个简单的例子来搭建一个 Spring Cloud Gateway。

步骤 1: 创建 Spring Boot 项目

使用 Spring Initializr (https://start.spring.io/) 或你的 IDE 创建一个新的 Spring Boot 项目。选择以下依赖:

  • Spring Boot Version (推荐使用最新的稳定版本)
  • Java Version
  • Project Metadata (Group, Artifact等)
  • Dependencies:
    • Spring Cloud Gateway
    • Spring Boot Actuator (可选,用于监控)
    • Spring Cloud Starter Netflix Eureka Client (如果需要集成服务发现)

生成项目并导入到你的 IDE。

Maven 依赖示例 (pom.xml):

“`xml


4.0.0 org.springframework.boot
spring-boot-starter-parent
3.2.5
com.example
gateway-demo
0.0.1-SNAPSHOT
gateway-demo
Demo project for Spring Cloud Gateway

<properties>
    <java.version>17</java.version> <!-- 或更高版本 -->
    <spring-cloud.version>2023.0.1</spring-cloud.version> <!-- 与Spring Boot版本匹配 -->
</properties>

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

    <!-- 如果需要服务发现,例如 Eureka -->
    <!--
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    -->

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring-cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

“`

步骤 2: 创建主应用程序类

Spring Boot 的主应用程序类非常简单,因为它只需要 @SpringBootApplication 注解。

“`java
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);
}

}
“`

步骤 3: 配置路由规则

src/main/resources/application.yml (或 application.properties) 文件中配置网关的端口和路由规则。

我们将创建一个简单的路由,将所有路径以 /anything/ 开头的请求转发到 http://httpbin.org/anythinghttpbin.org 是一个非常方便的测试服务,它会返回你发送的请求的详细信息。

“`yaml
server:
port: 8080 # 网关服务端口

spring:
cloud:
gateway:
routes:
– id: anything_route # 路由ID,唯一
uri: http://httpbin.org/anything # 目标URI
predicates:
– Path=/anything/** # 谓词:匹配所有以/anything/开头的路径
filters:
# 过滤器:移除路径前缀,例如 /anything/hello -> /hello
# 这里我们转发到 httpbin.org/anything,所以不需要移除前缀
#- StripPrefix=1
“`

配置解释:

  • server.port: 指定网关服务监听的端口,这里是 8080。
  • spring.cloud.gateway.routes: 这是一个路由配置列表。
  • - id: anything_route: 定义一个路由,ID 是 anything_route
  • uri: http://httpbin.org/anything: 定义这个路由的目标 URI 是 http://httpbin.org/anything
  • predicates: 定义这个路由的匹配条件列表。
  • - Path=/anything/**: 使用 Path 谓词工厂,匹配所有请求路径以 /anything/ 开头的请求 (** 表示匹配任意多层路径)。
  • filters: 定义应用于这个路由的过滤器列表。这里注释掉了 StripPrefix,因为我们的目标 URI 已经包含了 anything 部分。如果你的目标服务是 http://httpbin.org,并且希望将 /anything/hello 转发到 http://httpbin.org/hello,那么就需要使用 StripPrefix=1 过滤器来移除 /anything 前缀。

步骤 4: 运行网关服务

运行 GatewayDemoApplication 类。如果一切正常,你会看到 Spring Boot 和 Netty 启动的日志。网关服务现在运行在 http://localhost:8080

步骤 5: 测试路由

使用浏览器或命令行工具(如 curl)测试你的网关:

bash
curl http://localhost:8080/anything/hello

你应该会看到 httpbin.org/anything 返回的 JSON 响应,其中包含了你的请求信息,证明请求成功通过网关并被转发到了 http://httpbin.org/anything

如果你尝试访问一个不匹配任何路由的路径,例如 http://localhost:8080/status,你会收到一个 404 错误响应,表示没有找到匹配的路由。

集成服务发现 (Eureka 示例)

在微服务架构中,服务实例通常是动态变化的,它们在启动时注册到服务注册中心(如 Eureka、Consul、Nacos)并在停止时注销。API Gateway 需要能够从服务注册中心获取服务的实际地址,并使用负载均衡的方式将请求分发到服务的多个实例。

Spring Cloud Gateway 可以很方便地与服务发现集成。

步骤 1: 添加服务发现客户端依赖

假设你使用 Eureka,添加 Eureka Client 依赖到 pom.xml (如果之前没有添加的话):

xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

步骤 2: 配置服务发现

application.yml 中配置 Eureka Server 的地址:

yaml
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/ # 你的Eureka Server地址

步骤 3: 修改路由 URI

将路由的目标 URI 修改为使用 lb:// 前缀加上服务的注册名称。lb:// 表示使用 Spring Cloud LoadBalancer 进行负载均衡。例如,如果你的用户服务注册名称是 user-service

“`yaml
server:
port: 8080

eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/

spring:
application:
name: api-gateway # 为网关服务指定一个名称
cloud:
gateway:
routes:
– id: user_service_route
uri: lb://user-service # 目标URI使用服务发现名称,lb://表示负载均衡
predicates:
– Path=/user/** # 匹配所有以/user/开头的路径
filters:
– StripPrefix=1 # 移除路径前缀 /user
“`

解释:

  • uri: lb://user-service: 请求将被转发到注册中心中名为 user-service 的服务的某个实例,并由 Spring Cloud LoadBalancer 选择具体的实例。
  • Path=/user/**: 客户端通过 /user/ 开头的路径访问网关。
  • StripPrefix=1: 将 /user 前缀剥离,这样 http://localhost:8080/user/info/123 会被转发到 user-service/info/123 路径。

现在,当你访问 http://localhost:8080/user/some/path 时,网关会查找 Eureka 中注册的 user-service 实例,选择一个健康的实例,然后将请求转发到该实例的 /some/path 路径。

使用过滤器 (Filters)

过滤器是 Spring Cloud Gateway 实现各种横切关注点的核心。你可以在路由配置中定义特定路由的过滤器,也可以创建全局过滤器应用于所有路由。

内置过滤器示例:添加请求头

我们可以在路由配置中添加一个过滤器,为转发到目标服务的请求添加一个自定义请求头。

yaml
spring:
cloud:
gateway:
routes:
- id: anything_route
uri: http://httpbin.org/anything
predicates:
- Path=/anything/**
filters:
- AddRequestHeader=X-Request-Foo, Bar # 添加请求头 X-Request-Foo: Bar

现在访问 http://localhost:8080/anything/hello,你会看到 httpbin.org 返回的响应中包含了 X-Request-Foo: Bar 这个请求头。

内置过滤器示例:限流 (RequestRateLimiter)

Spring Cloud Gateway 内置了基于 Redis 的限流过滤器 RequestRateLimiter。使用它需要引入 Spring Boot Redis 依赖,并配置 Redis 连接信息。

  1. 添加 Redis 依赖:
    xml
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
    </dependency>

    注意这里是 spring-boot-starter-data-redis-reactive,因为 Gateway 是响应式的。

  2. 配置 Redis 连接信息:
    yaml
    spring:
    data:
    redis:
    host: localhost
    port: 6379

  3. 配置限流过滤器:
    yaml
    spring:
    cloud:
    gateway:
    routes:
    - id: limited_route
    uri: http://httpbin.org/delay/3 # 转发到一个会延迟3秒的接口
    predicates:
    - Path=/limited/**
    filters:
    - name: RequestRateLimiter
    args:
    # key-resolver: "#{@hostAddrKeyResolver}" # 指定限流的key(这里使用默认的Principal)
    redis-rate-limiter.replenishRate: 1 # 每秒允许的请求数
    redis-rate-limiter.burstCapacity: 3 # 令牌桶容量,允许的突发请求数
    redis-rate-limiter.requestedTokens: 1 # 每个请求消耗的令牌数

    这里的配置表示:对于匹配 /limited/** 路径的请求,每秒最多允许 1 个请求通过 (replenishRate=1),最多允许 3 个突发请求 (burstCapacity=3)。

    key-resolver 用于定义限流的维度。Spring Cloud Gateway 提供了一些内置的 KeyResolver 实现,例如基于用户主体的 PrincipalNameKeyResolver、基于请求路径的 UriKeyResolver 或基于来源 IP 的 RemoteAddrKeyResolver。你也可以实现自己的 KeyResolver bean。默认情况下,如果没有配置 key-resolver,它可能会使用 PrincipalNameKeyResolver,但这需要你在 Spring Security 中配置认证。更常用的方式是基于 IP 或某个请求头。

    为了简单起见,我们可以配置一个基于 IP 的 KeyResolver bean:

    “`java
    package com.example.gatewaydemo;

    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
    import reactor.core.publisher.Mono;

    @Configuration
    public class RateLimiterConfig {

    // 基于请求IP地址的限流KeyResolver
    @Bean
    public KeyResolver remoteAddrKeyResolver() {
        return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
    }
    

    }
    ``
    然后在
    application.yml` 中引用这个 bean:

    yaml
    spring:
    cloud:
    gateway:
    routes:
    - id: limited_route
    uri: http://httpbin.org/delay/3
    predicates:
    - Path=/limited/**
    filters:
    - name: RequestRateLimiter
    args:
    key-resolver: "#{@remoteAddrKeyResolver}" # 引用上面定义的bean
    redis-rate-limiter.replenishRate: 1
    redis-rate-limiter.burstCapacity: 3
    redis-rate-limiter.requestedTokens: 1

    现在快速访问 http://localhost:8080/limited/test 多次,你会发现大部分请求会被 429 Too Many Requests 错误拒绝。

全局过滤器 (Global Filters)

全局过滤器应用于所有路由,非常适合实现认证、日志记录、监控等功能。你可以通过实现 GlobalFilter 接口并将其注册为 Spring Bean 来创建自定义全局过滤器。

“`java
package com.example.gatewaydemo.filter;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Component
public class LoggingFilter implements GlobalFilter, Ordered {

private static final Logger logger = LoggerFactory.getLogger(LoggingFilter.class);

@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    // 前置处理:记录请求信息
    String requestPath = exchange.getRequest().getPath().value();
    logger.info("Incoming request: {} {}", exchange.getRequest().getMethod(), requestPath);

    // 调用过滤器链的下一个过滤器或目标服务
    return chain.filter(exchange).then(Mono.fromRunnable(() -> {
        // 后置处理:记录响应信息
        logger.info("Outgoing response: {} {}", exchange.getResponse().getStatusCode(), requestPath);
    }));
}

@Override
public int getOrder() {
    // 过滤器的执行顺序,值越小越靠前执行
    // 建议自定义全局过滤器order值较大,以在内置过滤器之后执行
    return -1; // 示例:使其靠前执行
}

}
“`

将上面的类添加到你的项目中,它会作为一个全局过滤器应用于所有请求。getOrder() 方法决定了过滤器的执行顺序,负值表示更靠前执行。

监控和可观测性

Spring Cloud Gateway 与 Spring Boot Actuator 集成良好,提供了丰富的监控端点。添加 spring-boot-starter-actuator 依赖后,你可以通过 /actuator 端点查看网关的健康状态、路由信息、度量指标等。

例如,访问 /actuator/gateway/routes 可以看到所有配置的路由列表。访问 /actuator/metrics/gateway.requests 可以查看网关的请求指标。

结合 Micrometer 和你的监控系统(如 Prometheus + Grafana)可以构建强大的网关监控体系。

最佳实践

  • 路由 ID 命名: 使用有意义的路由 ID,方便识别和管理。
  • 配置管理: 将网关配置集中管理,可以使用 Spring Cloud Config 或其他配置中心。
  • 测试: 编写单元测试和集成测试来验证路由和过滤器的行为。
  • 限流和熔断: 在重要的路由上配置限流和熔断策略,保护后端服务。
  • 安全性: 在网关层面进行统一的认证和授权检查。
  • 日志和监控: 配置详细的日志记录和监控,便于排查问题和性能分析。
  • 性能优化: 注意过滤器的实现效率,避免在热点路径上执行耗时操作。

总结

Spring Cloud Gateway 是构建现代微服务架构中不可或缺的一部分。它提供了一个高性能、灵活且易于集成的 API Gateway 解决方案。通过本文的介绍,你应该对 Spring Cloud Gateway 的核心概念(路由、谓词、过滤器)、基本用法以及如何集成服务发现有了初步的了解。

掌握 Spring Cloud Gateway,你就能更好地管理微服务之间的通信,集中处理横切关注点,提升系统的可伸缩性、弹性和安全性。这只是入门,Spring Cloud Gateway 还有更多高级功能等待你去探索,例如 Java DSL 配置路由、自定义谓词和过滤器、WebSockets 支持等等。

接下来,建议你:

  1. 尝试构建一个更复杂的例子,包含多个路由和不同类型的过滤器。
  2. 将网关与你的实际微服务(如用户服务、订单服务)和服务注册中心集成。
  3. 探索内置的各种 Predicate 和 Filter,了解它们的功能和用法。
  4. 学习如何编写自定义 Predicate 和 Filter。

祝你在 Spring Cloud Gateway 的学习和实践中一切顺利!


发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部