SpringCloud-24-Gateway:Spring Cloud API网关组件介绍

32 篇文章 3 订阅
2 篇文章 0 订阅

9 Gateway:Spring Cloud API网关组件

  • 在微服务架构中,系统由多个微服务组成,而这些服务可能部署在不同机房、不同地区、不同域名下。这种情况下,客户端(例如浏览器、手机、软件工具等)想要直接请求这些服务,就需要知道它们具体的地址信息,例如 IP 地址、端口号等。

  • 这种客户端直接请求服务的方式存在以下问题:

    • 当服务数量众多时,客户端需要维护大量的服务地址,这对于客户端来说,是非常繁琐复杂的。
    • 在某些场景下可能会存在跨域请求的问题。
    • 身份认证的难度大,每个微服务需要独立认证。
  • 我们可以通过 API 网关来解决这些问题。

9.1 API 网关
  • API 网关是一个搭建在客户端和微服务之间的服务,我们可以在 API 网关中处理一些非业务功能的逻辑,例如权限验证、监控、缓存、请求路由等。

  • API 网关就像整个微服务系统的门面一样,是系统对外的唯一入口。客户端会先将请求发送到 API 网关,然后由 API 网关根据请求的标识信息将请求转发到微服务实例。
    在这里插入图片描述

  • 对于服务数量众多、复杂度较高、规模比较大的系统来说,使用 API 网关具有以下好处:

    • 客户端通过 API 网关与微服务交互时,客户端只需要知道 API 网关地址即可,而不需要维护大量的服务地址,简化了客户端的开发。
    • 客户端直接与 API 网关通信,能够减少客户端与各个服务的交互次数。
    • 客户端与后端的服务耦合度降低。
    • 节省流量,提高性能,提升用户体验。
    • API 网关还提供了安全、流控、过滤、缓存、计费以及监控等 API 管理功能。
  • 常见的 API 网关实现方案主要有以下 5 种:

    • Spring Cloud Gateway
    • Spring Cloud Netflix Zuul(下一小节介绍)
    • Kong
    • Nginx+Lua
    • Traefik
9.2 Spring Cloud Gateway
  • Spring Cloud Gateway 是 Spring Cloud 团队基于 Spring 5.0、Spring Boot 2.0 和 Project Reactor 等技术开发的高性能 API 网关组件。

  • Spring Cloud Gateway 旨在提供一种简单而有效的途径来发送 API,并为它们提供横切关注点,例如:安全性,监控/指标和弹性。

  • Spring Cloud Gateway 是基于 WebFlux 框架实现的,而 WebFlux 框架底层则使用了高性能的 Reactor 模式通信框架 Netty。

  • Spring Cloud Gateway 核心概念: 最主要的功能就是路由转发,而在定义转发规则时主要涉及了以下三个核心概念,如下表。

核心概念描述
Route(路由)网关最基本的模块。它由一个 ID、一个目标 URI、一组断言(Predicate)和一组过滤器(Filter)组成。
Predicate(断言)路由转发的判断条件,我们可以通过 Predicate 对 HTTP 请求进行匹配,例如请求方式、请求路径、请求头、参数等,如果请求与断言匹配成功,则将请求转发到相应的服务。
Filter(过滤器)过滤器,我们可以使用它对请求进行拦截和修改,还可以使用它对上文的响应进行再处理。

注意:其中 Route 和 Predicate 必须同时声明。

  • Spring Cloud Gateway 具有以下特性:

    • 基于 Spring Framework 5、Project Reactor 和 Spring Boot 2.0 构建。
    • 能够在任意请求属性上匹配路由。
    • predicates(断言) 和 filters(过滤器)是特定于路由的。
    • 集成了 Hystrix 熔断器。
    • 集成了 Spring Cloud DiscoveryClient(服务发现客户端)。
    • 易于编写断言和过滤器。
    • 能够限制请求频率。
    • 能够重写请求路径。
  • Spring Cloud Gateway 工作流程如下图。
    在这里插入图片描述

  • Spring Cloud Gateway 工作流程说明如下:

    1. 客户端将请求发送到 Spring Cloud Gateway 上。
    2. Spring Cloud Gateway 通过 Gateway Handler Mapping 找到与请求相匹配的路由,将其发送给 Gateway Web Handler。
    3. Gateway Web Handler 通过指定的过滤器链(Filter Chain),将请求转发到实际的服务节点中,执行业务逻辑返回响应结果。
    4. 过滤器之间用虚线分开是因为过滤器可能会在转发请求之前(pre)或之后(post)执行业务逻辑。
    5. 过滤器(Filter)可以在请求被转发到服务端前,对请求进行拦截和修改,例如参数校验、权限校验、流量监控、日志输出以及协议转换等。
    6. 过滤器可以在响应返回客户端之前,对响应进行拦截和再处理,例如修改响应内容或响应头、日志输出、流量监控等。
    7. 响应原路返回给客户端。
  • 总而言之,客户端发送到 Spring Cloud Gateway 的请求需要通过一定的匹配条件,才能定位到真正的服务节点。在将请求转发到服务进行处理的过程前后(pre 和 post),我们还可以对请求和响应进行一些精细化控制。

  • Predicate 就是路由的匹配条件,而 Filter 就是对请求和响应进行精细化控制的工具。有了这两个元素,再加上目标 URI,就可以实现一个具体的路由了。

9.3 Predicate 断言
  • Spring Cloud Gateway 通过 Predicate 断言来实现 Route 路由的匹配规则。简单点说,Predicate 是路由转发的判断条件,请求只有满足了 Predicate 的条件,才会被转发到指定的服务上进行处理。

  • 使用 Predicate 断言需要注意以下 3 点:

    • Route 路由与 Predicate 断言的对应关系为“一对多”,一个路由可以包含多个不同断言。
    • 一个请求想要转发到指定的路由上,就必须同时匹配路由上的所有断言。
    • 当一个请求同时满足多个路由的断言条件时,请求只会被首个成功匹配的路由转发。
      在这里插入图片描述
  • 常见的 Predicate 断言如下表(假设转发的 URI 为 http://localhost:8001)。

    断言示例说明
    Path- Path=/dept/list/**当请求路径与 /dept/list/** 匹配时,该请求才能被转发到 http://localhost:8001 上。
    Before- Before=2021-10-20T11:47:34.255+08:00[Asia/Shanghai]在 2021 年 10 月 20 日 11 时 47 分 34.255 秒之前的请求,才会被转发到 http://localhost:8001 上。
    After- After=2021-10-20T11:47:34.255+08:00[Asia/Shanghai]在 2021 年 10 月 20 日 11 时 47 分 34.255 秒之后的请求,才会被转发到 http://localhost:8001 上。
    Between- Between=2021-10-20T15:18:33.226+08:00[Asia/Shanghai],2021-10-20T15:23:33.226+08:00[Asia/Shanghai]在 2021 年 10 月 20 日 15 时 18 分 33.226 秒 到 2021 年 10 月 20 日 15 时 23 分 33.226 秒之间的请求,才会被转发到 http://localhost:8001 服务器上。
    Cookie- Cookie=name,cloud.zk.com携带 Cookie 且 Cookie 的内容为 name=cloud.zk.com 的请求,才会被转发到 http://localhost:8001 上。
    Header- Header=X-Request-Id,\d+请求头上携带属性 X-Request-Id 且属性值为整数的请求,才会被转发到 http://localhost:8001 上。
    Method- Method=GET只有 GET 请求才会被转发到 http://localhost:8001 上。
  • 下面通过一个实例,来测试下 Predicate 是如何使用的。

  • 在基础工程 spring-cloud-microservice 下创建一个名为 microservice-cloud-gateway-8015 的 maven 模块,并在其 pom.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>spring-cloud-microservice</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>microservice-cloud-gateway-8015</artifactId>

    <dependencies>
        <!--devtools 开发工具 这个热部署重启得更快-->
        <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-devtools</artifactId>
             <scope>runtime</scope>
             <optional>true</optional>
         </dependency>
        <!--logback -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!--springboot-test-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- 修改后立即生效,热部署 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>springloaded</artifactId>
            <version>1.2.8.RELEASE</version>
        </dependency>
        <!--日志-->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
        </dependency>
        <!--Spring Cloud Eureka 客户端依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            <version>2.2.10.RELEASE</version>
        </dependency>
        <!--特别注意:在 gateway 网关服务中不能引入 spring-boot-starter-web 的依赖,否则会报错-->
        <!-- Spring cloud gateway 网关依赖-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>
  • 在microservice-cloud-gateway-8015 的类路径(/resources 目录)下,新建一个配置文件 application.yml,配置内容如下。
server:
  port: 8015
spring:
  application:
    name: microServiceCloudGateway #微服务名称,对外暴漏的微服务名称,十分重要
  cloud:
    gateway:     #网关路由配置
      routes:
        - id: provider_deptlist_route #路由 id,没有固定规则,但唯一,建议与服务名对应
          uri: http://localhost:8001
          predicates:
            #以下是断言条件,必选全部符合条件
           - Path=/dept/**   #断言,路径匹配 注意:Path 中 P 为大写
           - Method=Get   #只能时 GET 请求时,才能访问
      #将 microservice-cloud-provider-dept-8001 提供的服务隐藏起来,不暴露给客户端,只给客户端暴露 API 网关的地址 9527


#eureka配置,Spring cloud 自定义服务名称和 ip 地址
eureka:
  client:
    fetch-registry: true
    register-with-eureka: true
    service-url:
      #defaultZone: http://localhost:7001/eureka/  #这个地址是 7001注册中心在 application.yml 中暴露出来额注册地址 (单机版)
      defaultZone: http://eurekaserver7001.com:7001/eureka/,http://eurekaserver7002.com:7002/eureka/,http://eurekaserver7003.com:7003/eureka/ #将服务注册到 Eureka Server 集群
  instance:
    instance-id: microservice-cloud-gateway-8015 #自定义服务名称描述信息
    prefer-ip-address: true           #显示访问路径的 ip 地址
    hostname: microservice-cloud-gateway
    #zookeeper需要配置那些服务service被扫描,eureka则是将整个服务包注册进去了

ribbon:
  eager-load:
    enabled: true  #关闭懒加载
  # 指的是建立连接后从服务器读取到可用资源所用的时间
  ReadTimeout: 6000
  # 指的是建立连接所用的时间,适用于网络状态正常的情况下,两端连接所用的时间
  ConnectTimeout: 6000
  MaxAutoRetriesNextServer: 1
  MaxAutoRetries: 1
# spring cloud 使用 Spring Boot actuator 监控完善信息
# Spring Boot 2.50对 actuator 监控屏蔽了大多数的节点,只暴露了 heath 节点,本段配置(*)就是为了开启所有的节点
# zuul中包含actuator依赖
management:
  endpoints:
    web:
      exposure:
        include: health,info,hystrix.stream  #应包含的端点 ID 或 '' 全部,* 在yaml 文件属于关键字,所以需要加双引号
        #include的值也可以改成*,但建议还是最小暴露原则,用啥开启啥
  info:
    env:
      enabled: true   #启用配置里的info开头的变量

info:             #配置一些服务描述信息,监控信息页面显示,,可以判断服务是否正常
  app.name: microservice-cloud-zuul-8010
  company.name: cloud.zk.com
  auth: zk
  email: 123@qq.com
  build.aetifactId: @project.artifactId@
  build.version: @project.version@

#网关路径http://cloud.zk.com:8010/microservicecloudproviderdepthystrix/dept/getInfo/Hystrix/200/1
  • 以上配置中,我们在 spring.cloud.gateway.routes 下使用 predicates 属性,定义了以下两个断言条件:
cloud:
    gateway:     #网关路由配置
      routes:
        - id: provider_deptlist_route #路由 id,没有固定规则,但唯一,建议与服务名对应
          uri: http://localhost:8001
          predicates:
            #以下是断言条件,必选全部符合条件
           - Path=/dept/**   #断言,路径匹配 注意:Path 中 P 为大写
           - Method=Get   #只能时 GET 请求时,才能访问

注意:- id,- Path 前的-不能丢掉,且Path=/dept/**中的=也不能替换为:否则yml无法解析,具体可见源码RouteDefinition和PredicateDefinition类的构造器。出现org.springframework.web.server.ResponseStatusException: 404 NOT_FOUND错误

public RouteDefinition(String text) {
        int eqIdx = text.indexOf(61);
        if (eqIdx <= 0) {
            throw new ValidationException("Unable to parse RouteDefinition text '" + text + "', must be of the form name=value");
        } else {
            this.setId(text.substring(0, eqIdx));
            String[] args = StringUtils.tokenizeToStringArray(text.substring(eqIdx + 1), ",");
            this.setUri(URI.create(args[0]));

            for(int i = 1; i < args.length; ++i) {
                this.predicates.add(new PredicateDefinition(args[i]));
            }

        }
    }
public PredicateDefinition(String text) {
    int eqIdx = text.indexOf(61);
    if (eqIdx <= 0) {
        throw new ValidationException("Unable to parse PredicateDefinition text '" + text + "', must be of the form name=value");
    } else {
        this.setName(text.substring(0, eqIdx));
        String[] args = StringUtils.tokenizeToStringArray(text.substring(eqIdx + 1), ",");

        for(int i = 0; i < args.length; ++i) {
            this.args.put(NameUtils.generateName(i), args[i]);
        }

    }
}

在这里插入图片描述

  • 只有当外部(客户端)发送到 microservice-cloud-gateway-8015 的 HTTP 请求同时满足以上所有的断言时,该请求才会被转发到指定的服务端中(即 http://localhost:8001)。
  • 在 microservice-cloud-gateway-8015 的主启动类上,使用 @EnableEurekaClient 注解开启 Eureka 客户端功能,代码如下。
package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableEurekaClient
public class MicroserviceCloudGatewayApplication {

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

}
  • 依次启动 Eureka 服务注册中心(集群)、microservice-cloud-provider-dept-8001 以及 microservice-cloud-gateway-8015,使用浏览器访问“http://localhost:8015/dept/list”,结果如下图。
    在这里插入图片描述
下一篇:SpringCloud-25-Gateway:动态路由、过滤器使用
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值