spring cloud Greenwich 学习笔记(七)spring cloud gateway 教程入门

欢迎关注本人公众号

在这里插入图片描述

文章目录

  • 欢迎关注本人公众号
  • 概述
  • helloworld
  • 使用Hystrix
  • predicate
    • Path
    • After
    • Before
    • Between
    • cookie
    • header
    • Host
    • Get
    • Query
  • Filter
    • Filter生命周期
    • FilterFactory
      • AddRequestHeader GatewayFilter Factory
      • AddRequestParameter GatewayFilter Factory
      • Hystrix GatewayFilter Factory
      • PrefixPath GatewayFilter Factory
      • RequestRateLimiter GatewayFilter Factory
      • RedirectTo GatewayFilter Factory
      • RemoveRequestHeader GatewayFilter Factory
      • RemoveResponseHeader GatewayFilter Factory
      • RewritePath GatewayFilter Factory
      • StripPrefix GatewayFilter Factory

springcloud系列学习笔记目录参见博主专栏 spring boot 2.X/spring cloud Greenwich。
由于是一系列文章,所以后面的文章可能会使用到前面文章的项目。文章所有代码都已上传GitHub:https://github.com/liubenlong/springcloudGreenwichDemo
本系列环境:Java11;springboot 2.1.1.RELEASE;springcloud Greenwich.RELEASE;MySQL 8.0.5;

概述

Spring Cloud Gateway是Spring Cloud官方推出的第二代网关框架,取代Zuul网关。网关在微服务系统中有着非常作用,网关常见的功能有路由转发、权限校验、限流控制等作用。

spring cloud 早期版本中使用的是zuul 1.X 。后来zuul 1.X 升级到 zuul 2.x 的进度过于缓慢,springcloud官方图案就就自行研发了这个网关。

Spring Cloud Gateway是基于Project Reactor的,是响应式的架构。它依赖了reactor以及webflux。关于响应式编程请参考笔者系列文章:

  • 响应式编程规范
  • java设计模式之 观察者模式
  • reactor3 源码分析
  • java9 响应式编程支持

helloworld

本节看一下springcloud gateway如何使用。参考值官网教程https://spring.io/guides/gs/gateway/。
我们这里引入相关依赖:

<dependency>
    <groupId>org.springframework.cloudgroupId>
    <artifactId>spring-cloud-starter-gatewayartifactId>
dependency>
<dependency>
    <groupId>org.springframework.cloudgroupId>
    <artifactId>spring-cloud-starter-netflix-hystrixartifactId>
dependency>

application.yml中的配置:

server:
  port: 8095

# 最佳实践:springcloud应用都要指定application.name
spring:
  application:
    name: springcloud-gateway-helloworld

编写main方法,我这里将bean的配置放到了main方法中:

package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    public RouteLocator myRoutes(RouteLocatorBuilder builder) {
        return builder.routes()
                .route(p -> p
                        .path("/get")
                        .filters(f -> f.addRequestHeader("Hello", "World"))
                        .uri("http://httpbin.org:80"))
                .build();
    }
}

在上面的myRoutes方法用于创建路由,该方法中可以添加各种predicates和filters。predicates断言的意思,顾名思义就是根据具体的请求的规则,由具体的route去处理,filters是各种过滤器,用来对请求做各种判断和修改。

上面简单路由的规则是将/get的请求路由到http://httpbin.org:80/get,并且在header中添加参数Hello=World

启动服务,可以从日志输出中看到,springcloud gateway加载了各种路由断言工厂:
在这里插入图片描述
访问http://127.0.0.1:8095/get输出:

{
    "args": {},
    "headers": {
        "Accept": "*/*",
        "Accept-Encoding": "gzip, deflate, br",
        "Accept-Language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7",
        "Cache-Control": "no-cache",
        "Connection": "close",
        "Forwarded": "proto=http;host=\"127.0.0.1:8095\";for=\"127.0.0.1:52306\"",
        "Hello": "World",
        "Host": "httpbin.org",
        "Postman-Token": "fbcbbf84-b40b-d9b4-b62c-dea1105c3019",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36",
        "X-Forwarded-Host": "127.0.0.1:8095",
        "X-Postman-Interceptor-Id": "8f8a8b77-9faa-af0f-da6a-203d2049af46"
    },
    "origin": "127.0.0.1, 103.126.92.86",
    "url": "http://127.0.0.1:8095/get"
}

备注:httpbin这个网站能测试 HTTP 请求和响应的各种信息,比如 cookie、ip、headers 和登录验证等,且支持 GET、POST 等多种方法,对 web 开发和测试很有帮助。它用 Python + Flask 编写,是一个开源项目。其官网地址是https://httpbin.org/ 。可以httpbin的使用可以参考https://blog.phpgao.com/how-to-httpbin.html。

如果直接访问http://httpbin.org:80/get.输出结果是

{
    "args": {},
    "headers": {
        "Accept": "*/*",
        "Accept-Encoding": "gzip, deflate",
        "Accept-Language": "zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7",
        "Cache-Control": "no-cache",
        "Connection": "close",
        "Cookie": "_gauges_unique_day=1; _gauges_unique_month=1; _gauges_unique_year=1; _gauges_unique=1",
        "Host": "httpbin.org",
        "Postman-Token": "e77a3596-58f5-bdd8-a953-edbaaeb8e3ff",
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36",
        "X-Postman-Interceptor-Id": "090915cc-dc8e-3fd5-0d2c-d2a179a6b2fa"
    },
    "origin": "103.126.92.86",
    "url": "http://httpbin.org/get"
}

读者可以比较一下两者的区别,主要是使用springcloud gateway会存在Forwarded转发。

使用Hystrix

上面依赖中我们已经将Hystrix引入进来。这里修改启动类,添加一个新的路由器,并且添加一个fallback方法。

package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;

@SpringBootApplication
@RestController
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    public RouteLocator myRoutes(RouteLocatorBuilder builder) {
        return builder.routes()
                .route("path-route", p -> p
                        .path("/get")  //根据PATH路由
                        .filters(f -> f.addRequestHeader("Hello", "World"))
                        .uri("http://httpbin.org:80"))
//                使用hystrix
                .route("hystrix-route", p -> p
                        .host("*.abc.com")  //根据host路由
                        .filters(f -> f
                                .hystrix(config -> config
                                        .setName("mycmd")
                                        .setFallbackUri("forward:/fallback")))
                        .uri("http://httpbin.org:80"))
                .build();
    }

    /**
     * 该方法对应于上面的路由hystrix-route的fallback。
     * Mono是webflux的类,响应式编程的写法
     * @return
     */
    @RequestMapping("/fallback")
    public Mono<String> fallback() {
        return Mono.just("fallback");
    }
}

重启服务,在postman中访问http://127.0.0.1:8095/delay/3,并且在header中指定其host:
在这里插入图片描述
由于经过了路由之后,实际的访问路径变成了http://httpbin.org:80/delay/3,该接口访问失败,所以输出结果是fallback
我们将访问连接改为http://127.0.0.1:8095/headers就会访问成功:
在这里插入图片描述
到此为止,应该基本了解springcloud gateway的运行机制了。

predicate

Spring Cloud Gateway内置了许多Predict,这些Predict的源码在org.springframework.cloud.gateway.handler.predicate包中,如果读者有兴趣可以阅读一下。现在列举各种Predicate如下图:

在这里插入图片描述

接下来看一下每种predicate的应用。

上面是通过编写java代码来实现路由功能,接下来我们使用配置文件方式来实现。

首先我们将java启动类中的com.example.Application#myRoutes方法注释掉,避免与配置文件中的相互影响

Path

配置中添加对应的path route:

server:
  port: 8095

spring:
  application:
    name: springcloud-gateway-helloworld
  profiles: # 这里指定应用哪个profile,对应下面的spring.profiles
    active: path_route

--- # 三个减号的意思是在一个文件中编写多个配置,通过spring.profiles.active指定应用哪个配置
spring:
  cloud:
    gateway:
      routes:
      - id: path_route
        uri: http://httpbin.org:80/
        predicates:
        - Path=/get
  profiles: path_route

请求http://127.0.0.1:8095/get结果:
在这里插入图片描述
访问http://127.0.0.1:8095/ip返回404。

After

编写after_route

spring:
  application:
    name: springcloud-gateway-helloworld
  profiles:
    active: after_route
    
---
spring:
  cloud:
    gateway:
      routes:
      - id: after_route
        uri: http://httpbin.org:80/get # 这里后面的get没有生效,生效的只是前面的host: httpbin.org
        predicates:
        - After=2017-01-20T17:42:47.789-07:00[America/Denver]
  profiles: after_route

这里指定了访问时间在2017-01-20 17:42:47.789。访问http://127.0.0.1:8095/get方返回结果与上面一致,可以正常访问,说明路由生效了。
访问http://127.0.0.1:8095/ip也可以正常返回结果。说明这里spring.cloud.gateway.routes[0].uri=http://httpbin.org:80/get中的get路径没有生效,只是host生效了,通过host拼接上真实的访问URL的path,这里就是http://httpbin.org:80/拼接上ip即路由后真实访问的路径是http://httpbin.org:80/ip

Before

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

在指定直接之前的 任何请求相匹配。

Between

Between需要两个参数,用逗号分隔,与指定的两个时间内的任何请求相匹配。

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

cookie

---
spring:
  cloud:
    gateway:
      routes:
      - id: cookie_route
        uri: http://httpbin.org:80/get
        predicates:
        - Cookie=name, lisi
  profiles: cookie_route

上面指定了满足cookie中的name=lisi才进行路由。别忘了将spring.profiles.active改为cookie_route启动这个cookie_route。
在发起请求时我们这里需要指定Cookie的值。
在这里插入图片描述
读者可以将配置文件中的name的期望值改一下,再次测试。如果期望的Cookie在URL请求头中不存在,则会404.

header

这里模拟header中设置请求数据。同理别忘了将spring.profiles.active改为header_route启动这个header_route。
Header有两个参数,第一个是key,第二个是value

---
spring:
  cloud:
    gateway:
      routes:
      - id: header_route
        uri: http://httpbin.org:80/
        predicates:
        - Header=X-Request-Id, \d+
  profiles: header_route

上面路由的含义是header中要存在X-Request-Id=[数字]的信息。重启服务.
在这里插入图片描述
读者可以尝试将123改为任意一个非数字字符串,测试一下,返回404.

Host

---
spring:
  cloud:
    gateway:
      routes:
      - id: host_route
        uri: http://httpbin.org:80/
        predicates:
        - Host=*.abc.com
  profiles: host_route

满足Host=*.abc.com的请求才会被路由。如果与多个host匹配则用逗号分隔即可。
在这里插入图片描述

Get

---
spring:
  cloud:
    gateway:
      routes:
      - id: method_route
        uri: http://httpbin.org:80/
        predicates:
        - Method=GET
  profiles: method_route

所有的GET请求都会被转发。
在这里插入图片描述

Query

---
spring:
  cloud:
    gateway:
      routes:
      - id: query_route
        uri: http://httpbin.org:80/
        predicates:
        - Query=foo, abc
  profiles: query_route

Query接收两个参数,一个是参数名[必填],一个是参数值[选填]。
如果参数值不填,表示请求参数中保护指定key的参数即可,如果填了参数值,则不仅要包含key,其值还要与指定的value相同。

在这里插入图片描述

Filter

Filter生命周期

在这里插入图片描述
如上图所示(来自官网),客户端向Spring Cloud Gateway发出请求。 Gateway Handler Mapping根据predicate确定走哪个路由,则将其发送到Gateway web handler处理。 Gateway web handler处理请求时会经过一系列的过滤器链。 先执行所有pre过滤器逻辑,然后进行代理请求。 在发出代理请求之后,收到代理服务的响应之后执行post过滤器逻辑。在执行所有“pre”过滤器逻辑时,往往进行了鉴权、限流、日志输出等功能,以及请求头的更改、协议的转换;转发之后收到响应之后,会执行所有“post”过滤器的逻辑,在这里可以响应数据进行了修改,比如响应头、协议的转换等。

在Spring Cloud Gateway中,filter从作用范围可分为另外两种,一种是针对于单个路由的gateway filter,它在配置文件中的写法同predict类似;另外一种是针对于所有路由的global gateway filer。

FilterFactory

Spring Cloud Gateway 内置了很多过滤器工厂,都继承了AbstractGatewayFilterFactory抽象类,以AddRequestHeaderGatewayFilterFactory为例:
在这里插入图片描述
官方提供了很多filter工厂,这里我们只找几个常用的FilterFactory举例。
在这里插入图片描述

AddRequestHeader GatewayFilter Factory

这里我们以上面的query_route为例进行改造,在要求请求参数中有foo=abc之外,添加过滤器,在header中添加X-Request-Foo=Bar。注意这里filters下写的是FilterFactory的前缀,而不是完整的方法名,springboot的约定大于配置规则生效。

--- # Query--请求参数
spring:
  cloud:
    gateway:
      routes:
      - id: query_route
        uri: http://httpbin.org:80/
        predicates:
        - Query=foo, abc
        filters:
        - AddRequestHeader=X-Request-Foo, Bar
  profiles: query_route

在这里插入图片描述

AddRequestParameter GatewayFilter Factory

AddRequestParameter含义是追加请求参数。在上述实例基础上添加AddRequestParameter:

--- # Query--请求参数
spring:
  cloud:
    gateway:
      routes:
      - id: query_route
        uri: http://httpbin.org:80/
        predicates:
        - Query=foo, abc
        filters:
        - AddRequestHeader=X-Request-Foo, Bar
        - AddRequestParameter=age, 10
  profiles: query_route

在这里插入图片描述

Hystrix GatewayFilter Factory

Hystrix的配置还是比较复杂的,这里仅仅列出如何集成hystrix,详细的hystrix文档请参考笔者另一篇文章分布式熔断、限流与服务保护:深入 Hystrix 原理及使用。

我们还是在之前query_route基础上改造。首先指定使用HystrixGatewayFilterFactory过滤器工厂,并且设置了两个参数name和fallbackUri,这里fallbackUri必须以forward开头,表明是转发。然后我们这里设置了fallbackcommand的超时时间用于测试。读者可以尝试修改这个timeoutInMilliseconds的值,来观察http://127.0.0.1:8095/get?foo=abc的输出结果。
配置如下:

# 指定fallbackcommand这个command的超时时间。用于下面的hystrix测试
hystrix:
  command:
    fallbackcommand:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 100
            
--- # Query--请求参数
spring:
  cloud:
    gateway:
      routes:
      - id: query_route
        uri: http://httpbin.org:80/
        predicates:
        - Query=foo, abc
        filters:
        - AddRequestHeader=X-Request-Foo, Bar
        - AddRequestParameter=age, 10
        - name: Hystrix # 这里就是指定了 HystrixGatewayFilterFactory 这个过滤器工厂。下面是该工厂的参数
          args:
            name: fallbackcommand
            fallbackUri: forward:/fallback
  profiles: query_route

PrefixPath GatewayFilter Factory

给请求添加前缀。该功能可以用于对外隐藏真实路径。
为了验证该功能,我们编写一个controller,方便起见,这里我们没有新起项目,还是使用的现有8095端口的项目:

@RestController
@RequestMapping("hello")
public class HelloController {

    @RequestMapping("a")
    public Mono<String> a() {
        System.out.println("aaaaaaaa");
        return Mono.just("aa");
    }
    @RequestMapping("b")
    public Mono<String> b() {
        System.out.println("bbbbb");
        return Mono.just("bb");
    }
}

我们通过添加配置如下:

--- # prefixPath_route
spring:
  cloud:
    gateway:
      routes:
      - id: prefixPath_route
        uri: http://127.0.0.1:8095/
        predicates:
        - Query=foo, abc
        filters:
        - PrefixPath=/hello
  profiles: prefixPath_route

通过上面的filter,springcloud gateway会将http://127.0.0.1:8095/a?foo=abc 请求路由到http://127.0.0.1:8095/hello/a?foo=abc,添加前缀/hello。返回结果为aa:
在这里插入图片描述

RequestRateLimiter GatewayFilter Factory

这个功能属于中要功能。

限流算法常用的有两种:漏桶算法、令牌桶算法。由于本节内容稍多,故单独写一篇文章。请读者移步至 阅读。

RedirectTo GatewayFilter Factory

重定向。有两个参数,第一个是相应的status,第二个是重定向的URL地址。
下面我们请求接口a,之后重定向到b(借用上面的 prefixPath_route):

--- # prefixPath_route
spring:
  cloud:
    gateway:
      routes:
      - id: prefixPath_route
        uri: http://127.0.0.1:8095/
        predicates:
        - Query=foo, abc
        filters:
        - PrefixPath=/hello
        - RedirectTo=302, http://127.0.0.1:8095/hello/b # 执行完请求后重定向到b
  profiles: prefixPath_route

执行请求http://127.0.0.1:8095/a?foo=abc输出结果是bb。来看一下控制台输出:

aaaaaaaa
bbbbb

可见是先执行完a方法,然后重定向到b执行。

RemoveRequestHeader GatewayFilter Factory

spring:
  cloud:
    gateway:
      routes:
      - id: removerequestheader_route
        uri: http://example.org
        filters:
        - RemoveRequestHeader=X-Request-Foo

RemoveRequestHeader会在请求执行前将制定的header参数移除。

RemoveResponseHeader GatewayFilter Factory

spring:
  cloud:
    gateway:
      routes:
      - id: removeresponseheader_route
        uri: http://example.org
        filters:
        - RemoveResponseHeader=X-Response-Foo

RemoveResponseHeader会在将response返回给用户前将其header中的X-Response-Foo移除。

RewritePath GatewayFilter Factory

RewritePath URL重写(允许使用正则表达式)。
我们在header_route添加上RewritePath=/foo/(?.*), /$\{segment},含义是假如有/foo/get的请求,在发送给下游之前,会将其重写为/get

--- # Header--请求头
spring:
  cloud:
    gateway:
      routes:
      - id: header_route
        uri: http://httpbin.org:80/
        predicates:
        - Header=X-Request-Id, \d+
        filters:
        - RemoveRequestHeader=X-Request-Foo  # 将header中的参数移除
        - RewritePath=/foo/(?.*), /$\{segment}  # URL重写(允许使用正则表达式)
  profiles: header_route

在这里插入图片描述

StripPrefix GatewayFilter Factory

spring:
  cloud:
    gateway:
      routes:
      - id: nameRoot
        uri: http://nameservice
        predicates:
        - Path=/name/**
        filters:
        - StripPrefix=2

去除前缀。
例如发送请求/name/bar/foo 会被转换为http://nameservice/foo

还有很多过滤器工厂,这里就不一一介绍了,使用到的同学请参考 Spring Cloud Gateway官方文档 。

springcloud系列学习笔记目录参见博主专栏 spring boot 2.X/spring cloud Greenwich。
由于是一系列文章,所以后面的文章可能会使用到前面文章的项目。文章所有代码都已上传GitHub:https://github.com/liubenlong/springcloudGreenwichDemo
本系列环境:Java11;springboot 2.1.1.RELEASE;springcloud Greenwich.RELEASE;MySQL 8.0.5;

你可能感兴趣的:(spring,cloud,spring,boot,2.X/spring,cloud,Greenwich)