💨 作者:
laker
,因为喜欢LOL滴神faker
,又是NBA湖人队🏀(laker
)粉丝儿(主要是老詹的粉丝儿),本人又姓李,故取笔名:laker
❤️喜欢分享自己工作中遇到的问题和解决方案,以及一些读书笔记和心得分享。
🌰本人创建了微信公众号【Java大厂面试官】,用于和大家交流分享
🏰 个人微信【lakernote】,加作者备注下暗号:cv之道
。
文章目录
简介
Spring Cloud Gateway 是Spring Cloud的一个全新的API网关项目,目的是为了替换掉Zuul1。Gateway可以与Spring Cloud Discovery Client(如Eureka)、Ribbon、Hystrix等组件配合使用,实现路由转发、负载均衡、熔断等功能,并且Gateway还内置了限流过滤器,实现了限流的功能。
Gateway基于Spring 5、Spring boot 2和Reactor构建,使用Netty作为运行时环境,比较完美的支持异步非阻塞编程。Netty使用非阻塞的IO,线程处理模型建立在主从Reactors多线程模型上。其中Boss Group轮询到新连接后与Client建立连接,生成NioSocketChannel,将channel绑定到Worker;Worker Group轮询并处理Read、Write事件。
微服务架下,服务之间容易形成网状的调用关系,这种网状的调用关系不便管理和维护,这种场景下API网关应运而生。作为后端服务的入口,API网关在微服务架构中尤其重要,在对外部系统提供API入口的要求下,API网关应具备路由转发、负载均衡、限流熔断、权限控制、轨迹追踪和实时监控等功能。
目前,很多微服务都基于的Spring Cloud生态构建。Spring Cloud生态为我们提供了两种API网关产品,分别是Netflix开源的Zuul1和Spring自己开发的Spring Cloud Gateway(下边简称为Gateway)。Spring Cloud以Finchley版本为分界线,Finchley版本发布之前使用Zuul1作为API网关,之后更推荐使用Gateway。
虽然Netflix已经在2018年5月开源了Zuul2,但是Spring Cloud已经推出了Gateway,并且在github上表示没有集成Zuul2的计划。所以从Spring Cloud发展的趋势来看,Gateway代替Zuul是必然的。
Spring Cloud Gateway 依赖 Spring Boot 和 Spring WebFlux,基于 Netty 运行。它不能在传统的 servlet 容器中工作,也不能构建成 war 包。
该工具提供了开箱即用的路由机制,经常在微服务应用程序中使用,作为将多个服务隐藏在单个外观后面的一种方式。
官方文档:
- https://cloud.spring.io/spring-cloud-gateway/reference/html
- https://spring.io/projects/spring-cloud-gateway
- https://github.com/spring-cloud/spring-cloud-gateway
SpringCloud Gateway 特征
SpringCloud官方,对SpringCloud Gateway 特征介绍如下:
-
建立在Spring Framework 5,Project Reactor和Spring Boot 2.0之上
-
能够匹配任何请求属性上的路由。
-
谓词和过滤器特定于路由。
-
断路器集成。
-
Spring Cloud DiscoveryClient集成
-
易于编写的谓词和过滤器
-
请求速率限制
-
路径改写
从以上的特征来说,和Zuul的特征差别不大。SpringCloud Gateway和Zuul主要的区别,还是在底层的通信框架上。
在 Spring Cloud Gateway 中有如下几个核心概念需要我们了解:
(1)Route(路由):
Route 是网关的基础元素,由 ID、目标 URI、断言、过滤器组成。当请求到达网关时,由 Gateway Handler Mapping 通过断言进行路由匹配(Mapping),当断言为真时,匹配到路由。
(2)Predicate(断言):
Predicate 是 Java 8 中提供的一个函数。输入类型是 Spring Framework ServerWebExchange。它允许开发人员匹配来自 HTTP 的请求,例如请求头或者请求参数。简单来说它就是匹配条件。
(3)Filter(过滤器):
和Zuul的过滤器在概念上类似,可以使用它拦截和修改请求,并且对上游的响应,进行二次处理。过滤器为org.springframework.cloud.gateway.filter.GatewayFilter类的实例。
Spring Cloud Gateway 工作原理
Spring Cloud Gateway 的工作原理跟 Zuul 的差不多,最大的区别就是 Gateway 的 Filter 只有 pre 和 post 两种。下面我们简单了解一下 Gateway 的工作原理图。
客户端向 Spring Cloud Gateway 发出请求,如果请求与网关程序定义的路由匹配,则该请求就会被发送到网关 Web 处理程序,此时处理程序运行特定的请求过滤器链。
过滤器之间用虚线分开的原因是过滤器可能会在发送代理请求的前后执行逻辑。所有 pre 过滤器逻辑先执行,然后执行代理请求;代理请求完成后,执行 post 过滤器逻辑。
路由配置
配置文件方式
spring:
cloud:
gateway:
routes:
- id: path_route
uri: https://www.baidu.com
predicates:
- Path=/spring_cloud
各字段含义如下:
-
id:我们自定义的路由 ID,保持唯一
-
uri:目标服务地址
-
predicates:路由条件,Predicate 接受一个输入参数,返回一个布尔值结果。该接口包含多种默认方法来将 Predicate 组合成其他复杂的逻辑(比如:与,或,非)。
当你访问 http://localhost:8080/spring_cloud 的时候就会转发到 https://www.baidu.com/spring_cloud。
代码方式
与上面配置文件方式效果一致
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route(p -> p
.path("/spring_cloud")
.uri("https://www.baidu.com"))
.id("myOtherID"))
.build();
}
路由断言工厂
Spring Cloud Gateway使用Spring WebFlux HandlerMapping基础结构匹配路由。
它还包括许多内置的路由断言工厂。所有这些谓词都与HTTP请求的不同属性匹配。多个路由谓词工厂可以通过逻辑“和”进行组合。
路由匹配既可以通过编程方式应用,也可以通过配置属性文件使用其他类型的Route Predicate Factories来应用。
过滤器工厂
路由过滤器使修改传入的HTTP请求或传出的HTTP响应成为可能。
Spring Cloud Gateway包括许多内置的WebFilter工厂以及创建自定义过滤器的可能性。
注册中心相结合
Spring Cloud Gateway可以轻松地与服务发现和注册表库(例如Eureka Server和Consul)集成:
@Configuration
@EnableDiscoveryClient
public class GatewayDiscoveryConfiguration {
@Bean
public DiscoveryClientRouteDefinitionLocator
discoveryClientRouteLocator(DiscoveryClient discoveryClient) {
return new DiscoveryClientRouteDefinitionLocator(discoveryClient);
}
}
负载均衡
该LoadBalancerClientFilter寻找一个URI中使用的交换特性属性ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR。
如果URL具有lb方案(例如lb://baeldung-service
),它将使用Spring Cloud LoadBalancerClient将名称*(即baeldung-service)*解析为实际的主机和端口。
未经修改的原始URL放置在ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR属性中。
监控
Spring Cloud Gateway利用Actuator API,这是一个著名的Spring-Boot库,它提供了一些现成的服务来监视应用程序。
一旦安装并配置了Actuator API,就可以通过访问/gateway/
端点来可视化网关监视功能。
入门示例
现在,我们将使用路径断言创建一个简单的示例,将Spring Cloud Gateway用作代理服务器。
pom依赖
我们使用最新版本,构建方式可参考 如何正确选择Spring Boot与Spring Cloud的版本以及对应关系
这里我直接给出来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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>2020.0.0</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
代码
现在,我们在application.yml
文件中创建一个简单的路由配置:
spring:
cloud:
gateway:
routes:
- id: laker
uri: http://httpbin.org
predicates:
- Path=/get
management:
endpoints:
web:
exposure:
include: "*"
以及网关应用程序代码:
@SpringBootApplication
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
应用程序启动后,我们可以访问URL:http://localhost:8080/actuator/gateway/routes/laker
以检查所有创建的路由配置:
{
"predicate": "Paths: [/get], match trailing slash: true",
"route_id": "laker",
"filters": [],
"uri": "http://httpbin.org:80",
"order": 0
}
我们看到相对网址:/get
被配置为路由, 因此点击网址http://localhost/get
,我们将被重定向到http://httpbin.org:80
。
附录
反应式编程结合同步非阻塞IO或者异步非阻塞IO是目前网络编程框架的主流方向,最好要跟上主流的步伐掌握这些框架的使用,有能力最好成为它们的贡献者。
目前常见的反应式编程框架有:
- Reactor和
RxJava2
,其中Reactor
在后端的JVM应用比较常见,RxJava2
在安卓编写的APP客户端比较常见。 Reactor-Netty
,这个是基于Reactor
和Netty
封装的。Spring-WebFlux
和Spring-Cloud-Gateway
,其中Spring-Cloud-Gateway
依赖Spring-WebFlux
,而Spring-WebFlux
底层依赖于Reactor-Netty
。
选用Spring-Cloud-Gateway
不仅仅是为了使用新的技术,更重要的是它的性能有了不俗的提升,基准测试项目spring-cloud-gateway-bench的结果如下:
代理组件(Proxy) | 平均交互延迟(Avg Latency) | 平均每秒处理的请求数(Avg Requests/Sec) |
---|---|---|
Spring Cloud Gateway | 6.61ms | 32213.38 |
Linkered | 7.62ms | 28050.76 |
Zuul(1.x) | 12.56ms | 20800.13 |
None(直接调用) | 2.09ms | 116841.15 |
Spring Cloud 相关系列文章目录
网关服务
Spring Cloud Gateway
QQ群【837324215】
关注我的公众号【Java大厂面试官】,回复:常用工具、资源等关键词(更多关键词,关注后注意提示信息)获取更多免费资料。
公众号也会持续输出高质量文章,和大家共同进步。