该项目提供了一个用于在Spring WebFlux之上构建API网关的库。Spring Cloud Gateway旨在提供一种简单而有效的方式来路由到API并为它们提供交叉关注点,例如:安全性、监控和弹性。
Spring Cloud Gateway需要Spring Boot和Spring Webflux提供的Netty运行时环境。它不适用于传统的Servlet容器或将应用构建为WAR包。如果强制使用传统的Servlet来处理请求:
应用启动时会报错:
Spring Webflux is missing from the classpath, which is required for Spring Cloud Gateway at this time. Please add spring-boot-starter-webflux dependency.
Spring MVC found on classpath, which is incompatible with Spring Cloud Gateway at this time. Please remove spring-boot-starter-web dependency.
类路径中缺少Spring Webflux,此时Spring Cloud Gateway需要它。请添加spring-boot-starter-webflux依赖项。
在classpath上发现Spring MVC,此时与Spring Cloud Gateway不兼容。请删除spring-boot-starter-web依赖项。
重要概念:
-
路由(Route):路由是网关的基本构建模块。它由ID、目标URI、Predicate集合和Filter集合定义。如果聚合Predicate为真,则匹配路由。
-
断言(Predicate):输入类型是Spring framework ServerWebExchange。这允许开发人员匹配来自HTTP请求的任何内容,例如请求头或请求参数。
代码注释翻译插件Translation:
-
过滤器(Filter):使用特定工厂构建的Spring framework GatewayFilter实例,可以在发送请求之前或之后修改请求和响应。
Spring Cloud Gateway特性:
- 基于Spring framework 5、Project Reactor和Spring Boot 2.0。
- 能够匹配任何请求属性的路由。
- 特定于路由的断言和过滤器。
- 集成Circuit Breaker。
- 集成Spring Cloud DiscoveryClient。
- 断言和过滤器易于编写。
- 请求速率限制。
- 路径重写。
Spring Cloud Gateway工作方式(图来自官网):
客户端向Spring Cloud Gateway发出请求。如果Gateway Handler Mapping确定请求与路由匹配,则将其发送到Gateway Web Handler。此处理程序通过特定于请求的过滤器链,将请求转换成代理请求。过滤器被虚线分隔的原因是过滤器可能在发送代理请求之前或之后执行逻辑。执行所有pre过滤器逻辑,然后发出代理请求。代理请求得到响应后,执行所有post过滤逻辑。
一个父module和两个子module(nacos module提供服务,gateway module实现网关)。
父module的pom.xml:
nacos module4.0.0 com.kaven alibaba pom 1.0-SNAPSHOT Spring Cloud Alibaba nacos gateway 8 8 Hoxton.SR9 2.2.6.RELEASE org.springframework.boot spring-boot-starter-parent 2.3.2.RELEASE org.springframework.cloud spring-cloud-dependencies ${spring-cloud-version} pom import com.alibaba.cloud spring-cloud-alibaba-dependencies ${spring-cloud-alibaba-version} pom import
pom.xml:
4.0.0 com.kaven alibaba 1.0-SNAPSHOT nacos 8 8 com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery org.springframework.boot spring-boot-starter-web
application.yml:
server:
port: 8080
spring:
application:
name: nacos
cloud:
nacos:
discovery:
server-addr: 192.168.1.197:9000
接口定义:
package com.kaven.alibaba.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class MessageController {
@GetMapping("/message")
public String getMessage() {
return "hello kaven, this is nacos";
}
}
启动类:
package com.kaven.alibaba;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class NacosApplication {
public static void main(String[] args) {
SpringApplication.run(NacosApplication.class);
}
}
gateway module
pom.xml:
4.0.0 com.kaven alibaba 1.0-SNAPSHOT gateway 8 8 org.springframework.cloud spring-cloud-starter-gateway com.alibaba.cloud spring-cloud-starter-alibaba-nacos-discovery
application.yml:
server:
port: 8085
spring:
application:
name: gateway
cloud:
nacos:
server-addr: 192.168.1.197:9000
gateway:
routes:
- id: nacos
uri: http://localhost:8080
predicates:
- Path=/message
启动类:
package com.kaven.alibaba;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}
启动这两个module,Nacos的服务列表就会出现这两个服务。
路由断言工厂Spring Cloud Gateway匹配路由作为Spring WebFlux HandlerMapping基础功能的一部分。Spring Cloud Gateway包括许多内置的路由断言工厂。所有这些断言都匹配HTTP请求的不同属性。可以通过逻辑and组合多个路由断言工厂。
AfterAfter路由断言工厂接受一个日期时间,该断言匹配该日期时间之后的请求。生成这个日期时间的示例代码如下所示:
package com.kaven.alibaba;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
public class Test {
public static void main(String[] args) {
ZonedDateTime nowDateTime = LocalDateTime.now()
.atZone(ZoneId.systemDefault());
System.out.println(nowDateTime);
ZonedDateTime updateDateTime = LocalDateTime.now()
.plusMonths(1)
.minusDays(1).minusHours(1)
.atZone(ZoneId.systemDefault());
System.out.println(updateDateTime);
ZonedDateTime hardCodeDateTime = LocalDateTime.
of(2021, 12, 17, 18, 54, 17, 0)
.atZone(ZoneId.systemDefault());
System.out.println(hardCodeDateTime);
}
}
输出:
2021-12-17T19:15:40.297+08:00[Asia/Shanghai] 2022-01-16T18:15:40.298+08:00[Asia/Shanghai] 2021-12-17T18:54:17+08:00[Asia/Shanghai]
表示年、月、日,时、分、秒、纳秒(不是毫秒)以及时区。
- After=2021-12-17T19:30:00+08:00[Asia/Shanghai]
在指定日期时间之前访问接口会直接响应404。
指定日期时间之后,接口就可以正常访问了。
Before路由断言工厂接受一个日期时间。此断言匹配在该日期时间之前发生的请求。由于和After路由断言工厂类似,这里就不再演示。
- Before=2021-12-17T19:38:00.129+08:00[Asia/Shanghai]Between
Between路由断言工厂接受两个日期时间。此断言匹配发生在datetime1之后和datetime2之前的请求。Between路由断言工厂与上面两种路由断言工厂类似,这里也不再演示。
- Between=2021-12-17T19:38:00.129+08:00[Asia/Shanghai],2021-12-17T19:42:00.129+08:00[Asia/Shanghai]
datetime1参数指定的日期时间必须在datetime2参数指定的日期时间之前。
cookiecookie路由断言工厂接受两个参数,即cookie名称和一个正则表达式。此断言匹配具有给定名称且值与正则表达式匹配的cookie的请求。
- cookie=kaven,*kaven*
使用Postman来测试,请求没有cookie:
请求没有匹配的cookie:
请求有匹配的cookie:
断言配置是一个List类型,因此可以配置多个断言,当聚合断言(聚合在信息科学中是指对有关的数据进行内容挑选、分析、归类,最后分析得到人们想要的结果,看完这篇博客就应该理解这里使用聚合这个词是比较合适的,因为断言里面有and也有or)为真时,才匹配路由。
- cookie=username,.*kaven.*
- cookie=password,.*kaven.*
请求有所有匹配的cookie:
请求只有部分匹配的cookie:
所以,当断言列表中有互相矛盾的断言时,该路由就不可能匹配成功。
Header路由断言工厂接受两个参数,Header名称和一个正则表达式。此断言与具有给定名称且值与正则表达式匹配的Header的请求匹配。
- Header=gateway,.*kaven.*
Host路由断言工厂接受一个参数:Host模式列表。该模式是一个Ant风格(请求路径的一种匹配方式)的模式,以.作为分隔符。此断言匹配Host与模式匹配的请求。Ant通配符如下图所示:
- Host=**.kaven.com,**.kaven.top
*和?通配符这里不演示了。
Method路由断言工厂接受一个参数:要匹配的HTTP方法。
- Method=GET
为了演示必须要满足指定的HTTP方法才能匹配路由,在nacos module中增加一个POST接口。
@PostMapping("/message")
public String updateMessage(String message) {
return message;
}
POST方法不匹配指定的GET方法,直接响应404。
指定多个要匹配的HTTP方法,满足其中一个就可以匹配路由。
- Method=GET,POST
Path路由断言工厂接受两个参数:一个Spring PathMatcher模式列表和一个可选的标志matchOptionalTrailingSeparator(此参数为true时,如果模式没有尾部斜杠,请求路径有尾部斜杠也能成功匹配,否则不能成功匹配,该参数默认为true)。该模式匹配也符合Ant风格,Ant通配符如下图所示:
为了演示,在nacos module中增加几个接口。
@GetMapping("/kaven")
public String kaven() {
return "hello kaven";
}
@GetMapping("/kaven/1")
public String kaven1() {
return "hello kaven, this is 1";
}
@GetMapping("/kaven/1/2")
public String kaven1_2() {
return "hello kaven, this is 1/2";
}
@GetMapping("/itkaven")
public String itkaven() {
return "hello itkaven";
}
@GetMapping("/itkaven/1")
public String itkaven1() {
return "hello itkaven, this is 1";
}
@GetMapping("/itkaven/1/2")
public String itkaven1_2() {
return "hello itkaven, this is 1/2";
}
- Path=/kaven/{path},/itkaven/**
/itkaven/**表示路径是否以/itkaven开头(**表示后面有0个或者多个单位路径,*和?通配符这里不演示了),如果是则匹配。
/kaven/{path}表示后面有1个单位路径。
/kaven/2这个路径是匹配的,但nacos module中没有这个接口,仔细观察可以知道,/kaven/2路径的响应和/kaven路径的响应是不一样的(响应产生的主体不同,前者的响应是nacos module产生的,而后者的响应是gateway module产生的)。
修改成如下所示,表示后面有2个单位路径(以此类推)。
- Path=/kaven/{path1}/{path2}
/kaven/1/2路径就可以匹配成功了。
matchOptionalTrailingSeparator为false。
- Path=/kaven/{path},false
matchOptionalTrailingSeparator为true(默认)。
- Path=/kaven/{path},true
如果模式有尾部斜杠,请求路径也必须有尾部斜杠,此时matchOptionalTrailingSeparator参数的值就不起作用了。
Query路由断言工厂接受两个参数:必需的param和可选的regexp。
- Query=kaven
- Query=kaven,.*itkaven.*
RemoteAddr路由断言工厂采用CIDR(用于解释IP地址的标准)表示的IPv4或IPv6字符串列表(最少1个),例如192.168.1.199/24(其中192.168.1.199是IP地址,24是子网掩码位数)。
- RemoteAddr=192.168.1.1/24
在虚拟机中发送请求,虚拟机IP地址匹配。
- RemoteAddr=192.168.2.1/24
虚拟机IP地址已经不匹配了。
Gateway网关和路由断言工厂就介绍到这里,Gateway网关 & 路由断言工厂



