Spring Cloud Gateway 完整版笔记

一、核心概念(速记)

  1. Gateway:Spring 官方异步非阻塞网关,性能远超 Zuul,SpringCloud Alibaba 标配网关
  2. 三大核心路由 (Route) + 断言 (Predicate) + 过滤器 (Filter)
  3. 关键禁忌不能引入 spring-boot-starter-web(会与 WebFlux 冲突)
  4. 作用:统一入口、负载均衡、权限校验、日志、限流、熔断、请求 / 响应增强

二、搭建 Gateway 工程(完整代码)

1. pom.xml(必选依赖)

xml

复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>springcloud_parent</artifactId>
        <groupId>com.hg</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>api_gateway</artifactId>

    <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>

        <!-- ↓↓↓ 绝对不能加 ↓↓↓ -->
        <!--<dependency>-->
        <!--    <groupId>org.springframework.boot</groupId>-->
        <!--    <artifactId>spring-boot-starter-web</artifactId>-->
        <!--</dependency>-->
    </dependencies>
</project>

2. application.yml(基础配置)

yaml

复制代码
spring:
  application:
    name: api-gateway
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.209.129:8848
server:
  port: 9527

3. 启动类

java

运行

复制代码
package com.hg;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

三、路由配置(两种方式)

方式 1:固定 IP + 端口路由

yaml

复制代码
spring:
  cloud:
    gateway:
      routes:
        - id: sentinel-consumer
          uri: http://localhost:80  # 固定地址
          predicates:
            - Path=/consumer/**

方式 2:服务名 + 负载均衡路由(推荐)

yaml

复制代码
spring:
  cloud:
    gateway:
      routes:
        - id: sentinel-consumer
          uri: lb://sentinel-consumer  # lb = 负载均衡
          predicates:
            - Path=/consumer/**
          filters:
            - StripPrefix=1  # 去掉第一层路径

四、断言 Predicate(完整代码)

1. 常用内置断言

yaml

复制代码
predicates:
  - Path=/consumer/**              # 路径匹配
  - Method=GET                     # 请求方式
  - After=2025-01-01T00:00:00+08:00[Asia/Shanghai]
  - RemoteAddr=192.168.1.0/24      # IP 段
  - Header=X-Id, \d+               # 请求头
  - Query=age, \d+                 # 请求参数

2. 自定义断言工厂(年龄判断)

java

运行

复制代码
package com.hg.predicate;

import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;

import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

@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.isNotEmpty(ageStr)) {
                int age = Integer.parseInt(ageStr);
                return age > config.getMinAge() && age < config.getMaxAge();
            }
            return false;
        };
    }

    @Data
    @NoArgsConstructor
    public static class Config {
        private int minAge;
        private int maxAge;
    }
}

使用配置

yaml

复制代码
predicates:
  - Path=/consumer/**
  - Age=18,60

五、过滤器 Filter(全套代码)

1. 自定义局部过滤器(接口耗时统计)

java

运行

复制代码
package com.hg.filter;

import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

import java.util.Arrays;
import java.util.List;

@Component
public class LogGatewayFilterFactory extends AbstractGatewayFilterFactory<LogGatewayFilterFactory.Config> {

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

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            if (!config.isParamValue()) {
                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");
            }));
        };
    }

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

    @Data
    @NoArgsConstructor
    public static class Config {
        private boolean paramValue;
    }
}

使用配置

yaml

复制代码
filters:
  - Log=true

2. 自定义全局过滤器(登录 Token 校验)

java

运行

复制代码
package com.hg.filter;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

@Component
public class LoginFilter implements GlobalFilter, Ordered {

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

        if (token == null || token.isBlank()) {
            ServerHttpResponse response = exchange.getResponse();
            Map<String, Object> map = new HashMap<>();
            map.put("code", 401);
            map.put("msg", "未登录,请先携带token访问");

            byte[] bytes = new byte[0];
            try {
                bytes = new ObjectMapper().writeValueAsString(map).getBytes(StandardCharsets.UTF_8);
            } catch (JsonProcessingException e) {
                throw new RuntimeException(e);
            }
            DataBuffer buffer = response.bufferFactory().wrap(bytes);
            response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
            return response.writeWith(Mono.just(buffer));
        }
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

六、Sentinel 网关限流(完整代码)

1. pom.xml 加入依赖

xml

复制代码
<!-- Sentinel 核心 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

<!-- Gateway + Sentinel 整合 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>

2. application.yml

yaml

复制代码
spring:
  cloud:
    sentinel:
      transport:
        dashboard: 127.0.0.1:8080

3. 自定义限流返回结果

java

运行

复制代码
package com.hg.exception;

import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class GatewaySentinelConfig {

    @PostConstruct
    public void init() {
        BlockRequestHandler handler = (exchange, t) -> {
            Map<String, Object> map = new HashMap<>();
            map.put("code", 200);
            map.put("msg", "请求过于频繁,请稍后再试!");
            return ServerResponse.status(HttpStatus.OK)
                    .contentType(MediaType.APPLICATION_JSON)
                    .body(BodyInserters.fromValue(map));
        };
        GatewayCallbackManager.setBlockHandler(handler);
    }
}

七、全套测试地址

  1. 正常访问:http://localhost:9527/consumer/getUserById/1?token=123
  2. 年龄断言:http://localhost:9527/consumer/getUserById/1?token=123&age=20
  3. 未登录拦截:http://localhost:9527/consumer/getUserById/1
  4. 限流触发:快速刷新即可看到 Sentinel 自定义返回
相关推荐
qeen872 小时前
【算法笔记】二分查找与二分答案
c语言·c++·笔记·学习·算法·二分
脆皮炸鸡7552 小时前
进程的程序替换
linux·经验分享·笔记·vim·学习方法
萌新小码农‍2 小时前
机器学习概述 学习笔记day2
笔记·学习·机器学习
daanpdf3 小时前
大一《中国近代史纲要》题库及答案PDF知识点整理笔记
笔记·pdf
曦月逸霜3 小时前
区块链技术与应用学习笔记(持续更新中)
笔记·学习·区块链
中屹指纹浏览器4 小时前
跨终端跨平台指纹浏览器的环境一致性技术架构与落地实践
经验分享·笔记
码农的小菜园4 小时前
Android的Locale学习笔记
android·笔记·学习
zhangrelay5 小时前
Lubuntu 26.04移动系统使用配置测试记录与引导修复备注
笔记·学习
jy022688795 小时前
线性代数-笔记
笔记·线性代数