Gateway服务网关

概述

Cloud全家桶中有一个很重要的组件就是网关,在1.X版本都是采用的Zuul网关。但是在2.x版本中,Zuul的升级一直跳票,Spring最后自己研发了一个网关来替代Zuul,也就是SpringCloud Gateway

Gateway是在Spring生态系统之上构建的Api网关服务,基于Spring 5Spring Boot2Project Reactor等技术。Gateway旨在提供一种简单而有效的方式来对Api进行路由,以及提供一些强大的过滤器功能,例如熔断、限流、重试等等。为了提升网关的性能,SpringCloud Gateway是基于WebFlux框架实现的,而WebFlux框架底层则使用了高性能的Reactor模式通信框架Netty。

WebFlux

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

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

核心三大概念

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

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

工作流程

Spring Cloud GatewayProxyFilterFilterFilterFilterGateway WebHandlerGateway HandlerMappingGatewayClientProxiedService

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

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

Filter在“pre”类型的过滤器可以做参数校验、权限校验、流量监控、日志输出、协议转换等等。

Filter在“post”类型的过滤器可以做响应内容、响应头修改、日志输出,流量监控等等。

Gateway网关路由有两种配置方法

yml配置文件

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

注入RouteLocator的Bean

/**
 * Gateway配置
 *
 * @author Error
 * @class GatewayConfig
 */
@Configuration
public class GatewayConfig {
    @Bean
    public RouteLocator routes(RouteLocatorBuilder routeLocatorBuilder){
        return routeLocatorBuilder.routes()
                .route("route1",predicateSpec -> predicateSpec.path("/blog").uri("https://zygzyg.cloud"))
                .route("route2",predicateSpec -> predicateSpec.path("/alist").uri("https://alist.zygzyg.cloud"))
                .build();
    }
}

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

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

在yml中开启:

spring:
  application:
    name: cloud-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true                       # 开启从注册中心动态创建路由的功能,利用微服务名进行路由。
      routes:
        - id: service-route                   # 路由的ID,没有固定要求但是要求唯一,建议配合服务名
          uri: lb://cloud-service             # 修改为微服务名称
          predicates:
            - Path=/service/get/**  

Predicate的使用

SpringCloudGateway将路由匹配作为SpringWebFlux HandlerMapping基础架构的一部分。SpringCloudGateway包括许多内置的RoutePredicate Factory。所有这些Predicate都与HTTP请求的不同属性匹配。多个RoutePredicate工厂可以进行组合。

SpringCloudGateway创建Route对象时。使用RoutePredicateFactory创建Predicate对象,Predicate对象可以赋值给Route。SpringCloudGateway包含许多内置的RoutePredicateFactories。所有这些谓词都匹配HTTP请求的不同属性。多种谓词工厂可以组合,并通过逻辑and。

常用的RoutePredicateFactory

AbstractRoutePredicateFactory<C>AfterRoutePredicateFactoryBeforeRoutePredicateFactoryBetweenRoutePredicateFactory«Interface»Configurable<C>CookieRoutePredicateFactoryFunctionalInterfaceHeaderRoutePredicateFactoryHostRoutePredicateFactoryMethodRoutePredicateFactoryPathRoutePredicateFactoryQueryRoutePredicateFactory«Interface»RoutePredicateFactory<C>«Interface»ShortcutConfigurable
  • AfterRoutePredicate:

    predicates:
      - After=2020-06-17T19:40:00.444+8:00[Asia/Shanghai] #在指定时间之后才能请求
    
  • BeforeRoutePredicate:

    predicates:
      - Before=2020-06-17T19:40:00.444+8:00[Asia/Shanghai] #在指定时间之前才能请求
    
  • BetweenRoutePredicate:

    predicates:
      - Between=2020-06-17T19:40:00.444+8:00[Asia/Shanghai],2020-06-20T19:40:00.444+8:00[Asia/Shanghai] #在指定时间之间才能请求
    
  • CookieRoutePredicate

    CookieRoutePredicate需要两个参数,一个是Cookie name,一个是正则表达式。路由规则会通过获取对应的Cookie name值和正则表达式匹配。匹配上才能请求反之不能。

    predicates:
      - Cookie=apple,ch.p
    
  • HeaderRoutePredicate:

    HeaderRoutePredicate需要两个参数,一个属性名称和一个正则表达式,这个属性值和正则表达式匹配则执行反之不执行。

    predicates:
      - Header=X-Request-Id,\d+
    
  • HostRoutePredicate:

    HostRoutePredicate接收一组参数,一组匹配的域名列表,这个模板是一个ant分割的模板,用“,”号作为分隔符。它通过参数中的主机地址作为匹配原则。

    predicates:
      - Host=**.somehost.org,**.somehost.org
    
  • MethodRoutePredicate:

    predicates:
      - Method=GET #如果请求方式是GET则匹配执行,反之不行。
    
  • PathRoutePredicate:

    predicates:
      - Path=/service/get #路径匹配才能执行。
    
  • QueryRoutePredicate:

    QueryRoutePredicate可以有一个参数正则表达式,如果query参数匹配则执行。

    QueryRoutePredicate也可以有两个参数,一个参数名称和一个正则表达式,这个参数值和正则表达式匹配则执行反之不执行。

    predicates:
      - Query=username,\d+ 
      - Query=\d+ 
    

Filter的使用

过滤器可用于修改进入的Http请求和返回的Http响应,路由过滤器只能指定路由进行使用。SpringCloudGateway内置了多种路由过滤器,它们都是由GatewayFilter的工厂类来产生。

Filter生命周期只有Pre和Post两个。Filter的种类有两个分别是GatewayFilterGlobalFilter

常用的GatewayFilter

filters:
  - AddRequestParameter=X-Request-Id,1024 #过滤器会在匹配的请求上加上参数X-Request-Id=1024

自定义全局过滤器

自定义全局过滤器需要实现接口GlobalFilter和Ordered,全局过滤器可以做统一鉴权,日志记录等等。

/**
 * 网关全局日志过滤器
 *
 * @author Error
 * @class GlobalLogFilter
 */
@Component
@Slf4j
public class GlobalLogFilter implements GlobalFilter, Ordered {
    /**
     * 处理 Web 请求并(可选)通过给定的 {@link GatewayFilterChain} 委托给下一个 {@code WebFilter}
     * @param exchange 当前交换机
     * @param chain    提供一种委托给下一个过滤器的方法
     * @return {@code Mono<Void>} 指示请求处理何时完成
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("log content");
        return chain.filter(exchange);
    }
    @Override
    public int getOrder() {
        return 0;
    }
}