前言
Spring Cloud Gateway 基于WebFlux框架开发,目标是替换掉Zuul。本文我们就来系统学习一番 Spring Cloud Gateway 微服务网关的用法,笔者力争通过这篇文章带领大家学透 Spring Cloud Gateway 的用法。
Spring Cloud Gateway 概述
Spring Cloud Gateway 主要有两个特征:
-
默认使用RxNetty作为相应式Web容器,通过非阻塞方式,利用较少的线程和资源来处理高并发请求,并提升服务资源利用的可伸缩性
-
函数式编程端点, 通过使用Spring Web Flux 的函数式编程模式定义路由端点,处理请求
-
基于Spring Framework 5, Reactor 和 Spring Boot 2.0框架 -
根据请求的属性可以匹配对应的路由 -
集成Hystrix -
集成Spring Cloud DiscoveryClient -
把易于编写的的Predicates和Filters作用于特定路由 -
具备一些网关的高级功能,如动态路由、限流、路径重写
图 1 Spring Cloud Gagteway 架构图
外部请求首先会经过Spring Cloud Gateway网关的处理器映射器,然后到达网关的Web处理器,接下来再经过一些列的过滤器,最后才到达代理服务。
Spring Cloud Gateway 核心概念
简单说明一下架构图中的三个术语
-
Filter(过滤器):和Zuul的过滤器在概念上类似,可以使用Filter拦截和修改请求,实现对上游的响应,进行二次处理,实现横切与应用无关的的功能,如安全、访问超时设置、限流等功能。
-
Route(路由):网关配置的基本组成模块,和Zuul的路由配置模块类似。一个Route模块由一个ID、一个目标URI、一组断言和一组过滤器组成。如果断言为真,则路由匹配,目标URI会被访问。
-
Predicate(断言):Predicate来自Java 8的接口,它可以用来匹配来自Http请求的任何内容,例如headers或参数。接口包含多种默认方法,并将Predicate组合程复杂的逻辑(与、或、非),可以用于接口参数校验、路由转发判断等
Spring Cloud Gateway的接入和配置
Maven依赖引入
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
路由配置方式之配置文件方式
eureka:
instance:
prefer-ip-address: true
client:
service-url:
defaultZone: http://localhost:8888/eureka/
server:
port: 8080
spring:
application:
name: scg-api-gateway
cloud:
gateway:
routes:
- id: url-proxy-1
uri: https://localhost:8010
predicates:
- Path=/csdn
- id: message-provider-route
uri: lb://message-provider
predicates:
- Path=/message-provider/**
各字段含义如下:
-
id: 自定义的路由ID,保持唯一 -
uri: 目标服务地址 -
predicates: 路由条件,Predicates接受一个输入参数,返回一个布尔值结果。
-
第一个Predicate基于URL的方式。配置文件的第一个路由的配置采用URL的方式,配置了一个ID为uri-proxy-1的URI代理规则。路由的规则为:当访问地址为http://localhost:8080/dsdn/1.jpg时会路由到上游的地址http://localhost:8010/1.jpg。 -
第二个Predicate基于服务ID发现的方式。配置文件的第二个路由的配置采用与注册中心相结合的服务发现方式,与单个URI的路由配置相比,区别其实很小,仅在于URI的schema协议不同。单个URI的schema协议,一般为http或者https协议。
基于代码DSL方式的路由配置接入
GatewayApplication
中添加customRoutelocator
方法来定制转发规则,代码如下: @SpringBootApplication
public class GatewayApplication {
public static void main(Stirng[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes().route("path_route", r->r.path("/csdn").uri("https://localhost:8010")).build();
}
}
Spring Cloud Gateway 工作原理
图 2 Spring Cloud Gateway 工作流程图
Predicate 条件
在Spring Cloud Gateway 中, Spring 利用Predicate的特性实现了各种路由匹配规则,通过Header、请求参数等不同条件来匹配对应的路由。
我们来看Spring Cloud Gateway 内置的集中使用 Predicate 的方法。
server:
port: 8080
spring:
application:
name: api-gateway
cloud:
gateway:
routes:
- id: gateway-service
uri: https://localhost:8010
order: 0
predicates:
- Host=**.foo.org
- Path=/headers
- Method=GET
- Header=X-Request-Id, d+
- Query=foo, ba.
- Cookie=chocolate, ch.p
- After=2020-08-20T17:42:47.789-07:00[America/Denver]
- Before=2020-08-2017:42:47.789[America/Denver]
在上述配置文件中,如果多种 Predicate 同时存在于同一个路由,请求必须满足所有条件才能被这个路由匹配。
当一个请求满足多个 Predicate 条件时,请求只会被首个成功匹配的路由转发。下面分别对不同规则的路由进行解释。
-
通过请求路径匹配(Path Route Predicate)
路由断言工厂接收一个参数,根据 Path 定义好规则来判断访问的URI 是否匹配。配置示例如下:
spring:
cloud:
gateway:
routes:
- id: host_route
uri: https://localhost:8010
predicates:
- Path=/hello/{segment}
如果请求路径符合要求,则此路由将匹配,例如 /hello/1 或者 /hello/world
使用 curl 测试,命令行输入:
curl http://localhost:8080/hello/1
curl http://localhost:8080/hello/xx
curl http://localhost:8080/boo/xx
-
通过请求参数匹配 (Query Route Predicate) 路由断言工厂接收两个参数:一个必需的参数和一个可选的正则表达式。配置示例如下:
spring:
cloud:
gateway:
routes:
- id: query_route
uri: https://localhost:8010
predicates:
- Query=helloworld
spring:
cloud:
gateway:
routes:
- id: query_route
uri: localhost:8010
predicates:
- Query=hello, world
curl localhost:8080?hello=world
测试可以返回正确的页面代码。如果将 hello 的属性值改为 ok, 再次访问就会报 404 错误,证明路由需要匹配正则表达式才会进行路由。
-
通过请求方法匹配
路由断言工厂接收一个参数,即需要匹配 HTTP 方法。通过 POST、GET、PUT、DELETE 等不同的请求方式来进行路由。
spring:
cloud:
gateway:
routes:
-id: method_route
uri: http://localhost:8010
predicates:
- Method=GET
curl http://localhost:8080
curl -X POST http://localhost:8080
-
通过 Header 属性匹配 路由断言工厂接收两个参数,分别式请求头名称和正则表达式。Header Route Predicate 和 Cookie Route Predicate 一样,也是接收两个参数:一个 header 的属性值和一个正则表达式。这个属性值和正则表达式匹配则执行。
spring:
cloud:
gateway:
routes:
- id: header_route
uri: localhost:8010
predicates:
- Header=X-Request-ID, d+
通过 Host 路由匹配
spring:
cloud:
gateway:
routes:
- id: host_route
uri: http://xxx.com
predicates:
- Host=**.xxx.com
curl http://localhost:8080 -H "Host: www.xxx.com"
curl http://localhost:8080 -H "Host: hello.xxx.com"
通过测试以上两种 Host 设置方式,均可匹配到 host_route, 去掉 host 参数则会报 404 错误。
-
时间匹配
spring:
cloud:
gateway:
routes:
- id: time_route
uri: localhost:8010
predicates:
- After=2018-01-20T06:06:06+08:00[Asia/Shanghai]
spring:
cloud:
gateway:
routes:
- id: after_route
uri: localhost:8010
predicates:
- Before=2018-01-20T06:06:06+08:00[Asia/Shanghai]
spring:
cloud:
gateway:
routes:
- id: after_route
uri: localhost:8010
predicates:
- Between=2018-01-20T06:06:06[Asia/Shanghai]
2019-01-20T06:06:06[Asia/Shanghai]
-
通过 Cookie 匹配
spring:
cloud:
gateway:
routes:
- id: cookie_route
uri: http://localhost:8050/test/cookie
predicates:
- Cookie=hello, test
-
通过 IP 地址匹配
Remote Addr Route Predicate Factory 配置一个 IPv4 或者 IPv6 网络的字符串或 IP 地址。当请求的 IP 地址在网段之内或者配置的 IP 地址相同,匹配上则进行转发,否则不进行转发。下面是配置示例:
spring:
cloud:
gateway:
routes:
- id: remoteaddr_route
uri: localhost:8010
predicates:
- RemoteAddr=192.168.1.1/50
GatewayFilter 与 GlobalFilter
Spring Cloud Gateway 中有两种 Filter, 一种是 GlobalFilter (全局过滤器),一种是GatewayFilter。GlobalFilter 默认对所有的路由有效,GatewayFilter 需要通过路由分组指定。GlobalFilter 接口与 GatewayFilter 接口具有相同的签名,是有条件地应用于所有路由的特殊过滤器。
当请求进入路由匹配逻辑时,Web Handler 会将 GlobalFilter 的所有实例 和 GatewayFilter 路由特定实例添加到 Filter Chain 组件。Filter 组合执行的顺序由 Order 接口决定,可以通过 getOrder 方法 或使用 @Order
注解来设置。Spring Cloud Gateway 通过执行过滤器将逻辑分为“前置”和“后置” 阶段, 优先级较高的前置过滤器会优先被执行,而优先级较高的后置过滤器的执行顺序正好相反,最后执行。
GatewayFilter Factories
过滤器允许以某种方式修改传入的 Http 请求或返回 Http 响应。过滤器的作用是某些特定路由。Spring Cloud Gateway 包括许多内置的过滤器工厂。
-
实现前缀修改(增加前缀、去掉前缀)
PrefixPathGatewayFilterFactory
及 StriPrefixGatewayFilterFactory
是一对处理请求URL 的前缀 和 Filter
工厂,前者添加前缀,后者去除前缀。
配置文件 application.yml
如下:
spring:
cloud:
gateway:
#配置所有路由的默认过滤器,这里配置的是gatewayFilter
default-filter:
routes:
- id: filter_route
uri: lb://server-test #服务的application名称
order: 0 #路由级别
predicates:
- Path=/bus/**
filters:
- prefixPath=/mypath
- StripPrefix=2 #去掉前缀,去几层
PrefixPathGatewayFilterFactory
允许我们对相应的路由请求前增加前缀。例如实例配置中的请求/hello, 最后转发到目标服务的路径变为/mypath/hello。StripPrefixGatewayFilterFactory
允许我们将对应的路由请求去除前缀,例如实例配置中的请求name/bar/foo, 去掉前面两个前缀后,最后转发到目标服务的路径为/foo。-
实现请求头内容添加和改写
AddRequestHeaderGatewayFactory
采用一对名称和值作为参数,配置文件application.yml
如下:
spring:
cloud:
gateway:
routes:
- id: add_requerst_header_route
uri: http://localhost:8010
filters:
- AddRequestHeader=X-Request-Foo, Bar
-
实现请求体内容添加和改写
AddRequestParameterGatewayFilterFactory
采用一对名称和值作为参数,配置参数在application.yml文件中如下:spring:
cloud:
gateway:
routes:
- id: add_request_parameter_route
uri: localhost:8010
filters:
- AddRequestParameter=foo, bar
对于所有匹配的请求,将向下游请求添加foo=bar查询字符串。
-
实现熔断降级
HystrixGatewayFilter
允许向网关路由引入Hystrix, 保护服务不受级联故障影响,并允许在下游故障时提供fallback响应。要在项目中启用Hystrix网关过滤器,需要向Hystrix的依赖HystrixGatewayFilterFactory
添加一个name参数,即HystrixCommand
的名称,配置文件application.yml如下:
spring:
cloud:
routes:
- id: hystrix_route
uri: lb://test-service:8080
filters:
- name: Hystrix
args:
name: fallbackcmd
fallbackUri: forword:/incaseoffailureusethis
- RewritePath=/comsumingserviceendpoint, /backingserviceendpoint
hystrix fallback
时, 将转发到/incaseoffailureusethis。注意,这个示例还演示了通过目标URI上的”lb”前缀将 Spring Cloud Netflix Ribbon
客户端实现负载均衡。主要场景是网关应用程序中的内部控制器或处理程序使用 fallbackUri
,它也可以将请求重新路由到外部应用程序中的控制器或处理程序。-
分布式限流
Spring Cloud Gateway
内置的RequestRateLimiterGatewayFilterFactory
提供限流能力,基于令牌桶算法实现。目前它内置的RedisRateLimiter
, 依赖 Redis 来存储限流配置和统计数据。我们也可以实现自己的RateLimiter。 只需实现Spring Cloud Gateway
自带的RateLimiter
接口或者継承 AbstractRateLimiter>
首先,添加Maven 依赖:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
其次,添加限流配置:
spring:
cloud:
gateway:
routes:
- id: after_route
uri: lb://user-center
predicates:
- TimeBetween=上午0:00, 下午11:59
filters:
- AddRequestHeader=X-Request-Foo, Bar
- name: RequestRateLimiter
args:
#令牌桶每秒填充的平均速率
redis-rate-limiter.replenishRate: 1
#令牌桶的上限
redis-rate-limiter.burstCapacity: 2
#使用 SpEL表达式从Spring 容器中获取 Bean 对象
key-resolver: "#{@pathKeyResolver}"
redis:
host: 127.0.0.1
port: 6379
@Configuration
public class Configuration {
/**
* 按照 Path 限流
* @return key
*/
@Bean
public KeyResolver pathKeyResolver(){
return exchange -> Mono.just(
exchange.getRequest()
.getPath()
.toString()
);
}
}
参考阅读
【1】Spring Cloud Gateway(https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#the-prefixpath-gatewayfilter-factory)
【2】王佩华著《微服务架构深度解析原理、实践与进阶》之10.4 Spring Cloud Gateway
往期文章推荐
【1】ZooKeeper入门(四):ZooKeeper事务与分布式锁InterProcessMutax
【3】Zookeeper入门(三)—使用CuratorFramework操作节点并添加监视器
【4】ZooKeeper入门(二):ZooKeeper常用命令介绍及使用Curator客户端实现分布式配置中心
原文始发于微信公众号(阿福谈Web编程):一文学透微服务网关 Spring Clud Gateway 的用法
暂无评论内容