Gateway了解与使用

Zuul 简介

官网: https://github.com/Netflix/zuul/wiki

Zuul 是从设备和网站到 Netflix 流应用程序后端的所有请求的前门。作为边缘服务应用程序,Zuul 旨在实现动态路由,监视,弹性和安全性。它还可以根据需要将请求路由到多个 Amazon Auto Scaling 组。

现在的话基本上不会使用 Zuul 了,因为它总是存在一些小问题而且已经进入维护状态了,所以我们现在基本上都是使用 Gateway 网关,因为它是 Spring Cloud 团队自己开发的,值得信赖,并且很多功能 Zuul 都没有用起来也非常的简单快捷。

Geteway 简介

Gateway 是在 Spring 生态系统之上构建的 API 网关服务,基于 Spring 5, Spring Boot 2 和 Project Reactor 等技术。

Gateway 旨在提供一种简单而有效的方式来对 API 进行路由,以及提供了一些强大的过滤器功能,例如:熔断、限流、重试等

SpringCloud Gateway 作为 Spring Cloud 生态系统中的网关,目标是替代 Zuul,在 Spring Cloud 2.0 以上版本中,没有对新版本的 Zuul 2.0 以上最新高性能版本进行集成,仍然还是使用的 Zuul 1.x 非 Reactor 模式的老版本。而为了提升网关的性能,SpringCloud Gateway 是基于 WebFlux 框架实现的,而 WebFlux 框架底层则使用了高性能的 Reactor 模式通信框架 Netty。

特性

  1. 基于Spring Framework 5, Project Relactor 和 Spring Boot 2.0 进行构建;
  2. 动态路由:能够匹配任何请求属性;
  3. 可以对路由指定 Predicate (断言)和 Filter (过滤器) ;
  4. 集成 Hystrix 的断路器功能;
  5. 集成 Spring Cloud 服务发现功能;
  6. 易于编写的 Predicate (断言)和Filter (过滤器) ;
  7. 请求限流功能;
  8. 支持路径重写;

核心概念

  1. Route(路由)

    路由是构建网关的基本模块,它由 ID 、目标 URI、以及一系列的断言和过滤器组成,如果断言为 true 则匹配该路由。

  2. Predicate(断言)

    参考的是 java8 的 java.util.function.Predicate 开发人员可以匹配 HTTP 请求中的所有内容(例如请求头或请求参数等),如果请求和断言相匹配则进行路由。

  3. Filter(过滤)

    指的是 Spring 框架中 GatewayFilter 的实例,使用过滤器,可以在请求被路由前或之后对请求进行修改。

工作流程

在这里插入图片描述

客户端向 Spring Cloud Gateway 发出请求。然后在 Gateway Handler Mapping 中找到与请求相匹配的路由,将其发送到 Gateway Web Handler。

Handler 再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。

过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前( “pre” )或之后( “post” )执行业务逻辑。

Filter 在 “pre” 类型的过滤器可以做参数校验、权限校验、流量监控、日志输出、协议转换等,在 “post” 类型的过滤器中可以做响应内容、响应头的修改,日志的输出,流量监控等有着非常重要的作用。

配置网关的两种方式

第一种、在 yml 文件中进行配置

创建一个名为 cloud-gateway-gateway9527 的子工程

修改 pom 文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>SpringCloud2020</artifactId>
        <groupId>com.lyang.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>cloud-gateway-gateway9527</artifactId>

    <dependencies>
        <!--新增Gateway-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>

        <!-- 引入自己定义的api通用包,可以使用 Payment 支付 Entity -->
        <dependency>
            <groupId>com.lyang.springcloud</groupId>
            <artifactId>cloud-api-commons</artifactId>
            <version>1.0-SNAPSHOT</version>
            <scope>compile</scope>
        </dependency>

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

        <!-- eureka-client -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
</project>

这里千万不要加入web依赖,否则在后面启动的时候就会报错!!!

编写 yml 配置文件

server:
  port: 9527

spring:
  application:
    name: cloud-gateway
  cloud:
    # 网关配置
    gateway:
      routes:
        #路由的ID,没有固定规则但要求唯一,建议配合服务名
        - id: payment_routh
          #匹配后提供服务的路由地址
          uri: http://localhost:8001
          #断言,路径相匹配的进行路由
          predicates:
            - Path=/payment/get/**
        - id: payment_routh2
          uri: http://localhost:8001
          predicates:
            - Path=/payment/lb/**

eureka:
  instance:
    hostname: cloud-gateway-service
  # 服务提供者 provider 注册进 eureka 服务列表内
  client:
    service-url:
      register-with-eureka: true
      fetch-registry: true
      # 单机版
      defaultZone: http://eureka7001.com:7001/eureka

这里对路由器进行了路由映射(映射的是 cloud-provider-payment8001 服务中的部分方法,这里写了两个,如果大家想要更多的映射直接 copy 即可),然后将该服务注册进注册中心,这里为了方便就直接使用单机版的了。

配置都是对应方法中的数据的(比如方法访问地址,端口号等)

在这里插入图片描述

左边是 9527 的 yml 文件配置,右边是 8001 控制层中的方法

编写启动类

/**
 * 9527启动类
 */
@SpringBootApplication
@EnableEurekaClient
public class GatewayMain9527 {
    public static void main(String[] args) {
        SpringApplication.run(GatewayMain9527.class, args);
    }
}

网关中不需要编写业务类,所以可以直接省略!

测试

首先我们来看一下 Eureka,可以发现注册进了两个微服务(一个是支付微服务提供方8001,一个是网关9527)

在这里插入图片描述

在之前没有配置网关之前我们访问方法的方式是这样的

在这里插入图片描述

然后在使用 网关9527 来测试的时候是这样的

在这里插入图片描述

我们可以发现,配置网关之后,使用网关端口号也可以直接访问配置好的方法,这样的好处就是可以很好的提供服务的安全性!

第二种、代码注入RouteLocator的Bean

官网案例

我们接下来自己编写一个 百度新闻,通过 9527 网关访问到外网的百度新闻网址

在 9527 中编写配置类

@Configuration
public class GatewayConfig {
    //国内新闻
    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder) {
        /**
         * 配置了一个 id 为 path_route_atguigu 的路由规则
         * 当访问地址 http://localhost:9527/guonei 是就会自动转发到地址:http://news.baidu.com/guonei
         */
        RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
        routes.route("path_route_atguigu", r -> r.path("/guonei")
                .uri("http://news.baidu.com/guonei")).build();
        return routes.build();
    }
}

测试

在这里插入图片描述

我们可以使用自己设置的路径来访问外网

注意:

因为我们现在在配置类中只配置了国内的新闻,所以我们才能够正常使用国内的新闻。我们还没有配置其他的新闻,比如国际、军事、财经等,一旦我们点击就会导致 404 找不到页面的问题,所以如果想要访问多个页面,那么就需要自己去配置类中进行配置!

以下是在没有配置国际新闻的情况下点击国际进行访问的效果

在这里插入图片描述

对于其他的页面我们也可以来进行对应的配置

@Configuration
public class GatewayConfig {
    //国内新闻
    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder) {
        /**
         * 配置了一个 id 为 path_route_atguigu 的路由规则
         * 当访问地址 http://localhost:9527/guonei 是就会自动转发到地址:http://news.baidu.com/guonei
         */
        RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
        routes.route("path_route_atguigu", r -> r.path("/guonei")
                .uri("http://news.baidu.com/guonei")).build();
        return routes.build();
    }

    /**
     * 注意:如果想要访问其他的外网路径,那么就需要我们在下面继续配置!不配置的话是无法正常访问的!!!
     */

    //国际新闻
    @Bean
    public RouteLocator customRouteLocator2(RouteLocatorBuilder routeLocatorBuilder) {
        /**
         * 配置了一个 id 为 path_route_atguigu 的路由规则
         * 当访问地址 http://localhost:9527/guoji 是就会自动转发到地址:http://news.baidu.com/guoji
         */
        RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
        routes.route("path_route_atguigu2", r -> r.path("/guoji")
                .uri("http://news.baidu.com/guoji")).build();
        return routes.build();
    }
}

写完一个之后就会很简单了,直接 copy 然后改一下方法名、id 和路径即可

通过服务名实现动态路由

默认情况下 Gateway 会根据注册中心注册的服务列表,以注册中心上微服务名为路径创建动态路由并进行转发,从而实现动态路由的功能

修改 9527 的 yml 文件

server:
  port: 9527

spring:
  application:
    name: cloud-gateway
  cloud:
    gateway:
      discovery:
        locator:
          #开启从注册中心动态创建路由的功能,利用微服务名进行路由
          enabled: true
      routes:
        #路由的ID,没有固定规则但要求唯一,建议配合服务名
        - id: payment_routh
          # uri: http://localhost:8001   #匹配后提供服务的路由地址
          uri: lb://cloud-payment-service
          predicates:
            #断言,路径相匹配的进行路由
            - Path=/payment/get/**

        - id: payment_routh2
          #uri: http://localhost:8001   #匹配后提供服务的路由地址
          uri: lb://cloud-payment-service
          predicates:
            #断言,路径相匹配的进行路由
            - Path=/payment/lb/**

eureka:
  instance:
    hostname: cloud-gateway-service
  client:
    service-url:
      register-with-eureka: true
      fetch-registry: true
      # 单机版
      defaultZone: http://eureka7001.com:7001/eureka

然后按照 7001–8001-8002-9527 的顺序启动服务,启动后先查看 Eureka 中的服务

在这里插入图片描述

这次我们启动了两个服务提供者(8001和8002),从而达到一个动态路由的效果

查看 Eureka 服务后,我们在来访问 lb 方法进行测试

在这里插入图片描述

在这里插入图片描述

多次执行(刷新)查看效果会比较明显

Predicate(断言) 的使用

Spring Cloud Gateway 将路由匹配作为 Spring WebFlux HandlerMapping 基础架构的一部分。

Spring Cloud Gateway 包括许多内置的 Route PredicateI 厂。所有这些 Predicate 都与 HTTP 请求的不同属性匹配。多个 Route Predicate 厂可以进行组合。

Spring Cloud Gateway 创建 Route 对象时, 使用 RoutePredicateFactory 创建 Predicate 对象, Predicate 对象可以赋值给 Route。 Spring Cloud Gateway 包含许多内置的 Route Predicate Factories。

所有这些谓词都匹配 HTTP 请求的不同属性。多种谓词工厂可以组合,并通过逻辑 and。

上面已经简单了解过 断言 是个什么东西了,这里就直接给大家介绍一些 常用的 Route Predicate

在这里插入图片描述

1-3的使用

server:
  port: 9527

spring:
  application:
    name: cloud-gateway
  cloud:
    gateway:
      discovery:
        locator:
          #开启从注册中心动态创建路由的功能,利用微服务名进行路由
          enabled: true
      routes:
        #路由的ID,没有固定规则但要求唯一,建议配合服务名
        - id: payment_routh
          # uri: http://localhost:8001   #匹配后提供服务的路由地址
          uri: lb://cloud-payment-service
          predicates:
            #断言,路径相匹配的进行路由
            - Path=/payment/get/**

        - id: payment_routh2
          #uri: http://localhost:8001   #匹配后提供服务的路由地址
          uri: lb://cloud-payment-service
          predicates:
            #断言,路径相匹配的进行路由
            - Path=/payment/lb/**
            # After:设置执行时间为规定时间之后,如果在此时间之前访问则会直接报错,反之则会正常执行
            #- After=2020-06-18T17:34:34.554+08:00[Asia/Shanghai]
            # Before:设置执行时间为时间之前,如果超过规定时间执行则会出错,反之则会正常执行
            #- Before=2020-02-08T18:59:34.102+08:00[Asia/Shanghai]
            # Between:设置执行时间为哪个时间段之间,超出或低于都无法正常执行,反之则会正常执行
            - Between=2020-05-08T10:59:34.102+08:00[Asia/Shanghai] ,  2020-08-08T10:59:34.102+08:00[Asia/Shanghai]

eureka:
  instance:
    hostname: cloud-gateway-service
  client:
    service-url:
      register-with-eureka: true
      fetch-registry: true
      # 单机版
      defaultZone: http://eureka7001.com:7001/eureka

After: 设置执行时间为规定时间之后,如果在此时间之前访问则会直接报错,反之则会正常执行

Before: 设置执行时间为时间之前,如果超过规定时间执行则会出错,反之则会正常执行

Between: 设置执行时间为哪个时间段之间,超出或低于都无法正常执行,反之则会正常执行

好处: 比如现在在做一个系统,等到要上线了,自己可以直接设置什么时候才启动/上线

如何获取当前时间呢?

我们可以直接在测试方法中测试以下方法即可得到当前的系统时间

import java.time.ZonedDateTime;
public class T2 {
 public static void main(String[] args) {
     //当前系统时间
     ZonedDateTime zdt = ZonedDateTime.now();
     System.out.println(zdt);
 }
}

4的使用

server:
  port: 9527

spring:
  application:
    name: cloud-gateway
  cloud:
    gateway:
      discovery:
        locator:
          #开启从注册中心动态创建路由的功能,利用微服务名进行路由
          enabled: true
      routes:
        #路由的ID,没有固定规则但要求唯一,建议配合服务名
        - id: payment_routh
          uri: lb://cloud-payment-service
          predicates:
            #断言,路径相匹配的进行路由
            - Path=/payment/get/**

        - id: payment_routh2
          uri: lb://cloud-payment-service
          predicates:
            #断言,路径相匹配的进行路由
            - Path=/payment/lb/**
            - Cookie=username,zzyy

eureka:
  instance:
    hostname: cloud-gateway-service
  client:
    service-url:
      register-with-eureka: true
      fetch-registry: true
      # 单机版
      defaultZone: http://eureka7001.com:7001/eureka

Cookie Route Predicate 需要两个参数,一个是 Cookie name,一个是正则表达式。

路由规则会通过获取对于的 Cookie name 值和正则表达式去匹配,如果匹配上就会执行路由,如果没有匹配则不执行。

执行命令

curl http://localhost:9527/payment/lb --cookie "username=zzyy"

这个是在 cmd 命令行页面执行的!

正常效果

在这里插入图片描述

如果在执行的时候没有加上 cookie ,那么也无法正常执行,错误效果如下

在这里插入图片描述

所以千万要记得加上在执行对应的方法!!!!

5的使用

server:
  port: 9527

spring:
  application:
    name: cloud-gateway
  cloud:
    gateway:
      discovery:
        locator:
          #开启从注册中心动态创建路由的功能,利用微服务名进行路由
          enabled: true
      routes:
        #路由的ID,没有固定规则但要求唯一,建议配合服务名
        - id: payment_routh
          uri: lb://cloud-payment-service
          predicates:
            #断言,路径相匹配的进行路由
            - Path=/payment/get/**

        - id: payment_routh2
          #uri: http://localhost:8001   #匹配后提供服务的路由地址
          uri: lb://cloud-payment-service
          predicates:
            #断言,路径相匹配的进行路由
            - Path=/payment/lb/**
            # 请求头要有X-Request-Id属性并且值为整数的正则表达式
            - Header=X-Request-Id,\d+

eureka:
  instance:
    hostname: cloud-gateway-service
  client:
    service-url:
      register-with-eureka: true
      fetch-registry: true
      # 单机版
      defaultZone: http://eureka7001.com:7001/eureka

这个也有两个参数,一个是属性名称一个是正则表达式,这个属性值和正则表达式匹配则执行。

这个方法和第四种很像

curl http://localhost:9527/payment/lb -H "X-Request-Id:123"

在这里插入图片描述

7的使用

这个是设置只能以规定的提交方式进行提交,否则就无法正常访问!

server:
  port: 9527

spring:
  application:
    name: cloud-gateway
  cloud:
    gateway:
      discovery:
        locator:
          #开启从注册中心动态创建路由的功能,利用微服务名进行路由
          enabled: true
      routes:
        #路由的ID,没有固定规则但要求唯一,建议配合服务名
        - id: payment_routh
          uri: lb://cloud-payment-service
          predicates:
            #断言,路径相匹配的进行路由
            - Path=/payment/get/**

        - id: payment_routh2
          uri: lb://cloud-payment-service
          predicates:
            #断言,路径相匹配的进行路由
            - Path=/payment/lb/**
            #设置提交方式
            - Method=GET

eureka:
  instance:
    hostname: cloud-gateway-service
  client:
    service-url:
      register-with-eureka: true
      fetch-registry: true
      # 单机版
      defaultZone: http://eureka7001.com:7001/eureka

6、8、9暂时略过

过滤器

路由过滤器可用于修改进入的 HTTP 请求和返回的 HTTP 响应,路由过滤器只能指定路由进行使用。

Spring Cloud Gateway 内置了多种路由过滤器,他们都由 GatewayFilter 的工厂 类来产生。

生命周期

在这里插入图片描述

GatewayFilter 有 31 种

GlobalFilter 有 10 多种

自定义过滤器(GlobalFilter)

想要实现自定义过滤器就需要先满足两个接口,分别是 GlobalFilterOrdered

自定义过滤器可以用来记录全局日志、统一网关鉴权等…

案例

在 9527 中配置自定义过滤器

建议: 可以创建一个包(config)用于存放,看个人

@Component
@Slf4j
public class MyLogGateWayFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        /**
         * 小案例:在进入指定路径时必须有用户名,如果没有则不能进入
         */
        log.info("-----come in MyLogGateWayFilter:" + new Date());
        //获取用户名
        String uname = exchange.getRequest().getQueryParams().getFirst("uname");
        //验证用户名是否存在
        if (uname == null) {
            log.info("-----用户名为null,是非法用户");
            //如果不存在则返回一个http状态,进行对应提示
            exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
            return exchange.getResponse().setComplete();
        }
        //如果存在则正常进入
        return chain.filter(exchange);
    }

    //记载过滤器的顺序,数字越小优先级越高
    @Override
    public int getOrder() {
        return 0;
    }
}

一定要记得加 @Component 注解

测试

启动顺序:7001–8001-8002-9527

在这里插入图片描述

我们按照要求来进行访问时就可以正常访问,顺便一提,我们这里没有固定uname的值,所以只要给了一个值就可以正常访问!

但是如果我们不加参数直接访问,那么就无法正常访问,如下图

在这里插入图片描述

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值