SpringCloud - Gateway 网关

前言

该博客为Sentinel学习笔记,主要目的是为了帮助后期快速复习使用

学习视频:7小快速通关SpringCloud

辅助文档:SpringCloud快速通关

源码地址:cloud-demo

一、简介

官网:https://spring.io/projects/spring-cloud-gateway

Spring Cloud Gateway 是基于 Spring 生态系统(包括 Spring 6、Spring Boot 3 和 Project Reactor)构建的 API 网关 。它旨在为 API 提供简单而有效的路由,并处理诸如安全性、监控/指标和弹性等跨领域关注点。

1.1 主要特性

  • 构建于 Spring Framework 和 Spring Boot 之上 : 利用 Spring 强大的生态系统,确保与其他 Spring 项目的无缝集成。
  • 灵活的路由匹配: 能够基于任何请求属性(如路径、主机、方法等)定义路由规则。
  • 丰富的谓词和过滤器: 为每个路由提供特定的谓词和过滤器,方便实现复杂的路由逻辑和请求/响应的修改。
  • 断路器集成 : 与 Hystrix 等断路器库集成,增强系统的弹性和容错能力。
  • 服务发现集成 : 与 Spring Cloud DiscoveryClient 集成,实现动态路由和负载均衡。
  • 请求速率限制: 提供请求速率限制功能,保护后端服务免受流量突增的影响。
  • 路径重写: 支持对请求路径进行重写,满足不同的路由需求。

1.2 主要功能

1.3 版本选择

Spring Cloud Gateway 的两个版本主要区别在于它们的编程模型和适用场景:

  • Reactive Server(响应式版本)
    • 基于 WebFlux 框架,使用响应式编程模型。
    • 支持完全非阻塞的 I/O 操作,可以提高吞吐量和降低延迟。
    • 适用于构建高性能、高并发的微服务架构。
    • 与现代的响应式编程库(如 Reactor)兼容。
  • Server MVC(传统阻塞式版本)
    • 基于 Spring MVC 框架,使用传统的同步阻塞 I/O 模型。
    • 更适合与现有的 Spring MVC 应用程序集成。
    • 可能在高并发场景下的性能不如响应式版本。

推荐使用响应式Gateway

二、快速入门

2.1 需求

  1. 客户端发送 /api/order/** 转到 service-order
  2. 客户端发送 /api/product/** 转到 service-product
  3. 以上转发有负载均衡效果

2.2 创建模块

创建gateway模块,引入 spring-cloud-starter-gatewayspring-cloud-starter-alibaba-nacos-discoveryspring-cloud-starter-loadbalancer

xml 复制代码
 <!-- gateway网关 -->
 <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>

 <!--loadbalancer负载均衡-->
 <dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
 </dependency>

添加主程序GatewayMainApplication

java 复制代码
@SpringBootApplication
public class GatewayMainApplication {
    public static void main(String[] args) {
        SpringApplication.run(GatewayMainApplication.class, args);
    }
}

添加配置文件application.yml

yml 复制代码
# 服务端口
server:
  port: 80

spring:
  application:
    name: gateway # 服务名称
  cloud:
    nacos:
      server-addr: 127.0.0.1:8848 # nacos地址(默认)

这里可以启动微服务gatewayservice-orderservice-product,确保在Nacos控制台可以看到

2.3 改造微服务

创建 application-route.yml,为 service-orderservice-prduct 添加 /api基础路径

yml 复制代码
spring:
  cloud:
    gateway:
      routes:
        - id: order-route
          uri: lb://service-order  # lb(LoadBalance)代表负载均衡
          predicates: # 路径断言规则
            - Path=/api/order/** # 路径匹配
        - id: product-route
          uri: lb://service-product
          predicates:
            - Path=/api/product/**

并且需要在application.yml中启用 application-route.yml才能生效

yml 复制代码
spring:
  profiles:
    include: route # 启用route

2.4 测试

在浏览器中输入http://localhost/api/order/readDb,通过网关访问 service-order微服务的接口

2.5 基础原理

  1. 请求处理
    • 用户请求首先到达 Gateway
  2. 路由规则(Route)
    • 网关根据预定义的路由规则(Route)来决定请求的转发路径。
    • 每个路由规则包含三个主要部分:
      • Predicate 断言:用于匹配请求的条件,如路径、头信息、查询参数等。
      • URI 目的地:匹配请求后,请求将被转发到的目标地址。
      • Filter 过滤器:在请求转发前后执行的一系列过滤器链,用于修改请求/响应、进行认证等。
  3. 过滤器链(Filter Chain)
    • 请求在转发到目的地之前,会先经过一系列过滤器(Filter)的处理。
    • 过滤器可以执行各种任务,如日志记录、请求修改、认证授权等。
    • 过滤器按照定义的顺序执行,形成一个过滤器链。
  4. 转发请求
    • 经过过滤器链处理后,请求被转发到最终的目的地(服务提供者)。
  5. 响应处理
    • 目的地处理完请求后,将响应返回给网关。
    • 响应同样会经过过滤器链的处理,然后返回给用户。

三、Predicate - 断言

3.1 断言写法

3.1.1 短写法

快捷方式配置由过滤器名称识别,后跟等号 (=),后跟用逗号 () 分隔的参数值。

yml 复制代码
# 断言短写法
spring:
  cloud:
    gateway:
      routes:
        - id: order-route
          uri: lb://service-order  # lb(LoadBalance)代表负载均衡
          predicates: # 路径断言规则
            - Path=/api/order/** # 路径匹配

3.1.2 全写法

完全展开的参数看起来更像是带有名称/值对的标准 yaml 配置。通常,将有一个 name 键和一个 args 键。args 键是用于配置谓词或筛选条件的键值对的映射。

yml 复制代码
# 断言全写法
spring:
  cloud:
    gateway:
      routes:
        - id: order-route
          uri: lb://service-order  # lb(LoadBalance)代表负载均衡
          predicates:
            - name: Path # 路径断言
              args: # 参数
                pattern: /api/order/**  # 匹配路径
                matchTrailingSlash: true # 匹配尾部斜杠

3.2 断言工厂


3.3 自定义断言工厂

创建XxxRoutePredicateFactory继承AbstractRoutePredicateFactory

java 复制代码
/**
 * Vip断言工厂
 */
@Component
public class VipRoutePredicateFactory extends AbstractRoutePredicateFactory<VipRoutePredicateFactory.Config> {


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

    // 配置参数的顺序,为短写法准备
    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("param", "value");
    }

    // 断言逻辑
    @Override
    public Predicate<ServerWebExchange> apply(Config config) {
        return new GatewayPredicate() {
            @Override
            public boolean test(ServerWebExchange serverWebExchange) {
                // localhost/search?q=haha&user=jyh
                String first = serverWebExchange.getRequest().getQueryParams().getFirst(config.param);
                return StringUtils.hasText(first) && first.equals(config.value);
            }
        };
    }

    /**
     * 可以配置的参数
     */
    @Validated
    public static class Config {

        @NotEmpty
        private String param;

        @NotEmpty
        private String value;

        public @NotEmpty String getParam() {
            return param;
        }

        public void setParam(@NotEmpty String param) {
            this.param = param;
        }

        public @NotEmpty String getValue() {
            return value;
        }

        public void setValue(@NotEmpty String value) {
            this.value = value;
        }
    }
}

编写配置文件

yml 复制代码
spring:
  cloud:
    gateway:
      routes:
        - id: bing-route
          uri: https://cn.bing.com/ # 跳转地址
          predicates:
            - name: Path # 路径断言
              args:
                patterns: /search
            - name: Query # 查询参数断言
              args:
                param: q #  参数名
                regexp: haha  # 正则表达式
#            - Vip=user,jyh
            - name: Vip # 自定义Vip断言
              args:
                param: user
                value: jyh

四、Filter - 过滤器


4.1 路由重写 - RewritePath

yml 复制代码
spring:
  cloud:
    gateway:
      routes:
        - id: order-route
          uri: lb://service-order  # lb(LoadBalance)代表负载均衡
          predicates:
            - name: Path # 路径断言
              args: # 参数
                pattern: /api/order/**  # 匹配路径
                matchTrailingSlash: true # 匹配尾部斜杠
          filters:
            - RewritePath=/api/order/?(?<segment>.*), /$\{segment} # 重写路径

RewritePath=/api/order/?(?<segment>.*), /$\{segment}这个规则的意思是:

如果请求路径以 /api/order/ 开头,且后面有任意内容(如 /api/order/123),则会将路径重写为 /123 ,即删除 /api/order/ 部分,只保留后面的内容。

4.2 添加响应请求头 - AddResponseHeader

yml 复制代码
spring:
  cloud:
    gateway:
      routes:
        - id: order-route
          uri: lb://service-order  # lb(LoadBalance)代表负载均衡
          predicates:
            - name: Path # 路径断言
              args: # 参数
                pattern: /api/order/**  # 匹配路径
                matchTrailingSlash: true # 匹配尾部斜杠
          filters:
            - AddResponseHeader=X-Response-Abc, 123 # 添加响应请求头

4.3 默认filter - Default Filters

所有路由规则都生效默认filter里面配置的规则

yml 复制代码
spring:
  cloud:
    gateway:
      routes:
        - id: order-route
          uri: lb://service-order  # lb(LoadBalance)代表负载均衡
          predicates:
            - name: Path # 路径断言
              args: # 参数
                pattern: /api/order/**  # 匹配路径
                matchTrailingSlash: true # 匹配尾部斜杠
          filters:
            - RewritePath=/api/order/?(?<segment>.*), /$\{segment} # 重写路径
        - id: product-route
          uri: lb://service-product
          predicates:
            - Path=/api/product/**
          filters:
            - RewritePath=/api/product/?(?<segment>.*), /$\{segment} # 重写路径
      default-filters: # 默认过滤器
        - AddResponseHeader=X-Response-Abc, 123 # 添加响应头

4.4 全局filter - Global Filters

java 复制代码
/**
 * 全局过滤器 - 耗时统计
 * @author jiangyiheng
 */
@Slf4j
@Component
public class RtGlobalFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String uri = exchange.getRequest().getURI().toString();
        long start = System.currentTimeMillis();
        log.info("请求开始【{}】开始时间:{}", uri, start);
        // ======================以上是前置逻辑======================
        Mono<Void> filter = chain.filter(exchange)
                .doFinally((result)->{
                    // ======================以下是后置逻辑======================
                    long end = System.currentTimeMillis();
                    log.info("请求结束【{}】结束时间:{} ,耗时:{}ms", uri, end, end - start);
                }); // 放行

        return filter;
    }

    // 优先级
    @Override
    public int getOrder() {
        return 0;
    }
}

4.5 自定义过滤器

创建一个XxxGatewayFilterFactory类继承AbstractNameValueGatewayFilterFactory工厂

java 复制代码
/**
 * 自定义过滤器-令牌网关过滤器
 */
@Component
public class OneTokenGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {
    @Override
    public GatewayFilter apply(NameValueConfig config) {
        return new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                // 每次响应前,添加一个一次性令牌,支持 uuid,jwt等各种格式
                return chain.filter(exchange).then(Mono.fromRunnable(()->{
                    ServerHttpResponse response = exchange.getResponse();
                    String value = config.getValue();
                    if ("uuid".equalsIgnoreCase(value)) {
                        value = UUID.randomUUID().toString();
                    }
                    if ("jwt".equalsIgnoreCase(value)) {
                        value = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";
                    }
                    response.getHeaders().add(config.getName(), value);
                }));
            }
        };
    }
}

五、CORS - 跨域处理

5.1 全局跨域

yml 复制代码
spring:
  cloud:
    gateway:
      globalcors:
        cors-configurations:
          '[/**]':
            allowed-origin-patterns: '*' # 允许所有跨域
            allowed-methods: '*' # 允许所有请求方式
            allowed-headers: '*' # 允许所有头

5.2 局部跨域

yml 复制代码
spring:
  cloud:
    gateway:
      routes:
      - id: cors_route
        uri: https://example.org
        predicates:
        - Path=/service/**
        metadata:
          cors:
            allowedOrigins: '*'
            allowedMethods:
              - GET
              - POST
            allowedHeaders: '*'
            maxAge: 30

面试题 :微服务之间的调用经过网关吗?

微服务之间的调用一般不会经过网关 。网关主要用于处理外部请求,负责路由、认证、限流等功能。而微服务之间的通信通常通过服务发现负载均衡直接进行,不需要经过网关。然而,在某些特殊情况下,如果需要统一管理或控制,微服务之间的调用也可能通过网关。

相关推荐
TTDreamTT10 分钟前
SpringBoot十二、SpringBoot系列web篇之过滤器Filte详解
spring boot
一只爱撸猫的程序猿4 小时前
构建一个简单的智能文档问答系统实例
数据库·spring boot·aigc
crud4 小时前
Spring Boot 3 整合 Swagger:打造现代化 API 文档系统(附完整代码 + 高级配置 + 最佳实践)
java·spring boot·swagger
a_157153249864 小时前
SpringCloud学习笔记-4
笔记·学习·spring cloud
鳄鱼杆5 小时前
服务器 | Centos 9 系统中,如何部署SpringBoot后端项目?
服务器·spring boot·centos
千|寻5 小时前
【画江湖】langchain4j - Java1.8下spring boot集成ollama调用本地大模型之问道系列(第一问)
java·spring boot·后端·langchain
保持学习ing5 小时前
Spring注解开发
java·深度学习·spring·框架
techzhi5 小时前
SeaweedFS S3 Spring Boot Starter
java·spring boot·后端
酷爱码6 小时前
Spring Boot 整合 Apache Flink 的详细过程
spring boot·flink·apache
异常君6 小时前
Spring 中的 FactoryBean 与 BeanFactory:核心概念深度解析
java·spring·面试