当前位置: 首页 > article >正文

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

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

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

添加配置文件application.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基础路径

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才能生效

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 短写法

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

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

3.1.2 全写法

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

# 断言全写法
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

/**
 * 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;
        }
    }
}

编写配置文件

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

在这里插入图片描述

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

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里面配置的规则

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

/**
 * 全局过滤器 - 耗时统计
 * @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工厂

/**
 * 自定义过滤器-令牌网关过滤器
 */
@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 全局跨域

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

5.2 局部跨域

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

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


http://www.kler.cn/a/542500.html

相关文章:

  • 计算机毕业设计——Springboot的简历系统
  • Flink KafkaConsumer offset是如何提交的
  • 前端实现在PDF上添加标注(1)
  • SpringBoot启动流程简略版
  • ElementUI的<el-image>组件引用网络图片加载失败
  • 今日AI和商界事件(2025-02-10)
  • 常用电路(过压保护、电流/电压采集)
  • 开源AI智能名片2+1链动模式S2B2C商城小程序在实体店与线上营销中的应用探索
  • 教程 | MySQL 基本指令指南(附MySQL软件包)
  • 最新PHP盲盒商城系统源码 晒图+免签+短信验证+在线回收 ThinkPHP框架
  • MySQL——CRUD
  • Java爬虫:高效获取1688商品详情的“数字猎人”
  • 林语堂 | 生活的智慧在于逐渐澄清滤除那些不重要的杂质,而保留最重要的部分
  • AH比价格策略源代码
  • HALCON 数据结构
  • Vision Transformer:打破CNN垄断,全局注意力机制重塑计算机视觉范式
  • 青少年编程与数学 02-009 Django 5 Web 编程 04课题、应用创建
  • 本地部署的drawio绘图存储调研
  • 数据结构--迷宫问题
  • 在nodejs中使用RabbitMQ(三)Routing、Topics、Headers
  • Flink-DataStream API
  • Redis Sentinel(哨兵)模式介绍
  • 力扣动态规划-26【算法学习day.120】
  • DeepSeek API 调用 - Spring Boot 实现
  • 【经验分享】Linux 系统安装后内核参数优化
  • C++中函数的调用