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 上。
- Spring Cloud Gateway 通过 Gateway Handler Mapping 找到与请求相匹配的路由,将其发送给 Gateway Web Handler。
- Gateway Web Handler 通过指定的过滤器链(Filter Chain),将请求转发到实际的服务节点中,执行业务逻辑返回响应结果。
- 过滤器之间用虚线分开是因为过滤器可能会在转发请求之前(pre)或之后(post)执行业务逻辑。
- 过滤器(Filter)可以在请求被转发到服务端前,对请求进行拦截和修改,例如参数校验、权限校验、流量监控、日志输出以及协议转换等。
- 过滤器可以在响应返回客户端之前,对响应进行拦截和再处理,例如修改响应内容或响应头、日志输出、流量监控等。
- 响应原路返回给客户端。
-
总而言之,客户端发送到 Spring Cloud Gateway 的请求需要通过一定的匹配条件,才能定位到真正的服务节点。在将请求转发到服务进行处理的过程前后(pre 和 post),我们还可以对请求和响应进行一些精细化控制。
-
Predicate 就是路由的匹配条件,而 Filter 就是对请求和响应进行精细化控制的工具。有了这两个元素,再加上目标 URI,就可以实现一个具体的路由了。
9.3 Predicate 断言
-
Spring Cloud Gateway 通过 Predicate 断言来实现 Route 路由的匹配规则。简单点说,Predicate 是路由转发的判断条件,请求只有满足了 Predicate 的条件,才会被转发到指定的服务上进行处理。
-
使用 Predicate 断言需要注意以下 3 点:
-
常见的 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:动态路由、过滤器使用
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/123799.html