Gateway路由配置
Spring Cloud gateway工作流程
在之前的文章的Spring Cloud Gateway初体验中,大家已经对Spring Cloud Gateway的功能有一个初步的认识,网关作为一个系统的流量的入口,有着举足轻重的作用,通常的作用如下:
- 协议转换,路由转发
- 流量聚合,对流量进行监控,日志输出
- 作为整个系统的前端工程,对流量进行控制,有限流的作用
- 作为系统的前端边界,外部流量只能通过网关才能访问系统
- 可以在网关层做权限的判断
- 可以在网关层做缓存
Spring Cloud Gateway作为Spring Cloud框架的第二代网关,在功能上要比Zuul更加的强大,性能也更好。随着Spring Cloud的版本迭代,Spring Cloud官方有打算弃用Zuul的意思。在笔者调用了Spring Cloud Gateway的使用和功能上,Spring Cloud Gateway替换掉Zuul的成本上是非常低的,几乎可以无缝切换。Spring Cloud Gateway几乎包含了zuul的所有功能。
如上图所示,客户端向Spring Cloud Gateway发出请求。 如果Gateway Handler Mapping确定请求与路由匹配(这个时候就用到predicate),则将其发送到Gateway web handler处理。 Gateway web handler处理请求时会经过一系列的过滤器链。 过滤器链被虚线划分的原因是过滤器链可以在发送代理请求之前或之后执行过滤逻辑。 先执行所有“pre”过滤器逻辑,然后进行代理请求。 在发出代理请求之后,收到代理服务的响应之后执行“post”过滤器逻辑。这跟zuul的处理过程很类似。在执行所有“pre”过滤器逻辑时,往往进行了鉴权、限流、日志输出等功能,以及请求头的更改、协议的转换;转发之后收到响应之后,会执行所有“post”过滤器的逻辑,在这里可以响应数据进行了修改,比如响应头、协议的转换等。
在上面的处理过程中,有一个重要的点就是讲请求和路由进行匹配,这时候就需要用到predicate,它是决定了一个请求走哪一个路由。
路由配置方式
基础URI一种路由配置方式
如果请求的目标地址,是单个的URI资源路径,配置文件示例如下:
1 | spring: |
各字段含义
id:我们自定义的路由 ID,保持唯一
uri:目标服务地址
predicates:路由条件,Predicate 接受一个输入参数,返回一个布尔值结果。该接口包含多种默认方法来将 Predicate 组合成其他复杂的逻辑(比如:与,或,非)。
上面这段配置的意思是,配置了一个 id 为order-server的URI代理规则,路由的规则为:当访问地址http://localhost:9066/时,会路由到上游地址http://localhost:8083/。
测试
基于代码的路由配置方式
转发功能同样可以通过代码来实现,我们可以在配置类 GatewayConfiguration中添加方法 customRouteLocator() 来定制转发规则。
1 |
|
我们在yaml配置文件中注销掉相关路由的配置,重启服务,访问链接: http://localhost:9066/order/createorder/1, 可以看到和上面一样的页面,证明我们测试成功。
上面两个示例中 uri 都是指向了我们的 order服务,在实际项目使用中可以将 uri 指向对外提供服务的项目地址,统一对外输出接口。
和注册中心相结合的路由配置方式
在uri的schema协议部分为自定义的lb:类型,表示从微服务注册中心(如Eureka)订阅服务,并且进行服务的路由。
1 | spring: |
注册中心相结合的路由配置方式,与单个URI的路由配置,区别其实很小,仅仅在于URI的schema协议不同。单个URI的地址的schema协议,一般为http或者https协议。
新增的StripPrefix可以接受一个非负整数,对应的具体实现是StripPrefixGatewayFilterFactory,从名字就可以看出它的作用是去掉前缀的,那个整数即对应层数。具体到本例中,我们通过 Spring Cloud Gateway 访问 user/order/version,那么当网关服务向后转发请求时,会去掉/user,微服务收到的就是/order/version。
匹配规则
Spring Cloud Gateway 的功能很强大,我们仅仅通过 Predicates 的设计就可以看出来,前面我们只是使用了 predicates 进行了简单的条件匹配,其实 Spring Cloud Gataway 帮我们内置了很多 Predicates 功能。
Spring Cloud Gateway 是通过 Spring WebFlux 的 HandlerMapping 做为底层支持来匹配到转发路由,Spring Cloud Gateway 内置了很多 Predicates 工厂,这些 Predicates 工厂通过不同的 HTTP 请求参数来匹配,多个 Predicates 工厂可以组合使用。
predicate简介
Predicate来自于java8的接口。Predicate 接受一个输入参数,返回一个布尔值结果。该接口包含多种默认方法来将Predicate组合成其他复杂的逻辑(比如:与,或,非)。可以用于接口请求参数校验、判断新老数据是否有变化需要进行更新操作。add–与、or–或、negate–非。
在 Spring Cloud Gateway 中 Spring 利用 Predicate 的特性实现了各种路由匹配规则,有通过 Header、请求参数等不同的条件来进行作为条件匹配到对应的路由。
Spring Cloud Gateway内置了许多Predict,这些Predict的源码在org.springframework.cloud.gateway.handler.predicate包中,如果读者有兴趣可以阅读一下。网上有一张图总结了 Spring Cloud 内置的几种 Predicate 的实现。
说白了 Predicate 就是为了实现一组匹配规则,方便让请求过来找到对应的 Route 进行处理,接下来我们接下 Spring Cloud GateWay 内置几种 Predicate 的使用。
每一个Predicate
的使用,你可以理解为:当满足这种条件后才会被转发
,如果是多个,那就是都满足的情况下被转发。
通过请求参数匹配
Query Route Predicate 支持传入两个参数,一个是属性名一个为属性值,属性值可以是正则表达式。
1 | spring: |
经过测试必须包含有order的参数名,否则无法访问
使用 curl 测试,命令行输入:
1 | curl http://localhost:9066/order/createorder/1?order=xxx |
还可以将 Query 的值以键值对的方式进行配置,这样在请求过来时会对属性值和正则进行匹配,匹配上才会走路由。
1 | spring: |
这样只要当请求中包含 order属性并且参数值是以 xx开头的长度为三位的字符串才会进行匹配和路由。
使用 curl 测试,命令行输入:
1 | curl http://localhost:9066/order/createorder/1?order=xxx |
通过 Header 属性匹配
Header Route Predicate 和 Cookie Route Predicate 一样,也是接收 2 个参数,一个 header 中属性名称和一个正则表达式,这个属性值和正则表达式匹配则执行。
1 | spring: |
使用 curl 测试,命令行输入:
1 | curl http://localhost:9066/order/createorder/1 -H "X-Request-Id:1234" |
则返回页面代码证明匹配成功。将参数-H “X-Request-Id:88”改为-H “X-Request-Id:spring”再次执行时返回404证明没有匹配。
通过 Cookie 匹配
Cookie Route Predicate 可以接收两个参数,一个是 Cookie name ,一个是正则表达式,路由规则会通过获取对应的 Cookie name 值和正则表达式去匹配,如果匹配上就会执行路由,如果没有匹配上则不执行。
1 | spring: |
使用 curl 测试,命令行输入:
1 | curl http://localhost:9066/order/createorder/1 --cookie "sessionId=test" |
则会返回页面代码,如果去掉–cookie “sessionId=test”,后台汇报 404 错误。
通过 Host 匹配
Host Route Predicate 接收一组参数,一组匹配的域名列表,这个模板是一个 ant 分隔的模板,用.号作为分隔符。它通过参数中的主机地址作为匹配规则。
1 | spring: |
使用 curl 测试,命令行输入:
1 | curl http://localhost:9066/order/createorder/1 -H "Host:www.baidu.com" |
经测试以上两种 host 均可匹配到 host_route 路由,去掉 host 参数则会报 404 错误。
通过请求方式匹配
可以通过是 POST、GET、PUT、DELETE 等不同的请求方式来进行路由。
1 | spring: |
使用 curl 测试,命令行输入:
1 | GET 请求 |
我们发现 get请求是成功的 PUT请求 返回不支持的 请求,因为我们后端没有支持PUT请求 所以也是gateway请求通过,其他 POST和DELETE是404 错误,返回 404 没有找到,证明没有匹配上路由。
通过请求路径匹配
Path Route Predicate 接收一个匹配路径的参数来判断是否走路由。
1 | spring: |
如果请求路径符合要求,则此路由将匹配,例如:/order/search。
使用 curl 测试,命令行输入:
1 | curl http://localhost:9066/order/search |
经过测试第一条命令可以正常获取到页面返回值,第二条和最后一个命令报404,证明路由是通过指定路由来匹配。
通过请求 ip 地址进行匹配
Predicate 也支持通过设置某个 ip 区间号段的请求才会路由,RemoteAddr Route Predicate 接受 cidr 符号(IPv4 或 IPv6 )字符串的列表(最小大小为1),例如 192.168.0.1/16 (其中 192.168.0.1 是 IP 地址,16 是子网掩码)。
1 | spring: |
可以将此地址设置为本机的 ip 地址进行测试。
果请求的远程地址是 192.168.1.10,则此路由将匹配。
通过时间匹配
Predicate 支持设置一个时间,在请求进行转发的时候,可以通过判断在这个时间之前或者之后进行转发。
Spring 是通过 ZonedDateTime 来对时间进行的对比,ZonedDateTime 是 Java 8 中日期时间功能里,用于表示带时区的日期与时间信息的类,ZonedDateTime 支持通过时区来设置时间,中国的时区是:Asia/Shanghai
。
After Route Predicate
After Route Predicate 是指在这个时间之后的请求都转发到目标地址。下面的示例是指,请求时间在 2020 年 1 月 1日12 点 0 分 0 秒之后的所有请求都转发到地址http://localhost:8083/。+08:00是指时间和 UTC 时间相差八个小时,时间地区为Asia/Shanghai。
1 | spring: |
使用 curl 测试,命令行输入:
1 | curl http://localhost:9066/order/createorder/1 |
Before Route Predicate
Before Route Predicate 刚好相反,在某个时间之前的请求的请求都进行转发。我们把上面路由规则中的 After 改为 Before,如下:
1 | spring: |
就表示在这个时间之前可以进行路由,在这时间之后停止路由,修改完之后重启项目再次访问地址http://localhost:9066
,页面会报 404 没有找到地址。
使用 curl 测试,命令行输入:
1 | curl http://localhost:9066/order/createorder/1 |
Between Route Predicate
除过在时间之前或者之后外,Gateway 还支持限制路由请求在某一个时间段范围内,可以使用 Between Route Predicate 来实现。
1 | spring: |
这样设置就意味着在这个时间段内可以匹配到此路由,超过这个时间段范围则不会进行匹配。通过时间匹配路由的功能很酷,可以用在限时抢购的一些场景中。
组合使用
上面为了演示各个 Predicate 的使用,我们是单个单个进行配置测试,其实可以将各种 Predicate 组合起来一起使用。
1 | spring: |
各种 Predicates 同时存在于同一个路由时,请求必须同时满足所有的条件才被这个路由匹配。
一个请求满足多个路由的断言条件时,请求只会被首个成功匹配的路由转发