赞
踩
之前学习了nacos,sentinel,并对sentinel进行了持久化改造。现在轮到了gateway了
本文使用版本为
Spring Cloud Gateway是Spring Cloud官方推出的第二代网关框架,取代Zuul 网关。网关作为流量的,在微服务系统中有着非常作用。 注:由于不是Sevlet容器,所以他不能打成war包, 只支持SpringBoot2.X不 支持1.x.
该项目提供了一个用于在Spring WebFlux之上构建API网关的库。 Spring Cloud Gateway旨在提供一种简单而有效的方法来路由到API,并为它们提供跨领域的关注点,例如:安全性,监视/指标和弹性。
建立在Spring Framework 5,Project Reactor和Spring Boot 2.0之上
能够匹配任何请求属性上的路由。
谓词和过滤器特定于路由。
断路器集成。
Spring Cloud DiscoveryClient集成
易于编写的谓词和过滤器
请求速率限制
路径改写
网关作用: 路由转发、权限校验、限流控制等作用
没有网关:
使用网关:
什么是路由? 路由是构建网关的基本模块,由ID,目标URI,一系列的断言和过滤器组成,如果断言为true,则匹配该路由 什么是断言? 开发人员可以匹配HTTP请求中的所有内容(例如请求头或请求参数),如果请求与断言相匹配则进行路由 什么是过滤? 值得是Spring框架中GatewayFilter的实例,使用过滤器,可以使请求在被路由前/后进行修改
Route(路由)
:路由是网关的基本单元,由ID、URI、一组Predicate、一组Filter组成,根据Predicate进行匹配转发。
源码
- //
- // Source code recreated from a .class file by IntelliJ IDEA
- // (powered by Fernflower decompiler)
- //
-
- package org.springframework.cloud.gateway.route;
-
- import java.net.URI;
- import java.util.ArrayList;
- import java.util.HashMap;
- import java.util.List;
- import java.util.Map;
- import java.util.Objects;
- import javax.validation.Valid;
- import javax.validation.ValidationException;
- import javax.validation.constraints.NotEmpty;
- import javax.validation.constraints.NotNull;
- import org.springframework.cloud.gateway.filter.FilterDefinition;
- import org.springframework.cloud.gateway.handler.predicate.PredicateDefinition;
- import org.springframework.util.StringUtils;
- import org.springframework.validation.annotation.Validated;
-
- @Validated
- public class RouteDefinition {
- private String id;
- @NotEmpty
- @Valid
- private List<PredicateDefinition> predicates = new ArrayList();
- @Valid
- private List<FilterDefinition> filters = new ArrayList();
- @NotNull
- private URI uri;
- private Map<String, Object> metadata = new HashMap();
- private int order = 0;
-
- public RouteDefinition() {
- }
-
- public RouteDefinition(String text) {
- int eqIdx = text.indexOf(61);
- if (eqIdx <= 0) {
- throw new ValidationException("Unable to parse RouteDefinition text '" + text + "', must be of the form name=value");
- } else {
- this.setId(text.substring(0, eqIdx));
- String[] args = StringUtils.tokenizeToStringArray(text.substring(eqIdx + 1), ",");
- this.setUri(URI.create(args[0]));
-
- for(int i = 1; i < args.length; ++i) {
- this.predicates.add(new PredicateDefinition(args[i]));
- }
-
- }
- }
-
- public String getId() {
- return this.id;
- }
-
- public void setId(String id) {
- this.id = id;
- }
-
- public List<PredicateDefinition> getPredicates() {
- return this.predicates;
- }
-
- public void setPredicates(List<PredicateDefinition> predicates) {
- this.predicates = predicates;
- }
-
- public List<FilterDefinition> getFilters() {
- return this.filters;
- }
-
- public void setFilters(List<FilterDefinition> filters) {
- this.filters = filters;
- }
-
- public URI getUri() {
- return this.uri;
- }
-
- public void setUri(URI uri) {
- this.uri = uri;
- }
-
- public int getOrder() {
- return this.order;
- }
-
- public void setOrder(int order) {
- this.order = order;
- }
-
- public Map<String, Object> getMetadata() {
- return this.metadata;
- }
-
- public void setMetadata(Map<String, Object> metadata) {
- this.metadata = metadata;
- }
-
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- } else if (o != null && this.getClass() == o.getClass()) {
- RouteDefinition that = (RouteDefinition)o;
- return this.order == that.order && Objects.equals(this.id, that.id) && Objects.equals(this.predicates, that.predicates) && Objects.equals(this.filters, that.filters) && Objects.equals(this.uri, that.uri) && Objects.equals(this.metadata, that.metadata);
- } else {
- return false;
- }
- }
-
- public int hashCode() {
- return Objects.hash(new Object[]{this.id, this.predicates, this.filters, this.uri, this.metadata, this.order});
- }
-
- public String toString() {
- return "RouteDefinition{id='" + this.id + '\'' + ", predicates=" + this.predicates + ", filters=" + this.filters + ", uri=" + this.uri + ", order=" + this.order + ", metadata=" + this.metadata + '}';
- }
- }
Spring Cloud GateWay 工作流程如下所示:
客户端向Spring Cloud Gateway
发出请求。如果网关处理程序映射确定请求与路由匹配,则将其发送到网关Web处理程序。此处理程序运行时通过特定于请求的筛选链发送请求。过滤器被虚线分隔的原因是过滤器可以在发送代理请求之前或之后执行逻辑。执行所有“预”过滤逻辑,然后发出代理请求。在发出代理请求后,将执行“post”过滤器逻辑。
然后让我们先通过几个小demo先了解一下gateway的大概使用,然后我们在深入了解更多相关知识,这样比较容易理解。
pom
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-gateway</artifactId>
- </dependency>
-
- <dependency>
- <groupId>org.springframework.cloud</groupId>
- <artifactId>spring-cloud-starter-loadbalancer</artifactId>
- </dependency>
yml
- server:
- port: 9090
-
- spring:
- application:
- name: @artifactId@
- cloud:
- nacos:
- discovery:
- server-addr: 172.17.169.81:8848
- gateway:
- discovery:
- locator:
- enabled: true # 开启从注册中心动态创建路由的功能,利用微服务名称j进行路由
- routes:
- - id: payment_route # 路由的id,没有规定规则但要求唯一,建议配合服务名
- #匹配后提供服务的路由地址
- uri: lb://payment-service
- #uri: http://localhost:8070
- predicates:
- - Path=/payment/get/** # 断言,路径相匹配的进行路由
- #- After=2017-01-20T17:42:47.789-07:00[America/Denver]
- #- Before=2017-01-20T17:42:47.789-07:00[America/Denver]
- #- Cookie=username,zzyy
- #- Header=X-Request-Id, \d+ #请求头要有X-Request-Id属性,并且值为正数
- #- Host=**.atguigu.com
- #- Method=GET
- #- Query=username, \d+ # 要有参数名username并且值还要是正整数才能路由
- # 过滤
- #filters:
- # - AddRequestHeader=X-Request-red, blue
- #- id: payment_route2
- # uri: http://localhost:8001
- # predicates:
- #Path=/payment/lb/** #断言,路径相匹配的进行路由
-
启动类
- package com.liu.learn;
-
- import org.springframework.boot.SpringApplication;
- import org.springframework.boot.autoconfigure.SpringBootApplication;
- import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
-
- @SpringBootApplication
- @EnableDiscoveryClient
- public class GatewayApplication {
-
- public static void main(String[] args) {
- SpringApplication.run(GatewayApplication.class, args);
- }
-
- }
理解 predicates:断言,加入访问路径符合/payment/get/** 格式便会转发到uri配置的服务下
uri: lb://payment-service 例如我们访问
http://localhost:9090/payment/get/31 便会转发到http://localhost:8070/payment/get/31 提供服务。
下面详细介绍:断言
至此我们应该对gateway的使用方法有个大体的了解和印象了,然后让我们继续学习相关知识。
开发人员可以匹配HTTP请求中的所有内容(例如请求头或请求参数),如果请求与断言相匹配则进行路由。
也就是我们进行路由跳转的条件。官方文档:
https://docs.spring.io/spring-cloud-gateway/docs/3.0.2/reference/html/#gateway-request-predicates-factories
在之前我们的配置中使用了如下Path 这样一种断言。
- Path=/payment/get/**
但是实际上我们还有好多种断言,我们可以通过官方文档查询到,可以看我下面的连接,但是最好自己去官网找到自己响应版本的文档看(以后版本会不会出新的断言),并且官方文档有响应的例子以及解释。
https://docs.spring.io/spring-cloud-gateway/docs/3.0.2/reference/html/#gateway-request-predicates-factories
在我们的gateway服务启动时候,控制台业务打印出来我们断言的方式。
左图官方文档,右图启动控制台信息。
Spring Cloud Gateway将路由匹配作为Spring WebFlux HandlerMapping基础架构的一部分。 Spring Cloud Gateway包括许多内置的路由断言工厂。所有这些谓词都与HTTP请求的不同属性匹配。您可以将多个路由断言工厂与逻辑和语句结合使用。
断言种类:
After
该断言匹配在指定日期时间之后发生的请求。
- After=2021-04-20T17:42:47.789-07:00[America/Denver]
Before
该断言匹配在指定日期时间之前发生的请求。
- Before=2021-06-20T17:42:47.789-07:00[America/Denver]
Between 该断言匹配在两个指定日期时间之间发生的请求。
- Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2021-01-21T17:42:47.789-07:00[America/Denver]
Cookie
该断言采用两个参数,即cookie名称和一个regexp(这是Java正则表达式)。该断言匹配具有给定名称且其值与正则表达式匹配的cookie。
- Cookie=chocolate, ch.p
Header
该断言采用两个参数,header名称和一个regexp(这是Java正则表达式)。该断言与具有给定名称的header匹配,该header的值与正则表达式匹配。
- Header=X-Request-Id, \d+
Host
该断言采用一个参数:主机名模式列表。该模式是带有的Ant样式的模式。作为分隔符。
该断言匹配与模式匹配的Host标头。
- Host=**.somehost.org,**.anotherhost.org
Method
该断言采用方法参数,该参数是一个或多个参数:要匹配的HTTP方法。
- Method=GET,POST
Path
该断言采用两个参数:Spring PathMatcher模式列表和一个称为matchTrailingSlash的可选标志(默认为true)。
- Path=/red/{segment},/blue/{segment}
Query
该断言采用两个参数:必需的参数和可选的regexp(这是Java正则表达式)。如果请求包含匹配配置的查询参数,则路由匹配。
- Query=green
RemoteAddr
该断言采用sources列表(最小大小为1),这些源是CIDR标记(IPv4或IPv6)字符串,例如192.168.0.1/16(其中192.168.0.1是IP地址,而16是子网掩码) )。
- RemoteAddr=192.168.1.1/24
ReadBody
文档中没写这个,但是启动的时候控制台显示了,RoutePredicateFactory的实现类ReadBodyRoutePredicateFactory写了一点注释。断言可读取主体并应用用户提供的断言在主体上运行。
主体被缓存在内存中,因此后续对断言的调用无需再次反序列化。
Weight
文档中没写这个,但是启动的时候控制台显示了,RoutePredicateFactory的实现类WeightRoutePredicateFactory也没写啥注释
下面对各个断言进行介绍:
- server:
- port: 9090
-
- spring:
- application:
- name: @artifactId@
- cloud:
- nacos:
- discovery:
- server-addr: 172.17.169.81:8848
- gateway:
- discovery:
- locator:
- enabled: true # 开启从注册中心动态创建路由的功能,利用微服务名称j进行路由
- routes:
- - id: payment_route # 路由的id,没有规定规则但要求唯一,建议配合服务名
- #匹配后提供服务的路由地址
- uri: lb://payment-service
- #uri: http://localhost:8070
- predicates:
- - Path=/payment/get/** # 断言,路径相匹配的进行路由
-
-
uri: lb://payment-service 此处服务名一定不要包含字母 大小写 - 横杠以外的字符
先来解释下route
的组成部分:
id
:路由的IDuri
:匹配路由的转发地址predicates
:配置该路由的断言,通过PredicateDefinition
类进行接收配置。在上面的配置中,当访问http://localhost:9090/
payment/get/21
时就会被自动转发到http://localhost:8070/payment/get/31, 这里要注意完全匹配Path
的值时才会进行路由转发。
此处可为
- uri: lb://payment-service
- #uri: http://localhost:8070 若写为lb格式,服务名为注册中心服务名,若 多个服务可以负载均衡,底下的为服务器地址写死的,但此处写死更容易讲解
当部署有访问时间限制的接口时,我们可以通过Before Predicate
来完成某一个时间点之前允许访问,过时后则不允许转发请求到具体的服务,配置如下所示:
- server:
- port: 9090
-
- spring:
- application:
- name: @artifactId@
- cloud:
- nacos:
- discovery:
- server-addr: 172.17.169.81:8848
- gateway:
- discovery:
- locator:
- enabled: true # 开启从注册中心动态创建路由的功能,利用微服务名称j进行路由
- routes:
- - id: payment_route # 路由的id,没有规定规则但要求唯一,建议配合服务名
- #匹配后提供服务的路由地址
- #uri: http://localhost:8070/payment/get/31
- uri: lb://payment-service
- #uri: http://localhost:8070
- predicates:
-
- - Before=2022-01-20T17:42:47.789-07:00[America/Denver]
-
-
-
官网:
Example 2. application.yml
- spring:
- cloud:
- gateway:
- routes:
- - id: before_route
- uri: https://example.org
- predicates:
- - Before=2017-01-20T17:42:47.789-07:00[America/Denver]
This route matches any request made before Jan 20, 2017 17:42 Mountain Time (Denver).
在上面配置中,我们允许2022-01-20
日凌晨之前通过路由转发到
http://localhost:8070进行处理
通过查看org.springframework.cloud.gateway.handler.predicate.BeforeRoutePredicateFactory
源码我们发现,Spring Cloud Gateway
的Before
断言采用的ZonedDateTime
进行匹配时间,这里要注意存在时区的问题,需要配置[Asia/Shanghai]
作为中国时区。
After Predicate
与Before
配置使用一致,匹配某一个时间点之后允许路由转发
那如果是一个时间段内允许请求转发,通过Before
、After
组合配置也可以完成,不过Spring Cloud Gateway
还是提供了Between
方式,如下所示:
- spring:
- cloud:
- gateway:
- routes:
- - id: payment_route
- uri: lb://payment-service
- predicates:
- - Between=2019-04-29T00:00:00+08:00[Asia/Shanghai], 2019-05-01T00:00:00+08:00[Asia/Shanghai]
在上面配置中,允许在2019-04-29
日凌晨后 & 2019-05-01
凌晨之前请求转发
Spring Cloud Gateway
还提供了根据Cookie
值的方式匹配转发请求,如果请求中所携带的Cookie
值与配置的Predicate
匹配,那么就可以被允许转发到指定地址,如下所示:
- server:
- port: 9090
-
- spring:
- application:
- name: @artifactId@
- cloud:
- nacos:
- discovery:
- server-addr: 172.17.169.81:8848
- gateway:
- discovery:
- locator:
- enabled: true # 开启从注册中心动态创建路由的功能,利用微服务名称j进行路由
- routes:
- - id: payment_route # 路由的id,没有规定规则但要求唯一,建议配合服务名
- #匹配后提供服务的路由地址
- #uri: http://localhost:8070/payment/get/31
- uri: lb://payment-service
- #uri: http://localhost:8070
- predicates:
- #- Path=/payment/get/** # 断言,路径相匹配的进行路由
- #- After=2021-01-20T17:42:47.789-07:00[America/Denver]
- #- Before=2022-01-20T17:42:47.789-07:00[America/Denver]
- - Cookie=username,liu
- #- Header=X-Request-Id, \d+ #请求头要有X-Request-Id属性,并且值为正数
- #- Host=**.atguigu.com
- #- Method=GET
- #- Query=username, \d+ # 要有参数名username并且值还要是正整数才能路由
- # 过滤
- #filters:
- # - AddRequestHeader=X-Request-red, blue
- #- id: payment_route2
- # uri: http://localhost:8001
- # predicates:
- #Path=/payment/lb/** #断言,路径相匹配的进行路由
-
测试Cookie方式转发:
curl http://localhost:9090 --cookie "username=liu"
通过上面方式我们是可以成功转发请求的,如果我们修改Cookie
的值,就会导致无法转发
,出现错误。
Spring Cloud Gateway
可以根据发送请求的Header
信息进行匹配转发,加入我们可以根据X-Request-Id
的值进行匹配,如下所示:
- server:
- port: 9090
-
- spring:
- application:
- name: @artifactId@
- cloud:
- nacos:
- discovery:
- server-addr: 172.17.169.81:8848
- gateway:
- discovery:
- locator:
- enabled: true # 开启从注册中心动态创建路由的功能,利用微服务名称j进行路由
- routes:
- - id: payment_route # 路由的id,没有规定规则但要求唯一,建议配合服务名
- #匹配后提供服务的路由地址
- #uri: http://localhost:8070/payment/get/31
- uri: lb://payment-service
- #uri: http://localhost:8070
- predicates:
- #- Path=/payment/get/** # 断言,路径相匹配的进行路由
- #- After=2021-01-20T17:42:47.789-07:00[America/Denver]
- #- Before=2022-01-20T17:42:47.789-07:00[America/Denver]
- #- Cookie=username,liu
- - Header=X-Request-Id, \d+ #请求头要有X-Request-Id属性,并且值为正数
- #- Host=**.atguigu.com
- #- Method=GET
- #- Query=username, \d+ # 要有参数名username并且值还要是正整数才能路由
- # 过滤
- #filters:
- # - AddRequestHeader=X-Request-red, blue
- #- id: payment_route2
- # uri: http://localhost:8001
- # predicates:
- #Path=/payment/lb/** #断言,路径相匹配的进行路由
-
在上面配置中,如果X-Request-Id
的值为数字,那么就可以转发
Spring Cloud Gateway
可以根据Host
主机名进行匹配转发,如果我们的接口只允许**.liuliu.com
域名进行访问,那么配置如下所示:
- server:
- port: 9090
-
- spring:
- application:
- name: @artifactId@
- cloud:
- nacos:
- discovery:
- server-addr: 172.17.169.81:8848
- gateway:
- discovery:
- locator:
- enabled: true # 开启从注册中心动态创建路由的功能,利用微服务名称j进行路由
- routes:
- - id: payment_route # 路由的id,没有规定规则但要求唯一,建议配合服务名
- #匹配后提供服务的路由地址
- #uri: http://localhost:8070/payment/get/31
- uri: lb://payment-service
- #uri: http://localhost:8070
- predicates:
- #- Path=/payment/get/** # 断言,路径相匹配的进行路由
- #- After=2021-01-20T17:42:47.789-07:00[America/Denver]
- #- Before=2022-01-20T17:42:47.789-07:00[America/Denver]
- #- Cookie=username,liu
- #- Header=X-Request-Id, \d+ #请求头要有X-Request-Id属性,并且值为正数
- - Host=**.baidu.com
- #- Method=GET
- #- Query=username, \d+ # 要有参数名username并且值还要是正整数才能路由
- # 过滤
- #filters:
- # - AddRequestHeader=X-Request-red, blue
- #- id: payment_route2
- # uri: http://localhost:8001
- # predicates:
- #Path=/payment/lb/** #断言,路径相匹配的进行路由
-
- 1. curl http://localhost:9090 -H "Host: baidu.com" // 匹配
- 2. curl http://localhost:9090 -H "Host: api.baidu.com" // 匹配
- 3. curl http://localhost:9090 -H "Host: admin.baidu.com" // 匹配
- 3. curl http://localhost:9090 -H "Host: biiiii.com" // 不匹配
Rest
请求风格的接口内往往会存在多种请求方式的接口,如果我们的接口只允get请求访问,那么配置如下所示:
- server:
- port: 9090
-
- spring:
- application:
- name: @artifactId@
- cloud:
- nacos:
- discovery:
- server-addr: 172.17.169.81:8848
- gateway:
- discovery:
- locator:
- enabled: true # 开启从注册中心动态创建路由的功能,利用微服务名称j进行路由
- routes:
- - id: payment_route # 路由的id,没有规定规则但要求唯一,建议配合服务名
- #匹配后提供服务的路由地址
- #uri: http://localhost:8070/payment/get/31
- uri: lb://payment-service
- #uri: http://localhost:8070
- predicates:
- #- Path=/payment/get/** # 断言,路径相匹配的进行路由
- #- After=2021-01-20T17:42:47.789-07:00[America/Denver]
- #- Before=2022-01-20T17:42:47.789-07:00[America/Denver]
- #- Cookie=username,liu
- #- Header=X-Request-Id, \d+ #请求头要有X-Request-Id属性,并且值为正数
- #- Host=**.baidu.com
- - Method=GET
- #- Query=username, \d+ # 要有参数名username并且值还要是正整数才能路由
- # 过滤
- #filters:
- # - AddRequestHeader=X-Request-red, blue
- #- id: payment_route2
- # uri: http://localhost:8001
- # predicates:
- #Path=/payment/lb/** #断言,路径相匹配的进行路由
-
发送GET
请求测试:
Spring Cloud GateWay
还支持根据指定的参数进行匹配,Query
方式的Predicate
也有两种方式匹配情况,如下所示:
- server:
- port: 9090
-
- spring:
- application:
- name: @artifactId@
- cloud:
- nacos:
- discovery:
- server-addr: 172.17.169.81:8848
- gateway:
- discovery:
- locator:
- enabled: true # 开启从注册中心动态创建路由的功能,利用微服务名称j进行路由
- routes:
- - id: payment_route # 路由的id,没有规定规则但要求唯一,建议配合服务名
- #匹配后提供服务的路由地址
- #uri: http://localhost:8070/payment/get/31
- uri: lb://payment-service
- #uri: http://localhost:8070
- predicates:
- #- Path=/payment/get/** # 断言,路径相匹配的进行路由
- #- After=2021-01-20T17:42:47.789-07:00[America/Denver]
- #- Before=2022-01-20T17:42:47.789-07:00[America/Denver]
- #- Cookie=username,liu
- #- Header=X-Request-Id, \d+ #请求头要有X-Request-Id属性,并且值为正数
- #- Host=**.baidu.com
- #- Method=GET
- - Query=username, \d+ # 要有参数名username并且值还要是正整数才能路由
- # 过滤
- #filters:
- # - AddRequestHeader=X-Request-red, blue
- #- id: payment_route2
- # uri: http://localhost:8001
- # predicates:
- #Path=/payment/lb/** #断言,路径相匹配的进行路由
-
断言就讲解这么多下一讲,讲解过滤器
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。