SpringCloud 之 Gateway 入门案例
入门案例
首先创建一个简单的 SpringBoot 工程,pom 依赖如下:
<?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.5.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>club.lacerate</groupId>
<artifactId>lacerate-gateway</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>lacerate-gateway</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>2020.0.3</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-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</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>
<version>2.5.3</version>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
Java 流式 API 自定义 RouteLocator 的方式
创建 Gateway 的配置类:
package club.lacerate.config;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class CommonConfiguration {
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("gateway_test", r -> r.path("/test")
.uri("https://www.jd.com/")
).build();
}
}
上面这段配置的意思是,配置了一个 id 为 gateway_test的路由规则,当访问地址 http://localhost:8080/test 时会自动转发到地址:https://www.jd.com/
访问测试:(访问 http://localhost:8080/test)
### YML 配置文件的方式
在 application.yml 中配置如下参数:
server:
port: 8080
spring:
cloud:
gateway:
routes:
- id: gateway_test_2
uri: https://www.jd.com/
predicates:
- Path=/test_2
上面配置的作用:
- id:自定义的路由 ID,等同于 Gateway 的配置类中 route() 方法的第一个参数;
- uri:需要转发的目标服务地址;
- predicates:路由条件,predicate 接受一个输入参数,返回一个布尔值结果;该接口包含多种默认方法来将 predicate 组合成其他复杂的逻辑。
访问测试:(访问 http://localhost:8080/test_2)
路由断言
Spring Cloud Gateway 的路由匹配的功能是以 Spring WebFlux 中的 Handler Mapping 为基础实现的。Spring Cloud Gateway 也是由许多的路由断言工厂组成的,当 Http Request 请求进入 Spring Cloud Gateway 的时候,网关中的路由断言工厂会根据配置的路由规则,对 Http Request 请求进行断言匹配;匹配成功则进行下一步处理,否则断言失败直接返回错误信息。
After 路由断言工厂
After 路由断言工厂中会取一个 UTC 时间格式的时间参数,当请求进来的当前时间在配置的 UTC 时间之后,则会成功匹配,否则不能成功匹配。
示例:
@Configuration
public class CommonConfiguration {
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
// 生成比当前时间早一个小时的UTC时间
ZonedDateTime minusTime = LocalDateTime.now().minusHours(1).atZone(ZoneId.systemDefault());
return builder.routes()
.route("after_route", r ->r.after(minusTime).uri("https://www.jd.com/"))
.build();
}
}
或
spring:
cloud:
gateway:
routes:
- id: after_route
uri: https://www.jd.com/
predicates:
- After=2021-08-03T12:59:04.878+08:00[Asia/Shanghai]
若更改 UTC 时间为当前时间一个小时后的 UTC 时间,然后再启动应用,访问 http://127.0.0.1
,页面会返回 404 错误
Before 路由断言工厂
Before 路由断言工厂会取一个 UTC 时间格式的时间参数,当请求进来的当前时间在配置的 UTC 时间之前,则会成功匹配,否则不能成功匹配。
示例:
@Configuration
public class CommonConfiguration {
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
// 生成比当前时间晚一个小时的UTC时间
ZonedDateTime plusTime = LocalDateTime.now().plusHours(1).atZone(ZoneId.systemDefault());
return builder.routes()
.route("before_route", r -> r.before(plusTime).uri("https://www.jd.com/"))
.build();
}
}
或
spring:
cloud:
gateway:
routes:
- id: before_route
uri: https://www.jd.com/
predicates:
- Before=2021-08-03T14:59:04.878+08:00[Asia/Shanghai]
Between 路由断言工厂
Between 路由断言工厂会取一个 UTC 时间格式的时间参数,当请求进来的当前时间在配置的 UTC 时间之间,则会成功匹配,否则不能成功匹配。
示例:
@Configuration
public class CommonConfiguration {
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
ZonedDateTime minusTime = LocalDateTime.now().minusHours(1).atZone(ZoneId.systemDefault());
ZonedDateTime plusTime = LocalDateTime.now().plusHours(1).atZone(ZoneId.systemDefault());
return builder.routes()
.route("between_route", r -> r.between(minusTime, plusTime).uri("https://www.jd.com/"))
.build();
}
}
或
spring:
cloud:
gateway:
routes:
- id: between_route
uri: https://www.jd.com/
predicates:
- Between=2021-08-03T12:59:04.878+08:00[Asia/Shanghai], 2021-08-03T14:59:04.878+08:00[Asia/Shanghai]
Cookie 路由断言工厂
Cookie 路由断言工厂会取两个参数,分别是 cookie 名称对应的 key 和 value。当请求中携带的 cookie 和 Cookie 断言工厂中配置的 cookie 一致,则路由匹配成功,否则匹配不成功。
示例:
@Configuration
public class CommonConfiguration {
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("cookie_route", r -> r.cookie("key", "value").uri("https://www.jd.com/"))
.build();
}
}
或
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: https://www.jd.com/
predicates:
- Cookie=key, value
若 Cookie 包含book=java
,则访问 http://127.0.0.1
,可以成功跳转到京东的首页
Header 路由断言工厂
Header 路由断言工厂用于根据配置的路由 header 信息进行断言匹配路由,匹配成功进行转发,否则不进行转发。
示例:
@Configuration
public class CommonConfiguration {
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("header_route", r -> r.header("X-Request-Id", "Foley").uri("https://www.jd.com/"))
.build();
}
}
或
spring:
cloud:
gateway:
routes:
- id: header_route
uri: https://www.jd.com/
predicates:
- Header=X-Request-Id, Foley
若 Header 中包含X-Request-Id=Peter
,则然后访问 http://127.0.0.1
,可以成功跳转到京东的首页
Host 路由断言工厂
Host 路由断言工厂根据配置的 Host,对请求中的 Host 进行断言处理,断言成功则进行路由转发,否则不转发。
示例:
@Configuration
public class CommonConfiguration {
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("host_route", r -> r.host("**.lacerate.club").uri("https://www.jd.com/"))
.build();
}
}
或
spring:
cloud:
gateway:
routes:
- id: host_route
uri: https://www.jd.com/
predicates:
- Host=**.lacerate.club
若系统的 hosts 配置文件中包含域名映射:127.0.0.1 www.lacerate.club
,则访问 http://www.lacerate.club
,可以成功跳转到京东的首页
Method 路由断言工厂
Method 路由断言工厂会根据路由信息配置的 method 对请求方法是 Get 或者 Post 等进行断言匹配,匹配成功则进行转发,否则处理失败。
示例:
@Configuration
public class CommonConfiguration {
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("method_route", r -> r.method("GET").uri("https://www.jd.com/"))
.build();
}
}
或
spring:
cloud:
gateway:
routes:
- id: method_route
uri: https://www.jd.com/
predicates:
- Method=GET
Query 路由断言工厂
Query 路由断言工厂会从请求中获取两个参数,将请求中参数和 Query 断言路由中的配置进行匹配,比如 http://127.0.0.1?query=value
中的 query=value和下面的
r.query(“query”,“value”)` 配置一致,则转发成功,否则转发失败。
示例:
@Configuration
public class CommonConfiguration {
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("query_route", r -> r.query("query", "value").uri("https://www.jd.com/"))
.build();
}
}
或
spring:
cloud:
gateway:
routes:
- id: query_route
uri: https://www.jd.com/
predicates:
- Query=query, value
Path 路由断言工厂
Path 路由断言工厂接收一个参数,根据 Path 定义好的规则来判断访问的 URI 是否匹配。在下述配置中,如果请求路径为 /path/test/
,则此路由将匹配;也可以使用表达式,例如 /path/test/**
表示匹配 /path/test/
开头的多级 URI。
示例:
@Configuration
public class CommonConfiguration {
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("path_route", r -> r.path("/path/test/").uri("https://www.jd.com/"))
.build();
}
}
或
spring:
cloud:
gateway:
routes:
- id: path_route
uri: https://www.jd.com/
predicates:
- Path=/path/test/
注意:上述的 URI 如果不以 /
结尾,那么转发后的 URI 为 https://www.jd.com/path/test/
;若以 /
结尾,转发后的 URI 则为 https://www.jd.com/
Weight 路由断言工厂
Weight 路由断言工厂,在 Spring Cloud Gateway 中可以使用它对 URL 进行权重路由,只需在配置时指定分组和权重值即可。
示例:
spring:
cloud:
gateway:
routes:
- id: weight_route_v1
uri: http://127.0.0.1:8080/v1/
predicates:
- Path=/test
- Weight=group, 95
- id: weight_route_v2
uri: http://127.0.0.1:8080/v2/
predicates:
- Path=/test
- Weight=group, 5
上述配置中,添加了两个针对 /test
路径转发的路由定义配置,这两个路由属于同一个权重分组,权重的分组名称为 group
。最终的效果是把 /test
接口的 95% 的请求流量分发给服务的 V1 版本,把剩余 5% 的流量分发给服务的 V2 版本。
RemoteAddr 路由断言工厂
RemoteAddr 路由断言工厂配置一个 IPv4 或 IPv6 网段的字符串或者 IP。当客户端的 IP 地址在网段之内或者和配置的 IP 相同,则成功转发,否则不能转发。
示例:
@Configuration
public class CommonConfiguration {
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("remoteaddr_route", r -> r.remoteAddr("127.0.0.1").uri("https://www.jd.com/"))
.build();
}
}
或
spring:
cloud:
gateway:
routes:
- id: remoteaddr_route
uri: https://www.jd.com/
predicates:
- RemoteAddr=127.0.0.1
过滤器 Filter
Spring Cloud Gateway 路由过滤器允许以某种方式修改进来的 HTTP 请求或返回的 HTTP 响应。路由过滤器主要作用于需要处理的特定路由,Spring Cloud Gateway 提供了很多种的过滤器工厂,过滤器的实现类将近二十多个。总得来说,可以分为七类:Header、Parameter、Path、Status、Redirect 跳转、Hytrix 熔断和 RateLimiter 限流。
AddRequestHeader 过滤器
AddRequestHeader 过滤器工厂用于对匹配上的请求加上 Header:
@Configuration
public class CommonConfiguration {
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("add_request_header_route", r -> r.path("/addRequestHeader") .filters(f -> f.addRequestHeader("X-Request-Id", "Foley")) .uri("http://127.0.0.1:8080/addRequestHeader/")
).build();
}
}
AddRequestParameter 过滤器
AddRequestParameter 过滤器作用是对匹配上的请求添加请求参数:
@Configuration
public class CommonConfiguration {
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("add_request_parameter_route", r -> r.path("/addRequestParameter").filters(f -> f.addRequestParameter("key", "value")).uri("http://127.0.0.1:8080/addRequestParameter/")
).build();
}
}
RewritePath 过滤器
Spring Cloud Gateway 可以使用 RewritePath 替换 Zuul 的 StripPrefix 功能,而且功能更强大。
在 Zuul 中使用如下配置后,所有 /example/xxxx
的请求会转发给 http://example.com/xxxx
,同时去除掉了 example
前缀:
zuul:
routes:
example:
path: /example/**
stripPrefix: true
uri: http://example.com
Spring Cloud Gateway 实现了类似的功能,使用的是 RewritePath 过滤器工厂:
@Configuration
public class CommonConfiguration {
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("rewrite_path_route", r -> r.path("/example/**").filters(f -> f.rewritePath("/example/(?<segment>.*)", "/$\\{segment}")).uri("https://www.jd.com/")
).build();
}
}
注意:上述的 URI 是不以 /
结尾的,否则仅仅会直接跳转到 https://www.jd.com/
。
AddResponseHeader 过滤器
AddResponseHeader 过滤器工厂的作用是对从网关返回的响应添加 Header:
@Configuration
public class CommonConfiguration {
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("add_response_header_route", r -> r.path("/addResponseHeader").filters(f -> f.addResponseHeader("X-Request-Id", "Foley")).uri("https://www.jd.com/")
).build();
}
}
StripPrefix 过滤器
StripPrefixGatewayFilterFactory 是一个对针对请求 URL 前缀进行处理的 Filter 工厂,用于去除前缀,而 PrefixPathGatewayFilterFactory 是用于增加前缀。
spring:
cloud:
gateway:
routes:
- id: strip_prefix_route
uri: https://www.jd.com/
predicates:
- Path=/test/**
filters:
- StripPrefix=1
上述的配置,访问 http://127.0.0.1:8080/test
,会跳转到 https://www.jd.com/
,即去除了前缀 /test/
。
Retry 过滤器
网关作为所有请求流量的入口,网关对路由进行协议适配和协议转发处理的过程中,如果出现异常或网络抖动,为了保证后端服务请求的高可用,一般处理方式会对网络请求进行重试,接口必须需要做幂等处理。
@Configuration
public class CommonConfiguration {
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("retry_route", r -> r.path("/test/retry").filters(f -> f.retry(config -> config.setRetries(2).setStatuses(HttpStatus.INTERNAL_SERVER_ERROR))).uri("http://127.0.0.1:8080/retry?key=value&count=2"))
.build();
}
}
config.setRetries(2).setStatuses(HttpStatus.INTERNAL_SERVER_ERROR)
表示设置重试次数为两次,当服务调用失败时设置返回的状态码为 500,即服务器内部错误。
Hystrix 过滤器
Hystrix 可以提供熔断、服务降级和快速失败等功能。Spring Cloud Gateway 对 Hystrix 进行集成提供路由层面的服务熔断和降级,最简单的使用场景是当通过 Spring Cloud Gateway 调用后端服务,后端服务一直出现异常、服务不可用的状态。此时为了提高用户体验,就需要对服务降级,返回友好的提示信息给服务消费者,在保护网关自身可用的同时保护后端服务高可用。
示例:
引入spring-cloud-starter-netflix-hystrix
依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
Fallback 控制器:
@RestController
public class FallbackController {
@GetMapping("/fallback")
public String fallback() {
return "Spring Cloud Gateway Fallback!";
}
}
配置 Hystrix 过滤器:
spring:
cloud:
gateway:
routes:
- id: hystrix_route
predicates:
- Path=/test
filters:
- name: Hystrix # Hystrix Filter 的名称
args: # Hystrix 配置参数
name: fallbackcmd # HystrixCommand 的名字
fallbackUri: forward:/fallback # 表示触发熔断机制后的跳转请求url
uri: http://127.0.0.1:9000/hystrix
# Hystrix 配置
hystrix:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 6000 # 默认的熔断时间
fallbackcmd:
execution:
isolation:
thread:
timeoutInMilliseconds: 20000 # hystrix_route 的熔断时间
上面这段配置的意思是,配置了一个 id 为 hystrix_route 的路由规则,当访问地址 http://localhost:8080/test 时会自动转发到地址:http://127.0.0.1:9000/hystrix,当关闭 fallbackcmd 服务后会自动转发到地址:http://127.0.0.1:8080/fallback
Actuator API
添加开启端点的配置信息,Spring Cloud Gateway 提供了一个 Gateway Actuator,该 EndPiont 提供了关于 Filter 及 Routes 的信息查询以及指定 Route 信息更新的 Rest API 接口:
公开网关端点:
management:
endpoint:
gateway:
enabled: true
endpoints:
web:
exposure:
include: gateway
Spring Cloud Gateway执行器端请求总结:
请求路径 | 请求类型 | 描述 | 备注 |
---|---|---|---|
/actuator/gateway/globalfilters | GET | 检索应用于所有路由的全局过滤器 | |
/actuator/gateway/routefilters | GET | 检索应用于路由的GatewayFilter工厂 | |
/actuator/gateway/refresh | POST | 清除路由缓存 | |
/actuator/gateway/routes | GET | 检索网关中定义的路由 | |
/actuator/gateway/routes/{id} | GET | 检索有关单个路由的信息 | |
/gateway/routes/ {id_route_to_create} | POST | 创建路由 | 使用指定路由字段的JSON body |
/gateway/routes/{id_route_to_delete} | DELETE | 删除路由 |