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 自定义返回
相关推荐
智者知已应修善业8 小时前
【51单片机89C51及74LS273、74LS244组成】2022-5-28
c++·经验分享·笔记·算法·51单片机
奋斗的小乌龟8 小时前
langchain4j笔记-06
笔记
·醉挽清风·8 小时前
学习笔记—MySQL—库表操作
笔记·学习·mysql
weixin_4514315610 小时前
【学习笔记】微博视频页面ajax请求与响应数据分析
笔记·学习·音视频
快乐得小萝卜12 小时前
OpenVLA 论文精读笔记
笔记
叶~小兮13 小时前
K8S进阶核心综合学习笔记(持久化存储+特殊容器+调度管理)
笔记·学习·kubernetes
Skylwn13 小时前
保姆级教程之将 GitHub Models 接入 NewAPI
笔记·github
脆皮炸鸡75514 小时前
库制作与原理~动态链接
linux·开发语言·经验分享·笔记·学习方法
nnsix14 小时前
设计模式 - 工厂模式 笔记
笔记·设计模式
一只机电自动化菜鸟17 小时前
一建机电备考笔记(36) 焊接技术—焊接方法与工艺(含考频+题型)
笔记·学习·职场和发展·生活·学习方法