Spring Cloud 微服务实战(四):每个请求都直连服务?你的集群缺一个 Gateway 门卫


个人主页:夏天拐跑了西瓜
专栏传送门:《大模型应用开发》、《Spring 生态全家桶体系化实战》
学习方向:Java 后端|AI‑Agent 大模型应用开发爱好者
⭐人生格言:路虽远,行则将至


封面图

本文是「Spring Cloud 微服务实战」系列的第四篇。网关是微服务集群的统一入口,所有外部请求都要经过它转发与过滤。本文先讲清楚网关的定位与 Gateway、Nginx、Zuul 的区别,再实战路由规则配置、lb:// 负载均衡路由、常用断言与过滤器,最后手写一个全局过滤器实现统一的参数校验,帮你搭建一个安全可控的集群入口。

系列文章导航

  • 《Spring Cloud 微服务实战(一):还在硬编码服务地址?Nacos 注册中心从零搭建》
  • 《Spring Cloud 微服务实战(二):改个配置还要重启服务?Nacos 配置中心 + OpenFeign 实战》
  • 《Spring Cloud 微服务实战(三):一个服务挂了,凭什么拖垮整个系统?Sentinel 限流熔断实战》
  • 《Spring Cloud 微服务实战(四):每个请求都直连服务?你的集群缺一个 Gateway 门卫》(本篇)

一、Gateway

1.1 网关介绍

先明确网关的定位:它是所有后端服务的统一入口。外部请求要访问订单、库存或其他任何服务,都必须先经过网关,由网关转发到目标服务。
在这里插入图片描述

入口的安全策略做到位,后端服务的安全性就有了第一道保障。因此鉴权、限流、安全防护这类横切逻辑,通常都收敛在网关层统一处理。

网关的实现方案大致可以分为两类:

1、面向用户的网关:以 Nginx 为代表

  • Nginx 的并发能力极强,用户请求先到达 Nginx,再由 Nginx 转发到后端服务;
  • 但 Nginx 不属于 Spring Cloud 生态,与注册中心的整合成本较高,也难以用 Java 编写具体的业务逻辑。

2、后端入口网关:以 Spring Cloud Gateway 为代表(Netflix 开源的 Zuul 也属于此类)

  • Gateway 的并发能力相对 Nginx 较弱,但它是 Spring Cloud 生态的原生组件,可以直接对接注册中心,基于服务名获取实例元数据;
  • Gateway 还提供了丰富的 Filter 机制,可以在请求进入与响应返回的各个阶段插入自定义逻辑。

在展开 Gateway 之前,有必要了解一下它取代 Zuul 的原因。

Zuul 基于 Tomcat 容器,采用的是传统的 Servlet IO 模型:每进入一个请求,Web 服务就从线程池中分配一个线程进行处理。在并发不高时没有问题,但并发升高后会暴露两个缺陷:

  • 每个线程都占用内存资源,高并发下线程数激增,内存消耗随之上涨;
  • 线程过多时,大量 CPU 资源被消耗在线程上下文切换上。

因此在高并发场景下,网关不应该采用"一请求一线程"的传统 Servlet IO 模型。

针对这个问题,Gateway 选择了完全不同的技术路线。


Gateway 底层基于 Spring Framework 提供的 WebFlux 组件,用响应式模型替换了旧的 Servlet IO 模型:由少量线程(称为 Loop 线程)负责处理所有请求与响应的 IO 事件。

而具体的业务逻辑,则基于 WebFlux 使用的 Reactive Streams 异步编程模型处理。


处理请求的 Loop 线程对应 Reactor 模型中的 Reactor 线程;基于该模型实现的高性能通信框架代表是 Netty,此时 Reactor 线程即 Netty 中的 EventLoop 线程。


以上底层细节不必深究,只需理解结论:WebFlux 处理请求可以搭配 Netty 作为通信层。

Netty 提供了当前性能第一梯队的 IO 能力,再结合 Reactive Streams 的异步非阻塞处理,使得 Gateway 的性能远高于早期的 Zuul,并发能力更强。

补充:虽然 Zuul 新版本也切换到了基于 Netty 的 Reactor 模型,但 Spring Cloud 已停止对其维护,转而推出了自己的 Gateway,因此新项目直接选择 Gateway 即可。

建议:初学阶段不必深入 IO 模型、Netty 与异步编程的细节,等微服务整体落地后,再系统学习这些内容。

1.2 Gateway介绍

Gateway 作为网关,职责依然是请求的转发与过滤。其核心特点如下:

  • 基于 Spring Framework 5、Project Reactor 和 Spring Boot 2.0 构建;
  • 路由能够匹配请求携带的任何报文信息(请求头、参数、Cookie 等均可作为匹配条件);
  • 谓词(Predicate)与过滤器(Filter)与具体路由绑定;
  • 支持断路器集成(Hystrix / Sentinel);
  • 集成 Spring Cloud DiscoveryClient,可直接对接 Nacos 等注册中心;
  • 谓词与过滤器编写方式灵活,支持配置文件与 Java 代码两种方式;
  • 内置请求限流过滤器;
  • 支持路径重写,可在转发前与响应前修改各类报文。

此外,还需要了解 Gateway 的三个核心术语:

  • Route(路由):网关的核心机制,由 ID、目标 URI、谓词集合与过滤器集合组成。当请求满足谓词匹配后,依次经过过滤器链,最终转发到目标 URI;
  • Predicate(断言/谓词):用于匹配 HTTP 请求中携带的报文信息,可基于请求头、请求参数、Cookie 等多种条件作为匹配规则;
  • Filter(过滤器):可在向下游发送请求之前或收到响应之后,对请求或响应进行修改。

下图是 Gateway 底层请求流转的整体过程:

image.png

整个流程涉及三个角色:客户端、Gateway、目标服务。

完整流转过程:

  1. 客户端请求发送到 Gateway;
  2. Gateway 基于 HandlerMapping 判断请求能否匹配到某条路由的谓词,若不匹配则返回 404;
  3. 匹配成功后,请求交给 Web Handler 处理,先经过过滤器链的前置过滤;
  4. 请求转发到目标服务,目标服务处理完成后将响应返回给 Gateway;
  5. 响应再经过过滤器链的后置过滤,最终返回给客户端。

1.3 Gateway初体验

1、先启动目标服务,确认其接口可以单独访问:

image.png

2、创建 gateway 工程。

3、导入依赖:

<dependencies>
    <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>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-loadbalancer</artifactId>
    </dependency>
</dependencies>

4、创建启动类:

@SpringBootApplication
@EnableDiscoveryClient
public class GatewayStarterApp {

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

5、编写配置文件,开启基于服务名的默认路由:

spring:
  application:
    name: gateway
  cloud:
    gateway:
      discovery:
        locator:
          # 开启默认的路由规则,会根据注册中心中的服务名作为路径实现路由规则
          enabled: true
server:
  port: 80

6、测试效果:

image.png

以 http://localhost/order/order/info 为例,路径的三段含义如下:

http://localhost /order /order/info

  • http://localhost: 访问的是 gateway 服务;
  • /order: 路由的目标是订单服务;
  • /order/info: 最终访问订单服务的 /order/info 接口。

1.4 自定义路由配置

自定义路由的本质是:基于断言定义请求进入后的匹配与转发规则。

1.4.1 yml方式

spring:
  application:
    name: gateway
  cloud:
    gateway:
      discovery:
        locator:
          # 开启默认的路由规则,会根据注册中心中的服务名作为路径实现路由规则
          enabled: false
      routes:
        - id: path_route
          uri: http://localhost:9090/
          predicates:
            - Path=/order/**
          # http://localhost/order/order/info  路由到    http://localhost:9090/order/order/info
          # http://localhost/order/info   路由到    http://localhost:9090/order/info

1.4.2 配置类方式

除配置文件外,路由也可以通过 Java 配置类以编程方式定义:

import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class GatewayRoutesConfig {

    @Bean
    public RouteLocator pathRoute(RouteLocatorBuilder routeLocatorBuilder) {
        //1、基于routeLocatorBuilder获取构建route的Builder
        RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
        //2、设置route
        return routes.route("path_route",route -> route.path("/order/**").uri("http://localhost:9090/")).build();
        /*
        #        - id: path_route
        #          uri: http://localhost:9090/
        #          predicates:
        #            - Path=/order/**
        */
    }
}

1.4.3 负载均衡

1.3 节中基于服务名的路由与负载均衡是默认路由机制自带的。使用自定义路由时,若需要从注册中心获取实例并实现负载均衡,只需将 uri 改为 lb://服务名 即可。

配置类写法:

import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class GatewayRoutesConfig {

    @Bean
    public RouteLocator pathRoute(RouteLocatorBuilder routeLocatorBuilder) {
        //1、基于routeLocatorBuilder获取构建route的Builder
        RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
        //2、设置route                                                         将uri修改为lb://服务名即可
        return routes.route("path_route",route -> route.path("/order/**").uri("lb://order")).build();
    }

}

yml 写法:

spring:
  cloud:
    gateway:
      routes:
        - id: path_route
          #  这个位置  ,lb代表负载均衡
          uri: lb://order
          predicates:
            - Path=/order/**

1.5 断言

1.5.1 断言介绍

Gateway 内置了丰富的断言类型。每条路由可以配置多个断言,多个断言之间是"与"的关系——请求必须满足所有断言才会被路由到目标服务。

常用断言一览:

  1. After:只匹配指定时间之后的请求;
  2. Before:只匹配指定时间之前的请求;
  3. Between:只匹配指定时间范围内的请求;
  4. Cookie:请求携带的 Cookie 需匹配指定的正则表达式;
  5. Header:请求头需匹配指定的正则表达式;
  6. Host:匹配请求来源的主机(域名);
  7. Method:匹配请求方式(GET/POST 等);
  8. Path:匹配请求路径;
  9. Query:匹配请求参数;
  10. RemoteAddr:匹配请求来源的 IP 地址;
  11. Weight:按权重路由,参数为分组 group 与权重 weight,常用于灰度发布;
  12. XForWardedRemoteAddr:匹配请求头中的 X-Forwarded-For 或 RemoteAddr,识别经过代理的真实请求来源。

下面选取几个典型断言实测。

1.5.2 断言测试

After:只允许在指定时间之后的请求被路由:

spring:
  cloud:
    gateway:
      routes:
        - id: path_route
          uri: lb://order
          predicates:
            - Path=/order/**
#            - After=2025-05-16T19:30:25.789+08:00[Asia/Shanghai]
#            - After=2026-05-16T19:30:25.789+08:00[Asia/Shanghai]

Header:请求头必须携带指定名称且值匹配正则:

spring:
  cloud:
    gateway:
      routes:
        - id: path_route
          uri: lb://order
          predicates:
            - Path=/order/**
            - Header=X-Request-Id, \d+

Method:限定请求方式:

spring:
  cloud:
    gateway:
      routes:
        - id: path_route
          uri: lb://order
          predicates:
            - Path=/order/**
            - Method=GET

其余断言的配置格式与此类似,参照官方文档即可举一反三。

1.6 Filter

Filter 作用于请求转发的前后阶段:请求到达 Gateway 并经断言匹配后,转发前后会依次经过过滤器链。

过滤器分为两部分:

  • Gateway 内置过滤器:开箱即用,通过配置开启;
  • 自定义过滤器:实现接口编写业务逻辑。

1.6.1 Gateway自带的Filter

Gateway 内置的过滤器数量较多,日常开发中以下四个最常用,掌握它们即可覆盖大部分场景。

1.6.1.1 AddRequestHeader

在请求转发到目标服务之前,追加一个请求头:

spring:
  cloud:
    gateway:
      routes:
        - id: path_route
          uri: lb://order
          predicates:
            - Path=/order/**
          filters:
            - AddRequestHeader=X-Request-red, blue
1.6.1.2 AddRequestParameter

在请求转发到目标服务之前,追加一个请求参数:

spring:
  cloud:
    gateway:
      routes:
        - id: path_route
          uri: lb://order
          predicates:
            - Path=/order/**
          filters:
            - AddRequestHeader=X-Request-red, blue
            - AddRequestParameter=red, blue
1.6.1.3 AddResponseHeader

在响应返回给客户端之前,追加一个响应头:

spring:
  cloud:
    gateway:
      routes:
        - id: path_route
          uri: lb://order
          predicates:
            - Path=/order/**
          filters:
            - AddRequestHeader=X-Request-red, blue
            - AddRequestParameter=red, blue
            - AddResponseHeader=X-Response-Red, Blue
1.6.1.4 StripPrefix

请求匹配断言后,转发前可截掉请求路径开头的 N 段前缀。常用于网关路径前缀与下游服务路径不一致的场景:

spring:
  cloud:
    gateway:
      routes:
        - id: path_route
          uri: lb://order
          predicates:
            - Path=/order/**
          filters:
            - AddRequestHeader=X-Request-red, blue
            - AddRequestParameter=red, blue
            - AddResponseHeader=X-Response-Red, Blue
            - StripPrefix=1
          # http://localhost/order/order/info  路由到    http://localhost:9090/order/info
          # http://localhost/order/info   路由到    http://localhost:9090/info
1.6.1.5 DefaultFilter

前面介绍的过滤器都作用于单条路由。如果某些过滤器需要对所有路由统一生效,可以使用 default-filters 配置:

spring:
  cloud:
    gateway:
      default-filters:
        - AddRequestHeader=X-Request-red, blue
        - AddRequestParameter=red, blue
        - AddResponseHeader=X-Response-Red, Blue
        - StripPrefix=1

1.6.2 自定义过滤器

当内置过滤器无法满足需求时,可以自定义过滤器。实现方式很简单:创建一个类实现对应接口,重写方法并加入业务逻辑即可。

需要实现两个接口:

  • GlobalFilter:重写 filter 方法,通过 exchange 可以获取请求报文与响应报文的信息,在方法内部可完成限流、鉴权等操作:
    • 返回 exchange.getResponse().setComplete():拦截请求;
    • 返回 chain.filter(exchange):放行请求。
  • Ordered:指定多个过滤器之间的执行顺序,返回值越小,优先级越高。

下面实现一个参数校验过滤器:要求所有请求必须携带 key 参数且值非空,否则返回 400。

package com.study.filters;

import com.alibaba.nacos.common.utils.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
 * 实现Gateway的自定义过滤器,并且实现参数校验。
 */
@Component
public class ParameterInvalidateFilter implements GlobalFilter, Ordered {

    // 要求必须传递一个参数,   key=value, 要求value不允许为空,也不允许为空串,否则400的错误
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 具体的逻辑在这些。
        String value = exchange.getRequest().getQueryParams().getFirst("key");
        // 判断
        if(StringUtils.isEmpty(value)){
            System.out.println("参数异常!!!");
            exchange.getResponse().setStatusCode(HttpStatus.BAD_REQUEST);
            // 拦截住,直接不往下走Filter了。
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }

    @Override
    // 返回的数值越小,优先级越高。
    public int getOrder() {
        return 0;
    }
}

二、总结

本文讲解了微服务集群的统一入口 Gateway,核心收获如下:

  • Gateway 基于 WebFlux + Netty 的异步模型,相比 Zuul 的传统 Servlet 模型并发能力更强。
  • 掌握 Route、Predicate、Filter 三大核心概念及请求在 Gateway 内的完整流转流程。
  • 路由规则支持 yml 与配置类两种配置方式,lb://服务名 即可对接注册中心实现负载均衡路由。
  • 熟悉 Path、Method、Header、After 等常用断言,以及 AddRequestHeader、AddRequestParameter、AddResponseHeader、StripPrefix、DefaultFilter 等常用过滤器。
  • 通过实现 GlobalFilter + Ordered 自定义全局过滤器,统一完成鉴权、参数校验等入口逻辑。

至此,Spring Cloud 微服务链路的核心组件(注册发现、配置中心、远程调用、限流熔断、网关)已全部讲解完毕,后续可以自行扩展链路追踪(Sleuth + Zipkin)与 DevOps 自动化部署等内容。

感兴趣的同学记得 点赞 + 关注,第一时间获取更新通知!

更多推荐