【Spring Cloud Gateway】学习笔记
一、简介
Zuul 2.x版本一直跳票,2019年5月,Netflix终于开源了支持异步调用模式的zuul 2.0版本,真可谓千呼万唤始出来。但是Spring Cloud已经不再集成Zuul2.x了,那么是时候了解—下Spring Cloud Gateway了.
Spring Cloud Gateway是基于Spring生态系统之上构建的API网关,包括: Spring 5,Spring Boot2和Project Reactor。SpringCloud Gateway旨在提供—种简单而有效的方法来路由到API,并为它们提供跨领域的关注点,例如:安全性,监视/指标,限流等。由于Spring 5.0支持 Netty,Http2,而 Spring Boot2.0支持Spring 5.0,因此Spring Cloud Gateway支持Netty和Http2顺理成章。
网关应当具备以下功能:
- 性能:API高可用,负载均衡,容错机制。
- 安全:权限身份认证、脱敏,流量清洗,后端签名(保证全链路可信调用),黑名单(非法调用的限制)。
- 日志:日志记录,—旦涉及分布式,全链路跟踪必不可少。
- 缓存:数据缓存。
- 监控:记录请求响应数据,API耗时分析,性能监控。
- 限流:流量控制,错峰流控,可以定义多种限流规则。
- 灰度:线上灰度部署,可以减小风险。
- 路由:动态路由规则。
二、核心概念
路由(Route) ∶路由是网关最基础的部分,路由信息由ID、目标URI、一组断言和一组过滤器组成。如果断言路由为真,则说明请求的URI和配置匹配。
断言(Predicate) : Java8中的断言函数。Spring Cloud Gateway 中的断言函数输入类型是Spring 5.0框架中的ServerWebExchange。Spring Cloud Gateway中的断言函数允许开发者去定义匹配来自于Http Request 中的任何信息,比如请求头和参数等。
过滤器(Filter):一个标准的Spring Web Filter。Spring Cloud Gateway中的Filter分为两种类型,分别是Gateway Filter和Global Filter。过滤器将会对请求和响应进行处理。
三、代码案例
3.1 pom依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
3.2、配置项
spring:
application :
name: gateway-server
cloud:
gateway: #路由规则
routes:
- id: gateway #路由ID,唯一
uri: lb:CONSUMER #目标URI,路由到微服务的地址
predicates: #断言(判断条件)
- Path=/consumer/** #四且改对应URL的请求,将匹配至的请求追加在目标URI
eureka:
client:
service-url:
defaultZone: http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/,http://eureka7001.com:7001/eureka/
3.3 启动项
启动项无需配置其他的注解
四、路由规则
4.1、path
spring:
application :
name : gateway-server #应用名称
cloud :
gateway : #路由规则
routes :
- id : product-service #路由ID,唯一
uri: http://localhost:7878/ #目标URI,路由到到微服务的地址
predicates: #断言(判断条件)
- Path=/product/** #匹配对应URL的请求,将匹配到的请求追加在目标URI之后
- 请求
http://localhost:9999/product/1
将会路由至http://localhost:7878/product/1
4.2、Query
spring:
application:
name : gateway-server #应用名称cloud :
gateway : #路由规则routes:
- id : product-service #路由ID,唯一
uri: http://localhost:7078/ #目标URI,路由到到微服务的地址
predicates : #断言(判断条件)
#-Query=token #匹配请求参教中包含token的请求
- Query=token,abc. #匹配请求参数中包含token并且其参数值满足正则表达式abc.的请求
- Query=token : 比如,http://localhost:9000/product/1?token=123
- Query=token,abc. :比如,http://localhost:9000/product/1?token=abc1
4.3、Method
spring:
application :
name : gateway-server #应用名称cloud :
gateway : #路由规则routes:
- id : product-service #路由ID,唯一
uri: http://localhost:7070/ #目标URI,路由到微服务的地址
predicates : #断言(判断条件)
- Method=GET #匹配任意GET请求
4.4、Datetime
spring:
application :
name : gateway-server #应用名称cloud :
gateway : #路由规则routes :
- id : product-service #路由ID,唯一
uri: http://localhost:7870/ #目标URI,路由到微服务的地址
predicates : #断言(判断条件)
#匹配中国上海时间2820-82-0220:20:28之后的请求
- After=2828-82-82T28:20:20.888+88:88[ Asia/Shanghai]
4.5、RemoteAddr
spring:
application :
name : gateway-server #应用名称cloud :
gateway : #路由规则routes :
- id : product-service #路由ID,唯一
uri : http://localhost:7078/ #目标URI,路由到微服务的地址
predicates: #断言《判断条件)
- RemoteAddr=192.168.10.1/0 #匹配远程地址请求是RemoteAddr 的请求,0表示子网掩码
4.6、Header
spring:
application :
name : gateway-server #应用名称cloud :
gateway : #路由规则routes :
- id : product-service #路由ID,唯一
uri : http://localhost:7078/ #目标URI,路由到微服务的地址
predicates: #断言《判断条件)
#匹配请求头包含X-Request-Id并且其值匹配正则表达式\d+的请求
- Header=X-Request-Id, \d+
五、动态路由
5.1、添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
5.2、配置项
增加eureka配置项即可
eureka:
client:
service-url:
defaultZone: http://eureka7001.com:7001/eureka/
5.3、通过注册中心获取路由
将uri改成uri: lb://CONSUMER/ # lb://服务名
5.4、通过服务名称转发
cloud :
gateway :
discovery :
locator :
#是否与服务发现组件进行结合,通过serviceId 转发到具体服务实例。
enabled: true #是否开启基于服务发现的路由规则
lower-case-service-id: true #是否将服务名称转小写
六、过滤器
Spring Cloud Gateway根据作用范围划分为GatewayFilter
和 GlobalFilter
,二者区别如下:
GatewayFilter
:网关过滤器,需要通过spring.cloud.routes.filters配置在具体路由下,只作用在当前路由上或通过spring.cloud .default-filters配置在全局,作用在所有路由上。
GlobalFilter
:全局过滤器,不需要在配置文件中配置,作用在所有的路由上,最终通过GatewayFilterAdapter
包装成GatewayFilterChain
可识别的过滤器,它为请求业务以及路由的URI转换为真实业务服务请求地址的核心过滤器,不需要配置系统初始化时加载,并作用在每个路由上。
6.1、GatewayFilter
6.1.1、path过滤器
6.1.1.1、RewritePathGatewayFilterFactory(重写请求路径)
RewritePath网关过滤器工厂采用路径正则表达式参数和替换参数,使用Java正则表达式来灵活地重写。
filters: #网关过滤器
#将/api-gateway/product/1重写为/product/1
- RewritePath=/api-gateway(?<segment>/?.*),$\{segment}
6.1.1.2、PrefixPathGatewayFilterFactory(请求路径添加指定前缀)
PrefixPath 网关过滤器工厂为匹配的URI。
filters: #网关过滤器
#将/1重写为/product/1
- PrefixPath=/product
6.1.1.3、StripPrefixGatewayFilterFactory(剥离路径个数)
StripPrefix网关过滤器工厂采用一个参数 StripPrefix,该参数表示在将请求发送到下游之前从请求中剥离的路径个数。
filters: #网关过滤器
#将/api/123/product/1重写为/product/1
- StripPrefix=2
6.1.1.4、SetPathGatewayFilterFactory(模板化路径段)
SetPath网关过滤器工厂采用路径模板参数。它提供了一种通过允许模板化路径段来操作请求路径的简单方法,使用了SpringFramework中的uri模板,允许多个匹配段。
predicates: #断言(判断条件)
#匹配对应URI的请求,将匹配到的请求追加在目标URI之后
- Path=/api/product/{sagment}
filters: #网关过滤器
#将/api/product/1重写为/product/1
- SetPath=/product/{segment}
6.1.2、Parameter参数过滤器
AddRequestParameter网关过滤器工厂会将指定参数添加至匹配到的下游请求中。
predicates: #断言(判断条件)
#匹配对应UR工的请求,将匹配到的请求追加在目标URI之后
- Path=/api-gateway/**
filters: #网关过滤器
#将/api-gateway/producet/1重写为/product/1
- RewritePath=/api-gateway(?<segment>/?.*),$\{segment}
#在下游请求中添加flag=1
- AddRequestParameter=flag,1
6.1.3、Status状态过滤器
Setstatus网关过滤器工厂采用单个状态参数,它必须是有效的 Spring HttpStatus。它可以是整数404或枚举NOT_FOUND的字符串表示。
filters : #网关过滤器
#任何情况下,响应的HTTP状态都将设置为404
- SetStatus=404
#404或者对应的校举NOT_FOUND
6.2、GlobalFilter
全局过滤器不需要在配置文件中配置,作用在所有的路由上,最终通过GatewayFilterAdapter包装成GatewayFilterChain可识别的过滤器,它是请求业务以及路由的URI转换为真实业务服务请求地址的核心过滤器,不需要配置系统初始化时加载,并作用在每个路由上。
6.3、自定义过滤器
即使Spring Cloud Gateway自带许多实用的GatewayFilter Factory、 Gateway Filter、Global Filter,但是在很多情景下我们仍然希望可以自定义自己的过滤器,实现一些操作。
6.3.1、网关过滤器
自定义网关过滤器需要实现以下两个接口:GatewayFilter , Ordered .
6.3.1.1、定义网关过滤器
CustomGatewayFilter.java
public class CustomGatewayFilter implements GatewayFilter,Ordered {
/*
*过滤器业务逻辑
* param exchange
* param chain
* @return
*/
@override
public Mono<Void> filter(ServerWebExchange exchange,GatewayFilterChain chain) {
System.out.println("自定义网关过滤器被执行");
return chain.filter (exchange); //继续向下执行
}
/**
*过滤器执行顺序,数值越小,优先级越高*
* @return
*/
@Override
public int getOrder(){
return 0;
}
}
6.3.1.2、注册过滤器
@Configuration
public class GatewayRoutesConfiguration{
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {
return builder.routes().route(r -> r
//断言(判断条件)
.path("/product/**")
//目标URI,路由到微服务的地址
.uri("lb://product-service" )
//注册自定义网关过滤器
.filters(new CustomGatewayFilter())
//路由 ID,唯一
.id("product-service")
)
.build();
}
}
6.3.2、全局过滤器
自定义全局过滤器需要实现以下两个接口:GlobalFilter , ordered。通过全局过滤器可以实现权限校验,安全性验证等功能。
6.3.2.1、定义过滤器
实现指定接口,添加@component
注解即可。
全局过滤器无需注册
CustomGlobalFilter.java
@Component
public class CustomGlobalFilter implements GlobalFilter,Ordered {
/*
*过滤器业务逻辑
* param exchange
* param chain
* @return
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange,GatewayFilterChain chain) {
System.out.println("自定义全局过滤器被执行");
return chain.filter (exchange) ; //继续向下执行
}
/**
*过滤器执行顺序﹔教值越小,优先级越高
* return
*/
@Override
public int getordef(){
return 0 ;
}
}
七、限流
Spring Cloud Gateway官方提供了RequestRateLimiterGatewayFilterFactory 过滤器工厂,使用Redis和 Lua脚本实现了令牌桶的方式。
7.1、配置
依赖
新版redis使用的是pool2连接池
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
配置项
filters:
- StripPrefix=1
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 1 #令牌桶每秒填充速率
redis-rate-limiter.burstcapacity: 2 #令牌桶总容量
key-resolver: "#{@pathKeyResolver}" #使用SpEL表达式按名称引用bean
redis:
timeout: 10000 #连接超时时间
host: 127.0.0.1 #Redis服务器地址
port: 6379 #Redis服务器端口
password: root #Redis服务器密码
database: 0 #选择哪个库,默认0库
lettuce:
pool:
max-active: 1024 #最大连接数,默认8
max-wait: 10000 #最大连接阻塞等待时间,单位毫秒,默认 -1
max-idle: 200 #最大空闲连接,默认8
min-idle: 5 #最小空闲连接,默认4
还需要一个bean:pathKeyResolver 根据不同限流方式,书写不同
7.2、限流方式
7.2.1、URL限流
bean:pathKeyResolver
@Configuration
public class KeyResolverConfiguration {
@Bean
public KeyResolver pathKeyResolver(){
return exchange -> Mono.just(exchange.getRequest().getURI( ).getPath());
}
}
7.2.2、参数限流
bean:parameterKeyResolver
@Bean
public KeyResolver parameterKeyResolver(){
return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst( k: "userId"));
}
7.2.3、IP限流
bean:ipKeyResolver
@Bean
public KeyResolver ipKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
}