阅读 568

服务网关GateWay

GateWay

一、概述简介

1、官网

cloud.spring.io/spring-clou…

2、概述

image.png

SpringCloud Gateway 是 Spring Cloud 的一个全新项目,基于 Spring 5.0+Spring Boot 2.0 和 Project Reactor 等技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。

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

Spring Cloud Gateway的目标提供统一的路由方式且基于 Filter 链的方式提供了网关基本的功能,例如:安全,监控/指标,和限流。

总结:SpringCloud Gateway 使用的Webflux中的reactor-netty响应式编程组件,底层使用了Netty通讯框架。

3、能干嘛

反向代理、鉴权、流量控制、熔断、日志监控……

4、微服务架构中网关在哪里

image.png

5、有Zuul了怎么又出来了gateway

(1)为什么选择Gateway

①neflix不太靠谱,zuul2.0一直跳票,迟迟不发布。

②SpringCloud Gateway具有如下特性:

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

③SpringCloud Gateway 与 Zuul的区别:

Zuul 1.x,是一个基于阻塞 I/ O 的 API Gateway,基于Servlet 2. 5使用阻塞架构

(2)Zuul1.x模型

Springcloud中所集成的Zuul版本,采用的是Tomcat容器,使用的是传统的Servlet IO处理模型。

servlet由servlet container进行生命周期管理。container启动时构造servlet对象并调用servlet init()进行初始化;container运行时接受请求,并为每个请求分配一个线程(一般从线程池中获取空闲线程)然后调用service();container关闭时调用servlet destory()销毁servlet。

image.png

servlet在并发不高的场景下这种模型是适用的,但是一旦高并发(比如抽风用jemeter压),线程数量就会上涨,而线程资源代价是昂贵的(上线文切换,内存消耗大),严重影响请求的处理时间。

(3)GateWay模型

底层用WebFlux和Netty。

传统的Web框架,比如说:struts2,springmvc等都是基于Servlet API与Servlet容器基础之上运行的。但是在Servlet3.1之后有了异步非阻塞的支持。而WebFlux是一个典型非阻塞异步的框架,它的核心是基于Reactor的相关API实现的。相对于传统的web框架来说,它可以运行在诸如Netty,Undertow及支持Servlet3.1的容器上。非阻塞式+函数式编程(Spring5必须让你使用java8)。

Spring WebFlux 是 Spring 5.0 引入的新的响应式框架,区别于 Spring MVC,它不需要依赖Servlet API,它是完全异步非阻塞的,并且基于 Reactor 来实现响应式流规范

二、三大核心概念

1、Route(路由)

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

2、Predicate(断言)

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

3、Filter(过滤)

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

4、总体

image.png

web请求,通过一些匹配条件,定位到真正的服务节点。并在这个转发过程的前后,进行一些精细化控制。predicate就是我们的匹配条件;而filter,就可以理解为一个无所不能的拦截器。有了这两个元素,再加上目标uri,就可以实现一个具体的路由了。

三、Gateway工作流程

1、官网总结

image.png

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

Handler 再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前(“pre”)或之后(“post”)执行业务逻辑。

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

2、核心逻辑

路由转发+执行过滤器链

四、入门配置

1、新建Module

cloud-gateway-gateway9527

2、POM

<!--gateway-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!--eureka-client      //zookeeper、consul也可以-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
复制代码

3、YML新增网关配置

server:
  port: 9527

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

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

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

4、无业务类

5、主启动类

@SpringBootApplication
@EnableEurekaClient
public class GateWayMain9527 {
    public static void main(String[] args) {
        SpringApplication.run(GateWayMain9527.class,args);
    }
}
复制代码

6、测试

启动7001、启动8001(cloud-provider-payment8001)、启动9527网关

添加网关前:http://localhost:8001/payment/get/31

添加网关后:http://localhost:9527/payment/get/31

7、Gateway网关路由另一种配置方式

代码中注入RouteLocator的Bean

@Configuration
public class GateWayConfig{
    /**
     * 配置了一个id为route-name的路由规则,
     * 当访问地址 http://localhost:9527/guonei时会自动转发到地址:http://news.baidu.com/guonei
     * @param builder
     * @return
     */
    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder){
        RouteLocatorBuilder.Builder routes = builder.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 builder){
        RouteLocatorBuilder.Builder routes = builder.routes();
        routes.route("path_route_atguigu2", r -> r.path("/guoji").uri("http://news.baidu.com/guoji")).build();
        return routes.build();
    }
}
复制代码

五、通过微服务名实现动态路由

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

1、启动

一个eureka7001 + 两个服务提供者8001/8002

2、POM

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

3、YML

需要注意的是uri的协议为lb,表示启用Gateway的负载均衡功能。

lb://serviceName是spring cloud gateway在微服务中自动为我们创建的负载均衡uri。

server:
  port: 9527

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

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

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

4、测试

http://localhost:9527/payment/lb

8001/8002两个端口切换

六、Predicate的使用

1、Route Predicate Factories

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

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

Spring Cloud Gateway 创建 Route 对象时, 使用 RoutePredicateFactory 创建 Predicate 对象,Predicate 对象可以赋值给 Route。

Spring Cloud Gateway 包含许多内置的Route Predicate Factories。

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

2、常用的Route Predicate

(1)After Route Predicate

public class ZonedDateTimeDemo{
    public static void main(String[] args){
        ZonedDateTime zbj = ZonedDateTime.now(); // 默认时区
        System.out.println(zbj);
//        ZonedDateTime zny = ZonedDateTime.now(ZoneId.of("America/New_York")); // 用指定时区获取当前时间
//        System.out.println(zny);
    }
}
复制代码
     predicates:
       - Path=/payment/lb/**         # 断言,路径相匹配的进行路由
       - After=2020-02-05T15:10:03.685+08:00[Asia/Shanghai]         # 断言,路径相匹配的进行路由
复制代码

(2)Before Route Predicate

 - Before=2020-02-05T15:10:03.685+08:00[Asia/Shanghai]         # 断言,路径相匹配的进行路由
复制代码

(3)Between Route Predicate

- Between=2020-02-02T17:45:06.206+08:00[Asia/Shanghai],2020-03-25T18:59:06.206+08:00[Asia/Shanghai]
复制代码

(4)Cookie Route Predicate

Cookie Route Predicate需要两个参数,一个是 Cookie name ,一个是正则表达式。路由规则会通过获取对应的 Cookie name 值和正则表达式去匹配,如果匹配上就会执行路由,如果没有匹配上则不执行。

- Cookie=username,zzyy
复制代码

(5)Header Route Predicate

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

- Header=X-Request-Id, \d+  # 请求头要有X-Request-Id属性并且值为整数的正则表达式
复制代码

(6)Host Route Predicate

Host Route Predicate 接收一组参数,一组匹配的域名列表,这个模板是一个 ant 分隔的模板,用.号作为分隔符。它通过参数中的主机地址作为匹配规则。

 - Host=**.atguigu.com
复制代码

(7)Method Route Predicate

- Method=GET
复制代码

(8)Path Route Predicate

 - Path=/payment/lb/**         # 断言,路径相匹配的进行路由
复制代码

(9)Query Route Predicate

支持传入两个参数,一个是属性名,一个为属性值,属性值可以是正则表达式。

- Query=username, \d+  # 要有参数名username并且值还要是整数才能路由
复制代码

(10)小总结

Predicate就是为了实现一组匹配规则,让请求过来找到对应的Route进行处理。

七、Filter的使用

1、是什么

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

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

2、Spring Cloud Gateway的Filter

(1)生命周期

pre、post

(2)种类

GatewayFilter、GlobalFilter

3、常用的GatewayFilter

AddRequestParameter

 filters:
            - AddRequestParameter=X-Request-Id,1024 #过滤器工厂会在匹配的请求头加上一对请求头,名称为X-Request-Id值为1024
复制代码

4、自定义过滤器(自定义全局GlobalFilter)

(1)两个主要接口介绍

implements GlobalFilter,Ordered

(2)能干嘛

全局日志记录、统一网关鉴权……

(3)案例代码

@Component //必须加,必须加,必须加
public class MyLogGateWayFilter implements GlobalFilter,Ordered{
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain){
        System.out.println("time:"+new Date()+"\t 执行了自定义的全局过滤器: "+"MyLogGateWayFilter"+"hello");

        String uname = exchange.getRequest().getQueryParams().getFirst("uname");
        if (uname == null) {
            System.out.println("****用户名为null,无法登录");
            exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }

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

(4)测试

http://localhost:9527/payment/lb?uname=z3

文章分类
后端