Spring Cloud Gateway(译)(Part 1)

Spring Cloud Gateway

2.2.2.RELEASE

该项目提供了一个在Spring生态系统之上构建的API网关,包括:Spring 5,Spring Boot 2和Project Reactor。 Spring Cloud Gateway旨在提供一种简单而有效的方法来路由到API,并为它们提供跨领域的关注点,例如:安全性,监视/指标和弹性。

1.如何在项目中引入Spring Cloud Gateway

要想在项目中引入Spring Cloud Gateway,你需要引入一个starter, group ID 为 org.springframework.cloud 并且 artifact ID 为: spring-cloud-starter-gateway . 如下所示:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

如果已经引入了starter,但不希望启用网关,请设置 spring.cloud.gateway.enabled = false。

Spring Cloud Gateway基于Spring Boot 2.x,Spring WebFlux和Project Reactor构建。 结果是,当您使用Spring Cloud Gateway时,许多您熟悉的同步库(例如,Spring Data和Spring Security)和模式可能不适用。 如果您不熟悉这些项目,建议您在使用Spring Cloud Gateway之前先阅读它们的文档以熟悉一些新概念。

Spring Cloud Gateway需要Netty运行时是由Spring Boot和Spring Webflux提供的。 它不能在传统的Servlet容器中或构建成WAR使用。

2.词汇表

  • Route: 网关的基本构建块。 它由ID,目标URI,predicates集合和filters集合组成。 如果aggregate predicates为true,则匹配路由。

  • Predicate: 这就是 Java 8 Function Predicate. 传入的参数类型是 Spring Framework ServerWebExchange. 可以匹配HTTP请求中的所有内容,例如 headers 或 parameters 。

  • Filter: 这些是使用特定工厂构造的 Spring Framework GatewayFilter 实例。所以,你可以在发送下游请求之前或之后修改请求和响应。

3. 它是怎么工作的

下图从总体上概述了Spring Cloud Gateway的工作方式:

spring_cloud_gateway_diagram.png

客户端向Spring Cloud Gateway发出请求。 Gateway Handler Mapping 判断请求与路由是否匹配,若匹配,则将其发送到 Gateway Web Handler. 该handler通过该request的filter链来运行请求。 filters由虚线分隔的原因是, filters可以在发送代理请求之前和之后运行逻辑。 所有“pre” filter逻辑均被执行。 然后发出代理请求。 发出代理请求后,将运行“post” filter逻辑。

若路由中定义的URIs没有给定端口号,那么HTTP和HTTPS URI的默认端口值分别为80和443。

4.配置Route Predicate工厂和Gateway Filter 工厂

有两种配置predicates 和 filters 的方法:简洁配置和完全扩展的参数。 下面的大多数示例都使用简洁配置。

名称和参数名称将在每个部分的第一个或两个句子中以代码形式列出。 参数通常按简洁配置所需的顺序列出。

4.1简洁配置

简洁配置由过滤器名称识别,后跟一个等号(=),然后是由逗号分隔参数值(,)。

application.yml

spring:
  cloud:
    gateway:
      routes:
      - id: after_route
        uri: https://example.org
        predicates:
        - Cookie=mycookie,mycookievalue

上一个示例定义了Cookie Route Predicate Factory 包含了两个参数,这两个参数是cookie名称【mycookie】和与之匹配的值【mycookievalue】。

4.2 完全扩展的参数

完全扩展的参数看起来更像带有 name/value 键值对的标准Yaml配置. 通常, 将有一个name 键 和一个 args 键. args 键是用于配置 predicate 或 filter的键值对映射(map).

application.yml

spring:
  cloud:
    gateway:
      routes:
      - id: after_route
        uri: https://example.org
        predicates:
        - name: Cookie
          args:
            name: mycookie
            regexp: mycookievalue

这是上面显示的Cookie predicate 的简洁配置的完整配置。

5. Route Predicate 工厂

Spring Cloud Gateway将路由匹配作为Spring WebFlux HandlerMapping基础架构的一部分。 Spring Cloud Gateway包括许多内置的Route Predicate工厂。 所有这些predicates 都与HTTP请求的不同属性匹配。 您可以将多个Route Predicate工厂组合使用。

5.1. The After Route Predicate Factory

这个After route predicate factory 包括一个参数, 即 datetime (这是一个java的 ZonedDateTime). 这个predicate 匹配发生在特定时间之后的请求 . 下面是一个 after route predicate的配置示例:

Example 1. application.yml

spring:
  cloud:
    gateway:
      routes:
      - id: after_route
        uri: https://example.org
        predicates:
        - After=2017-01-20T17:42:47.789-07:00[America/Denver]

这个route 匹配任何发生在 20, 2017 17:42 Mountain Time (Denver) 之后的请求.

5.2. The Before Route Predicate Factory

这个Before route predicate factory 包括一个参数, 即 datetime (这是一个java的 ZonedDateTime). 这个predicate 匹配发生在特定datetime 之前的请求. 下面是一个 before route predicate 的配置示例:

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]

这个route 匹配任何发生在 Jan 20, 2017 17:42 Mountain Time (Denver)之前的请求.

5.3. The Between Route Predicate Factory

Between route predicate factory 有连个参数, datetime1datetime2 它们是 java ZonedDateTime 对象. 这个predicate 匹配发生在 datetime1datetime2之间的请求. 而且datetime2 参数必须在 datetime1之后. 下面是一个 between route predicate 的配置示例:

Example 3. application.yml

spring:
  cloud:
    gateway:
      routes:
      - id: between_route
        uri: https://example.org
        predicates:
        - Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]

这个route 匹配 Jan 20, 2017 17:42 Mountain Time (Denver) 之后而且 Jan 21, 2017 17:42 Mountain Time (Denver)之前的请求. 这对于维护时段可能很有用.

5.4. The Cookie Route Predicate Factory

The Cookie route predicate factory 有两个参数, 即cookie nameregexp (这是一个java正则表达式). 这个predicate 匹配 有给定的名称的 cookies 并且其值符合正在表达式. 下面是cookie route predicate factory的配置示例:

Example 4. application.yml

spring:
  cloud:
    gateway:
      routes:
      - id: cookie_route
        uri: https://example.org
        predicates:
        - Cookie=chocolate, ch.p

这个route 匹配拥有名称为chocolate 并且值匹配 ch.p 正则表达式.

5.5. The Header Route Predicate Factory

这个Header route predicate factory 包含两个参数, header nameregexp (java正则表达式). 这个predicate 匹配给定名称的 header 并且值匹配 正则表达式. 下面是 header route predicate的配置示例:

Example 5. application.yml

spring:
  cloud:
    gateway:
      routes:
      - id: header_route
        uri: https://example.org
        predicates:
        - Header=X-Request-Id, \d+

如果请求有一个名为 X-Request-Id 的header 并且值匹配 \d+ 正则表达式(其值为一个或多个数字),则此路由匹配

5.6. The Host Route Predicate Factory

Host route predicate factory 拥有一个参数: host name patterns集合. 该模式是一种Ant样式模式使用 . 作为分隔符 . predicates 匹配 Host header 作为匹配模式. 下面是host route predicate的配置示例:

Example 6. application.yml

spring:
 cloud:
   gateway:
     routes:
     - id: host_route
       uri: https://example.org
       predicates:
       - Host=**.somehost.org,**.anotherhost.org

还支持URI模板变量 (例如 {sub}.myhost.org) .

如果请求的 Host 头为 www.somehost.orgbeta.somehost.orgwww.anotherhost.org, 则该模式匹配.

该predicate 提取URI模板变量(如上例中定义的 sub) 作为名称和值的映射. 并使用ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE 中定义的key 将其存储在ServerWebExchange.getAttributes() 中 . 这些值会在 GatewayFilter factories 中使用.

5.7. The Method Route Predicate Factory

Method Route Predicate Factory 拥有一个名为methods 的参数,该参数是一个集合,包含一个或多个HTTP 方法(GETHEADPOSTPUTDELETECONNECTOPTIONSTRACEPATCH). 下面是 method route predicate 配置示例:

Example 7. application.yml

spring:
  cloud:
    gateway:
      routes:
      - id: method_route
        uri: https://example.org
        predicates:
        - Method=GET,POST

该route 匹配请求方法是 GETPOST.

5.8. The Path Route Predicate Factory

Path Route Predicate Factory 携带两个参数: 一个 Spring PathMatcher patterns 列表和一ge个名为matchOptionalTrailingSeparator可选标志. 下面是path route predicate的配置示例:

Example 8. application.yml

spring:
  cloud:
    gateway:
      routes:
      - id: path_route
        uri: https://example.org
        predicates:
        - Path=/red/{segment},/blue/{segment}

该 route 匹配以下请求路径, 如: /red/1 or /red/blue or /blue/green.

该predicate 提取URI模板变量(如上例中定义的 segment) 作为名称和值的映射. 并使用ServerWebExchangeUtils.URI_TEMPLATE_VARIABLES_ATTRIBUTE 中定义的key 将其存储在ServerWebExchange.getAttributes() 中 . 这些值会在 GatewayFilter factories 中使用.

可以使用程序的方法 (叫get) 来简化对这些变量的访问. 下面的示例演示如何使用 get 方法:

Map<String, String> uriVariables = ServerWebExchangeUtils.getPathPredicateVariables(exchange);

String segment = uriVariables.get("segment");

5.9. The Query Route Predicate Factory

Query route predicate factory 有两个参数: 必选参数param 和可选参数 regexp (这是一个java正则表达式). 下面是query route predicate配置示例:

Example 9. application.yml

spring:
  cloud:
    gateway:
      routes:
      - id: query_route
        uri: https://example.org
        predicates:
        - Query=green

如果请求中包含green 查询参数,则该路由匹配.

application.yml

spring:
  cloud:
    gateway:
      routes:
      - id: query_route
        uri: https://example.org
        predicates:
        - Query=red, gree.

该preceding route matches 匹配请求中包含 red 的查询参数并且其值匹配 gree. 正则表达式, 所以 greengreet 都匹配

5.10. The RemoteAddr Route Predicate Factory

RemoteAddr route predicate factory 包含一个源列表(只有一个值)参数, 这些源是CIDR标记(IPv4或IPv6)字符串,例如 192.168.0.1/16 (其中192.168.0.1 是 IP 地址,而16 是子网掩码). 下面示例是RemoteAddr route predicate 的配置示例:

Example 10. application.yml

spring:
  cloud:
    gateway:
      routes:
      - id: remoteaddr_route
        uri: https://example.org
        predicates:
        - RemoteAddr=192.168.1.1/24

如果请求的远程地址是 192.168.1.10,则此路由匹配.

5.11. The Weight Route Predicate Factory

Weight route predicate factory 携带两个参数: groupweight (int类型). 权重是按组计算的. 下面是 weight route predicate 的配置示例:

Example 11. application.yml

spring:
  cloud:
    gateway:
      routes:
      - id: weight_high
        uri: https://weighthigh.org
        predicates:
        - Weight=group1, 8
      - id: weight_low
        uri: https://weightlow.org
        predicates:
        - Weight=group1, 2

该route 会将大约80% 的流量转发到 weighthigh.org, 大约20%的流量转发到 weighlow.org

5.11.1. Modifying the Way Remote Addresses Are Resolved

默认情况下,RemoteAddr route predicate factory使用传入请求中的远程地址。 如果Spring Cloud Gateway位于代理层之后,则可能与实际的客户端IP地址不匹配。

你可以通过实现 RemoteAddressResolver来自定义解析远程地址的方式. Spring Cloud Gateway 内置了一个非default的名为XForwardedRemoteAddressResolver解析器,它是基于 X-Forwarded-For header 请求头的。

XForwardedRemoteAddressResolver 有两个静态构造方法, 它们采取了不同的安全策略:

  • XForwardedRemoteAddressResolver::trustAll 返回一个 RemoteAddressResolver ,该解析器始终采用 X-Forwarded-For 头中找到的第一个IP地址. 这种方法容易受到欺骗的攻击, 因为恶意客户端可能会为 X-Forwarded-For设置初始值, 该初始值将被解析器接受。.
  • XForwardedRemoteAddressResolver::maxTrustedIndex 配置一个索引, 该索引与在 Spring Cloud Gateway 前面运行的受信任基础架构的数量相关. 比如只能通过HAProxy访问 Spring Cloud Gateway, 则索引值应该设置为1.如果在访问Spring Cloud Gateway 之前需要两跳可信基础架构, 那么索引值应该为2.

例如以下标头值:

X-Forwarded-For: 0.0.0.1, 0.0.0.2, 0.0.0.3

下面不同maxTrustedIndex 值产生不同的远程地址:

maxTrustedIndex result
[Integer.MIN_VALUE,0] (invalid, IllegalArgumentException during initialization)
1 0.0.0.3
2 0.0.0.2
3 0.0.0.1
[4, Integer.MAX_VALUE] 0.0.0.1

以下示例显示了如何使用Java实现相同的配置:

Example 12. GatewayConfig.java

RemoteAddressResolver resolver = XForwardedRemoteAddressResolver
    .maxTrustedIndex(1);

...

.route("direct-route",
    r -> r.remoteAddr("10.1.1.1", "10.10.1.1/24")
        .uri("https://downstream1")
.route("proxied-route",
    r -> r.remoteAddr(resolver, "10.10.1.1", "10.10.1.1/24")
        .uri("https://downstream2")
)
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,734评论 6 505
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,931评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,133评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,532评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,585评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,462评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,262评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,153评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,587评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,792评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,919评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,635评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,237评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,855评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,983评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,048评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,864评论 2 354

推荐阅读更多精彩内容