Gateway新一代网关

是什么

Gateway是在Spring生态系统之上构建的API网关服务,基于Spring 5,Spring Boot 2和 Project Reactor等技术。Gateway旨在提供一种简单而有效的方式来对API进行路由,以及提供一些强大的过滤器功能, 例如:熔断、限流、重试等。

Spring Cloud Gateway 是Spring Cloud的一个全新项目,基于Spring 5.0 + Spring Boot 2.x和Project Reactor等技术开发的网关,旨在为微服务架构提供一种有效的统一的API路由管理方式。 Spring Cloud Gateway作为Spring Cloud生态系统中的网关组件,目标是替代Zuul。由于Zuul2.x的多次跳票,为了提升网关的性能,Spring Cloud官方基于Spring WebFlux开发了非阻塞的网关组件Gateway,WebFlux框架底层使用了高性能的Reactor模式的非阻塞通信框架Netty。

官方资料https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1.RELEASE/reference/html/

能干嘛

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

网关在微服务架构中的位置

有Zuul了怎么又出来gateway

  • 我们为什么选择Gateway?
    • 1.netflix不太靠谱,zuul2.0一直跳票,迟迟不发布
    • 2.Spring Cloud Gateway具有如下特性:
      基于Spring Framework 5, Project Reactor和Spring Boot 2.0进行构建;
      动态路由:能够匹配任何请求属性;
      可以对路由指定Predicate (断言)和Filter (过滤器) ;
      集成Hystrix的断路器功能;
      集成Spring Cloud服务发现功能;
      易于编写的Predicate (断言)和Filter (过滤器) ;
      请求限流功能;
      支持路径重写。
    • zuul1是基于servlet之上的一个 阻塞式处理模型,而gateway是基于WebFlux

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

三大核心概念

  • Route(路由):路由是构建网关的基本模块,它由ID,目标URI,一系列的断言和过滤器组成,如断言为true则匹配该路由
  • Predicate(断言):参考的是Java8的java.util.function.Predicate开发人员可以匹配HTTP请求中的所有内容(例如请求头或请求参数),如果请求与断言相匹配则进行路由
  • Filter(过滤):指的是Spring框架中GatewayFilter的实例,使用过滤器,可以在请求被路由前或者之后对请求进行修改.

image-20200330203814548

gateway的工作流程

客户端向Spring Cloud Gateway发出请求。然后在Gateway Handler Mapping中找到与请求相匹配的路由,将其发送到Gateway Web Handler。Handler再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前( "pre” )或之后( "post" )执行业务逻辑。Filter在"pre" 类型的过滤器可以做参数校验、权限校验、流量监控、日志输出、协议转换等,在"post"类型的过滤器中可以做响应内容、响应头的修改,日志的输出,流量监控等有着非常重要的作用。

简单地说:

  1. 请求去访问GateWay Client
  2. 访问GateWay Handler Mappeing 寻找有没有与路径相匹配的路由
  3. 根据predicate来决定访问那个路由
  4. 再经过pre类型的过滤器,进行请求代理
  5. 再经过post类型过滤器

入门案例

  • 新建 cloud-gateway-gateway9527

  • pom

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <!--gateway无需web和actuator-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </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>
        <dependency>
            <groupId>com.example</groupId>
            <artifactId>cloud-api-common</artifactId>
            <version>${project.version}</version>
        </dependency>
    </dependencies>
    
  • yml

  • 主启动类

    @SpringBootApplication
    @EnableEurekaClient
    public class GateWayMain9527 {
         public static void main(String[] args) {
                 SpringApplication.run(GateWayMain9527.class,args);
             }
    }
    
  • 在yml中新增配置

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

gateway的两种配置方式

第一种就是直接用yml配置

第二种就是使用配置类bean注入的方式(不推荐)

@Configuration
public class GateWayConfig {
    /**
     *id:路由名,
     * @param routeLocatorBuilder
     * @return
     */
    @Bean
    public RouteLocator customRouteLocator2(RouteLocatorBuilder routeLocatorBuilder) {
        RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
        return routes.route("path_route", r -> r.path("/markets/3c/tbdc").uri("https://www.taobao.com/")).build();
    }

}

测试: 访问原来的https://www.taobao.com/markets/3c/tbdc

访问通过gateway网关转发的http://localhost:9527/markets/3c/tbdc

注意: 在配置类里面配置的,

通过服务名实现动态

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

【实例】

不要忘记引入Eureka的依赖

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

在yml中新增配置

discovery:
  locator:
    enabled: true # 开启从注册中心动态创建路由的功能,利用微服务名称j进行路由

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

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

Predicate断言的类型

Spring Cloud Gateway包含许多内置的路由断言Factories。这些断言都匹配HTTP请求的不同属性。多个路由断言Factories可以通过 and 组合使用

一些默认的路由断言 Factory配置

#路由断言
-Path=/auth/**
#After路由断言,使用一个日期时间,在该日期以后请求才被匹配
- After=2020-03-31T00:00:18.471+08:00[Asia/Shanghai]
#Before路由断言,使用一个日期时间,在该日期之前才被匹配
- Before=2020-03-31T00:00:18.471+08:00[Asia/Shanghai]
- Cookie=username,zzyy
- Header=X-Request-Id, \d+ #请求头要有X-Request-Id属性,并且值为正数
- Host=**.hzx.com
- Method=GET
- Query=username, \d+ # 要有参数名username并且值还要是正整数才能路由
#Between路由断言,使用两个参数用逗号分隔,在两个时间范围内的请求才被匹配
-Between=2020-03-12T17:42:47.789-07:00,2020-10-12T17:42:47.789-07:00

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

还有这个2020-03-31T00:00:18.471+08:00[Asia/Shanghai]这一串这么来的

public static void main(String[] args) {
    ZonedDateTime zonedDateTime=ZonedDateTime.now();
    System.out.println(zonedDateTime);
}

这样送出出来就是这样子的

Filter的使用

filter分为pre类型过滤器和post类型过滤器,pre类型过滤器作用于访问路由前,可以进行鉴权,限流,日志输出,请求头的修改,post类型过滤器作用访问路由后,可以进行响应头协议的修改。

内置Filter

全局过滤器作用
Forward Routing Filter用于本地forward,也就是将请求在Gateway服务内进行转发,而不是转发到下游服务
LoadBalancerClient Filter整合Ribbon实现负载均衡
Netty Routing Filter使用Netty的 HttpClient 转发http、https请求
Netty Write Response Filter将代理响应写回网关的客户端侧
RouteToRequestUrl Filter将从request里获取的原始url转换成Gateway进行请求转发时所使用的url
Websocket Routing Filter使用Spring Web Socket将转发 Websocket 请求
Gateway Metrics Filter整合监控相关,提供监控指标

SpringCloud GateWay 内置了许多Filter

【示例】

spring:
  cloud:
    gateway:
      routes:
      - id: add_request_header_route
        uri: http://example.org
        filters:
        - AddResponseHeader=X-Response-Foo, Bar

更多示例可以看官网介绍

自定义Filter

需要实现两个接口GlobalFilter,OrderI

【示例】

创建一个GateWayFilter类 ,实现GlobalFilter,Order连个接口

@Component
@Slf4j
public class GateWayFilter implements Ordered, GlobalFilter {
  /**
  *进行filter自定义操作
 */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("come in :"+ new Date());
        ServerHttpRequest request = exchange.getRequest();
        String name = request.getQueryParams().getFirst("name");
        //假如name为空
        if (name ==null){
            log.error("name 不能为 空");
            exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
            return  exchange.getResponse().setComplete();
        }

        return chain.filter(exchange);
    }

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

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

Q.E.D.

知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议

只有不断的努力才会有更大的惊喜等着你去发现!!