Spring Cloud Gateway 从入门到实战

前言

在微服务架构中,前端直接调用众多微服务会面临 IP / 端口硬编码、无法负载均衡、权限无法统一控制 等问题。Spring Cloud Gateway 作为 Spring 官方推出的网关组件,性能远超 Zuul,是微服务生态的统一入口,承担路由转发、权限校验、流量控制、日志监控等核心职责。


一、Gateway 核心介绍

1.1 为什么要用 Gateway?

前端(Vue / 小程序 / App)直接调用微服务存在致命问题:

  1. 客户端维护大量 IP / 端口,硬编码严重
  2. 无法实现负载均衡
  3. 权限、日志、限流无法统一处理
  4. 跨域、安全难以统一管控

引入网关后架构:

复制代码
前端 → Gateway(统一入口) → 微服务集群

1.2 什么是 Spring Cloud Gateway?

Spring Cloud Gateway 是 Spring 官方基于 WebFlux(Reactor+NIO) 开发的新一代网关,用于替代 Zuul,核心功能:

  • 路由转发
  • 断言匹配
  • 过滤器链
  • 限流 / 安全 / 监控

性能对比:Gateway RPS 是 Zuul 1.x 的 1.6 倍。

1.3 核心依赖注意

XML 复制代码
<!-- 网关核心依赖 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

⚠️ 严禁引入:spring-boot-starter-webGateway 基于 WebFlux(非阻塞),Web 依赖是 Servlet(阻塞),会冲突导致启动失败。

二、Gateway 工程搭建

2.1 pom.xml

XML 复制代码
<dependencies>
    <!-- Nacos 服务发现 -->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <!-- Gateway 网关 -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>
</dependencies>

2.2 application.yml

XML 复制代码
server:
  port: 9527

spring:
  application:
    name: api-gateway
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.61.132:8848 # Nacos 地址

2.3 启动类

java 复制代码
@SpringBootApplication
@EnableDiscoveryClient // 开启服务发现
public class ApiGatewayApp {
    public static void main(String[] args) {
        SpringApplication.run(ApiGatewayApp.class, args);
    }
}

三、路由配置(核心)

路由 = ID + URI + Predicates + Filters

3.1 方式一:固定地址路由(硬编码)

java 复制代码
spring:
  cloud:
    gateway:
      routes:
        - id: sentinel-consumer # 唯一标识
          uri: http://127.0.0.1:8080 # 固定地址
          predicates:
            - Path=/consumer/** # 路径匹配

3.2 方式二:服务名负载均衡(推荐)

lb:// 表示从 Nacos 获取服务列表,自动负载均衡

java 复制代码
spring:
  cloud:
    gateway:
      routes:
        - id: sentinel-consumer
          uri: lb://sentinel-consumer # 服务名
          predicates:
            - Path=/consumer/**

四、断言工厂 Predicates

断言:满足条件才路由,可组合使用。

4.1 常用内置断言工厂

断言 说明
Path=/xxx/** 路径匹配
Method=GET 请求方法匹配
After = 时间 在指定时间之后
Before = 时间 在指定时间之前
Between = 时间,时间 时间区间
RemoteAddr=IP 段 IP 白名单
Header=name,\d+ 请求头匹配
Cookie=name,xxx Cookie 匹配
Query=name,xxx 请求参数匹配

示例:

java 复制代码
predicates:
  - Path=/consumer/**
  - Method=GET
  - After=2024-12-31T23:59:59.999+08:00[Asia/Shanghai]

4.2 自定义断言工厂(实战:年龄判断)

4.2.1 编写断言工厂
java 复制代码
@Component
public class AgeRoutePredicateFactory 
        extends AbstractRoutePredicateFactory<AgeRoutePredicateFactory.Config> {

    public AgeRoutePredicateFactory() {
        super(Config.class);
    }

    // 配置文件参数顺序
    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("minAge", "maxAge");
    }

    @Override
    public Predicate<ServerWebExchange> apply(Config config) {
        return exchange -> {
            String ageStr = exchange.getRequest().getQueryParams().getFirst("age");
            if (StringUtils.isNotBlank(ageStr)) {
                int age = Integer.parseInt(ageStr);
                return age > config.minAge && age < config.maxAge;
            }
            return false;
        };
    }

    @Data
    public static class Config {
        private int minAge;
        private int maxAge;
    }
}
4.2.2 使用自定义断言
java 复制代码
predicates:
  - Path=/consumer/**
  - Age=18,60 # 年龄在 18~60 之间才放行

五、过滤器 Filter

过滤器用于修改请求 / 响应,分为:

  • 局部过滤器(单个路由)
  • 全局过滤器(所有路由)

5.1 常用内置过滤器

过滤器 作用
StripPrefix=1 去掉路径前缀
AddRequestHeader 添加请求头
AddResponseHeader 添加响应头
RewritePath 重写路径
RequestRateLimiter 限流

示例:去掉前缀

java 复制代码
filters:
  - StripPrefix=1 # 去掉第一层路径

5.2 自定义过滤器工厂(实战:日志耗时)

5.2.1 编写过滤器工厂
java 复制代码
@Component
public class LogGatewayFilterFactory 
        extends AbstractGatewayFilterFactory<LogGatewayFilterFactory.Config> {

    public LogGatewayFilterFactory() {
        super(Config.class);
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("enable");
    }

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            if (!config.enable) {
                return chain.filter(exchange);
            }
            long start = System.currentTimeMillis();
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                long time = System.currentTimeMillis() - start;
                System.out.println("接口耗时:" + time + "ms");
            }));
        };
    }

    @Data
    public static class Config {
        private boolean enable;
    }
}
5.2.2 使用自定义过滤器
java 复制代码
filters:
  - StripPrefix=1
  - Log=true # 开启耗时日志

六、全局过滤器 GlobalFilter(统一鉴权)

全局过滤器对所有路由生效,常用于:登录校验、黑名单、日志。

6.1 编写登录全局过滤器

java 复制代码
@Component
public class LoginFilter implements GlobalFilter, Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 获取 token
        String token = exchange.getRequest().getQueryParams().getFirst("token");

        if (StringUtils.isBlank(token)) {
            ServerHttpResponse response = exchange.getResponse();
            Map<String, Object> map = new HashMap<>();
            map.put("code", 401);
            map.put("msg", "请先登录");

            byte[] bytes = JSON.toJSONString(map).getBytes(StandardCharsets.UTF_8);
            DataBuffer buffer = response.bufferFactory().wrap(bytes);
            response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
            return response.writeWith(Mono.just(buffer));
        }

        return chain.filter(exchange);
    }

    // 优先级,数字越小越先执行
    @Override
    public int getOrder() {
        return 0;
    }
}

6.2 测试

  • 无 token:返回 401 未登录
  • 带 token:正常路由转发

七、常见问题总结

  1. Gateway 不能引入 spring-boot-starter-web
  2. 服务名路由必须加 lb://
  3. 路径匹配尽量使用 /
  4. 过滤器顺序通过 getOrder () 控制
  5. 自定义断言 / 过滤器工厂类名必须以 XxxRoutePredicateFactory / XxxGatewayFilterFactory 结尾

八、本文总结

  1. Gateway 是微服务统一入口,解决硬编码、无负载均衡、无统一管控问题
  2. 路由支持 固定地址 与 服务名负载均衡
  3. 断言用于路由条件匹配
  4. 过滤器用于请求 / 响应增强
  5. 全局过滤器实现统一登录鉴权
相关推荐
Rain的Java大神之路4 天前
如何快速上传10G文件
java·spring boot·redis·后端·mysql·spring cloud·面试
需要8265 天前
MySQL 索引 B+ 树与“查询为什么变慢了
java·spring boot·spring·spring cloud·tomcat
需要8265 天前
Arthas 一个命令排查线上问题
java·jvm·spring·spring cloud
Raas1005 天前
MAI Gateway(魔芋企业级AI网关)功能全解:AI网关支持流式输出吗?一文看懂AI网关能力矩阵
大数据·人工智能·gateway·mai gateway·企业级网关
苏渡苇6 天前
Spring Insight 里如何把 Span 画成瀑布时间线
后端·spring·spring cloud·springboot·监控·apm
叶总没有会6 天前
Nacos—注册中心
spring cloud·微服务
毅炼6 天前
不写多语言 SDK,怎么让 Python、Go、Node 服务接入注册中心?
java·后端·系统架构·gateway
苏渡苇7 天前
Spring Insight 里如何把 Span 收成一条链路
spring boot·spring·spring cloud·系统监控·apm
叶总没有会7 天前
微服务--分布式基础
java·spring·spring cloud·微服务
孙启超8 天前
【AI开发之Rust】第 7 课:错误处理 —— panic、Result 与 `?`
人工智能·分布式·后端·爬虫·spring cloud·架构·rust