Spring Cloud Gateway实践体验

栏目: Java · 发布时间: 6年前

内容简介:Spring Cloud Gateway是Spring Cloud官方推出的第二代网关框架,从官网给出的对比分析结果来看,Gateway比Zuul的性能要好很多,而且功能也更加丰富。 以下是官方对比Gateway、Zuul、Linkered的分析结果,可以看到Gateway是三个钟效果性能最好的。从官网给出的图中可以看到客户端向Spring Cloud Gateway发出请求,然后网关转发给代理的服务,然后在将服务响应的结果返回给客户端。而且Gateway内部还有一系列的处理。请求进来后,会首先由Gatew

Spring Cloud Gateway是Spring Cloud官方推出的第二代网关框架,从官网给出的对比分析结果来看,Gateway比Zuul的性能要好很多,而且功能也更加丰富。 以下是官方对比Gateway、Zuul、Linkered的分析结果,可以看到Gateway是三个钟效果性能最好的。

Spring Cloud Gateway实践体验

从官网给出的图中可以看到客户端向Spring Cloud Gateway发出请求,然后网关转发给代理的服务,然后在将服务响应的结果返回给客户端。而且Gateway内部还有一系列的处理。

Spring Cloud Gateway实践体验

请求进来后,会首先由Gateway Handler Mapping进行处理,这里处理的过程中用到 predicate ,通过的请求才发送到Gateway web handler做进一步处理。然后又会经过一系列的过滤器。过滤器和Zuul的类似,也有"pre"、"post"分类。

  • "pre"代表在请求前之前进行过滤处理
  • "post"代表在请求之后进行过滤处理 一般我们在执行“pre”过滤器时,会进行鉴权、限流、日志输出等功能,以及请求头的更改、协议的转换;在请求之后执行“post”过滤器时,会对数据进行修改,比如响应头、协议的转换等。 整个过程中有两个比较重要的概念就是 predicatefilter ,filter比较好理解,下面来介绍一下predicate。

predicate

predicate在JDK8中的定义如下:

Predicate 接受一个输入参数,返回一个布尔值结果。该接口包含多种默认方法来将Predicate组合成其他复杂的逻辑(比如:与,或,非)。可以用于接口请求参数校验、判断新老数据是否有变化需要进行更新操作。add--与、or--或、negate--非

predicate这种输入类型属于Spring体系中的ServerWebExchange,它允许我们匹配HTTP请求中的任何内容,比如请求头或参数。而且Spring Cloud Gateway已经内置了很多Predict,这些Predict的源码在org.springframework.cloud.gateway.handler.predicate包中。具体可参考如下:

Spring Cloud Gateway实践体验

(图片来源网络)

环境准备

准备三个工程

  • leon-eureka 注册中心项目
  • leon-consumer 微服务项目
  • leon-gateway 网关项目

因为spring cloud gateway项目只支持spring boot 2.0以上版本,所以对spring cloud的版本也有要求。本文中使用的整体版本环境为:

  • spring boot :2.0.5
  • spring cloud : Finchley.SR1

(以上项目创建不在赘述,可参考案例工程),运行效果后,保证leon-gateway、leon-consumer都已经注册在leon-eureka服务上。

Spring Cloud Gateway实践体验

在leon-consumer中提供login接口,进行简单的登录验证:

@GetMapping("/login")
public String login(@RequestParam String username, @RequestParam String password) {
    if ("leon".equals(username) && "888".equals(password)) {
        return "登录成功";
    }
    return "登录失败";
}
复制代码

注意问题

在此版本上,添加注册中心客户端以来的包和版本

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
复制代码

另外在leon-gateway中不能添加 web 依赖包,如下:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
复制代码

添加后启动会报错:

Description:
Parameter 0 of method modifyRequestBodyGatewayFilterFactory in org.springframework.cloud.gateway.config.GatewayAutoConfiguration required a bean of type 'org.springframework.http.codec.ServerCodecConfigurer' that could not be found.

Action:
Consider defining a bean of type 'org.springframework.http.codec.ServerCodecConfigurer' in your configuration.

复制代码

因为Spring Cloud Gateway 是使用 netty+webflux实现,webflux与web是冲突的。

接下来我们在项目中实际使用gateway,通过gateway转发路由,将请求转发到leon-consumer中。

Route Predicate

Predicate的分类比较多,接下来在项目总一一使用。

3.1 After Route Predicate Factory

接收一个日期类型参数,在这个日期之后可以通过。 在配置文件(application.yml)中添加

spring:
  application:
    name: leon-gateway
  cloud:
    gateway:
      routes:
      - id: dev
        uri: http://localhost:8081/login?username=leon&password=888
        predicates:
        - After=2020-05-20T17:42:47.789-07:00[America/Denver]
复制代码
  • id 为路由route的唯一标识。(这次测试不添加也可以,后续测试多个route是否必须添加)
  • uri 为转发请求的地址
  • predicates 为请求谓词,此处是在指定时间之后

现在时间为2019-05,所以是在指定时间之前,此时启动服务,我们访问http://localhost:8085/ 可以看到转发请求后出现404错误,

Spring Cloud Gateway实践体验

然后我们修改时间为2018

spring:
  application:
    name: leon-gateway
  cloud:
    gateway:
      routes:
      - id: dev
        uri: http://localhost:8081/login?username=leon&password=888
        predicates:
        - After=2018-05-20T17:42:47.789-07:00[America/Denver]
复制代码

重启服务后再次访问: http://localhost:8085/ 可以看到已经转发到leon-consumer的login接口,并能够收到正确返回信息

Spring Cloud Gateway实践体验

3.2 Before Route Predicate Factory

在指定时间之前才能通过转发,具体效果类似,不在赘述

spring:
  cloud:
    gateway:
      routes:
      - id: before_route
        uri: http://localhost:8081/login?username=leon&password=888
        predicates:
        - Before=2019-01-20T17:42:47.789-07:00[America/Denver]
复制代码

3.3 Between Route Predicate Factory

在指定范围时间之内的请求才能通过转发。接收两个日期参数,并且参数的形式,必须是较早的日期在前,较晚的日期在后,具体如下

spring:
  cloud:
    gateway:
      routes:
      - id: between_route
        uri: http://localhost:8081/login?username=leon&password=888
        predicates:
        - Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]
复制代码

3.4 Cookie Route Predicate Factory

cookiel谓词工厂接收两个参数,Cookie名称和值(也可以是正则表达式)。请求中必须包含给定名称的cookie,并且cookie值要符合给定的值(正则规则)才能通过转发。 通过postman添加cookie进行测试如下。

Spring Cloud Gateway实践体验

如果参数值不对同样会报404问题。

3.5 Header Route Predicate Factory

同样需要2个参数,一个是header名称,另外一个header值(可以为正则表达式),匹配通过后才转发

      - id: header_route
        uri: http://localhost:8081/login?username=leon&password=888
        predicates:
        - Header=X-Request-Id, \d+
复制代码

在Header中必须要有X-Request-Id名称的参数,并且值要满足正则规则,必须为数字

Spring Cloud Gateway实践体验

3.6 Host Route Predicate Factory

只接收一个参数,就是host name,可以使用"."来进行匹配,此参数在head中添加

       - id: host_route
        uri: http://localhost:8081/login?username=leon&password=888
        predicates:
        - Host=**.leon.cn
复制代码
Spring Cloud Gateway实践体验

3.7 Method Route Predicate Factory

接收一个参数,代表请求的类型。

      - id: method_route
        uri: http://localhost:8081/login?username=leon&password=888
        predicates:
        - Method=GET
复制代码

此时所有的GET请求都会转发,如果是POST请求就会404

Spring Cloud Gateway实践体验
Spring Cloud Gateway实践体验

3.8 Path Route Predicate Factory

接收一个参数,就是路径地址(可以为正则表达式)

      - id: path_route
        uri: http://localhost:8081/login?username=leon&password=888
        predicates:
        - Path=/leon/{segment}
复制代码

所有的请求路径满足/leon/{segment}的请求将会匹配并被路由,比如/leon/1 、/leon/bar的请求,将会命中匹配,并成功转发。

Spring Cloud Gateway实践体验

3.9 Query Route Predicate Factory

需要两个参数,一个是参数名,一个是参数值(正则表达式)。

      - id: query_route
        uri: http://localhost:8081/login?username=leon&password=888
        predicates:
        - Query=name, leon.
复制代码

上面配置了请求中含有参数name,并且name的值匹配leon.,则进行转发 比如请求参数为name=leon8、name=leono、name=leon静都可以匹配

Spring Cloud Gateway实践体验

也可以只加一个参数,代表只验证参数名称,不验证参数值。只要包含指定名称的参数即可通过转发。

3.10 RemoteAddr Route Predicate Factory

接收一个字符串参数,此字符串代表地址列表(最少1个),只有是以上要求的IP地址发来的请求才通过转发

      - id: remoteaddr_route
        uri: http://localhost:8081/login?username=leon&password=888
        predicates:
        - RemoteAddr=172.17.153.1/24
复制代码

以上配置表明只有IP地址为172.17.153.1、172.17.153.2、172.17.153.3、。。。172.17.153.24可以通过转发。

3.11 组合

可以同时配置多个predicates,如果一个请求满足多个路由的谓词条件时,请求只会被首个成功匹配的路由转发。 我们在leon-consumer项目中添加接口方法

@GetMapping("/info")
public String info() {
    return "获取信息成功";
}
复制代码

然后在leon-gatewa重视添加两个

spring:
  application:
    name: leon-gateway
  cloud:
    gateway:
      routes:
      - id: header_route
        uri: http://localhost:8081/login?username=leon&password=888
        predicates:
        - Header=X-Request-Id, \d+
      - id: query_route
        uri: http://localhost:8081/info
        predicates:
        - Query=name, leon.
复制代码

这里我们同时添加了header_route、query_route,然后我们在postman中发送请求,同时满足两个条件:

Spring Cloud Gateway实践体验

结果可见是按照header_route来进行转发。

我们把两个条件顺序互换:

  cloud:
    gateway:
      routes:
      - id: query_route
        uri: http://localhost:8081/info
        predicates:
        - Query=name, leon.
      - id: header_route
        uri: http://localhost:8081/login?username=leon&password=888
        predicates:
        - Header=X-Request-Id, \d+
复制代码

重启服务,然后再次访问,相同的请求这次被转发到info接口。

Spring Cloud Gateway实践体验

当然如果设置多个路由谓词,第一个满足优先转发,如果第一个不满足会继续往下判断,遇到满足的进行转发,我们把请求条件改成不合适,则准发第二个接口。

Spring Cloud Gateway实践体验

以上的配置类似"or"的条件,我们还可以配置组合使用,达到"and"的效果,要同时满足才能进行转发。

  cloud:
    gateway:
      routes:
      - id: zuhe_route
        uri: http://localhost:8081/info
        predicates:
        - Query=name, leon.
        - Header=X-Request-Id, \d+
复制代码

此时如果有一个参数设置不对,那么就不会进行转发

Filter

Predict决定了请求由哪一个路由处理,在路由处理之前,需要经过“pre”类型的过滤器处理,处理返回响应之后,可以由“post”类型的过滤器处理。

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

GatewayFilter

GatewayFilter的使用同Predicate类似,都是在配置文件application.yml中配置即可。这里选择几个常用介绍,更多的配置可参考官方文档: cloud.spring.io/spring-clou…

4.1 AddRequestHeader GatewayFilter Factory

在网关中添加Filter

   cloud:
    gateway:
      routes:
      - id: zuhe_route
        uri: http://localhost:8081/info
        predicates:
        - Query=name, leon.
        - Header=X-Request-Id, \d+
        filters:
        - AddRequestHeader=X-Request-Foo, Bar

复制代码

这里我们添加了AddRequestHeader过滤器,在请求转发后给HEADER中添加参数X-Request-Foo,值为:Bar。

改造leon-consuemr工程

@GetMapping("/info")
public String info(HttpServletRequest request) {
    String header = request.getHeader("X-Request-Foo");
    return "获取信息成功:" + header;
}
复制代码

可以看到,我们在路由转发后的处理方法中获取相关参数。然后发送请求,这个请求我们只满足路由转发条件,并没有添加X-Request-Foo的HEADER参数,但是我们在转发后服务处理中是可以获取到的。

Spring Cloud Gateway实践体验

其他更多的设置暂不涉及,请参考官方文档。

Global Filters

全局过滤器,不需要在配置文件中配置,作用在所有的路由上。Gatewqy内置的GlobalFilter如下:

Spring Cloud Gateway实践体验
Spring Cloud Gateway实践体验

(图片来源网络)

如果想要自己实现GlobalFilter也可以,实现GlobalFilter和Ordered接口即可。

public class GlobalFilter implements org.springframework.cloud.gateway.filter.GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String token = exchange.getRequest().getHeaders().getFirst("token");
        if (token == null || token.isEmpty()) {
            System.out.println("token为空");
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return 0;
    }
}
复制代码

在启动类中添加配置,将配置类加入容器管理。

@Bean
    public GlobalFilter globalFilter() {
        return new GlobalFilter();
    }
复制代码

此时发情请求,可以发现如果HEADER中没有token参数,则无法通过转发。

Spring Cloud Gateway实践体验

项目综合使用

使用过Zuul的同学都了解,在Zuul中可以配置统一前置路由,比如现在我们想把所有路径中包含/user的都转发到leon-consumer工程去处理。 在以上的项目中继续改造,在leon-consumer项目中添加前缀映射:

@RestController
@RequestMapping("/user")
public class UserController {

    @GetMapping("/login")
    public String login(@RequestParam String username, @RequestParam String password) {
        if ("leon".equals(username) && "888".equals(password)) {
            return "登录成功";
        }
        return "登录失败";
    }

    @GetMapping("/info")
    public String info(HttpServletRequest request) {
        String header = request.getHeader("X-Request-Foo");
        return "获取信息成功:" + header;
    }
}
复制代码

在leon-gateway中添加配置:

  cloud:
    gateway:
      routes:
      - id: path_route
        uri: lb://leon-consumer #服务名,注意一定要以lb://开头
        predicates:
        - Path=/user/{segment}
        filters:
        - AddRequestHeader=X-Request-Foo, Bar
      discovery:
        locator:
          enabled: true #设置可以通过服务名获取服务
          lower-case-service-id: true #设置获取服务可以通过小写形式
复制代码

在前面我们的uri都是直接写好的具体的地址,现在的服务已经注册到Eureka上,我们也可以通过服务名称找到具体的服务。 添加配置

      discovery:
        locator:
          enabled: true #设置可以通过服务名获取服务
复制代码

通过**lb:**指定即可。默认配置的名称必须是全大写,想要通过小写识别,可添加配置

      discovery:
        locator:
          lower-case-service-id: true #设置获取服务可以通过小写形式
复制代码

配置完成后,重启服务访问:

Spring Cloud Gateway实践体验

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Learning PHP, MySQL, JavaScript, and CSS

Learning PHP, MySQL, JavaScript, and CSS

Robin Nixon / O'Reilly Media / 2012-9-3 / USD 39.99

If you're familiar with HTML, you can quickly learn how to build interactive, data-driven websites with the powerful combination of PHP, MySQL, and JavaScript - the top technologies for creating moder......一起来看看 《Learning PHP, MySQL, JavaScript, and CSS》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

MD5 加密
MD5 加密

MD5 加密工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具