Gateway服务网关概述

1、SpringCloud Gateway 是 SpringCloud 的一个全新项目,基于Spring 5.0 + Springboot 2.0 和 Project Reactor 等技术开发的服务网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。
SpringCloud Gateway 作为 SpringCloud 生态系统中的网关,目标是替代Zuul,在SpringCloud 2.0以上版本中,没有对新版本的Zuul 2.0以上最新高性能版本进行集成,仍然还是使用的Zuul 1.x非Reactor模式的老版本。而为了提升网关的性能,SpringCloud Gateway 是基于WebFlux 实现的,而WebFlux框架底层则使用了高性能的Reactor模式通信框架Netty。
作用:Spring Cloud Gateway的目标提供统一的路由方式且基于Filter链的方式提供了网关基本的功能,例如:安全、监控/指标,和限流。(反向代理、鉴权、流量控制、熔断、日志监控.......
在这里插入图片描述
2、为什么要选择Gateway?
Gateway:
(1)Gateway 是基于异步非阻塞模型进行开发的,性能强大。
(2)基于Spring 5.0、Project Reactor 和 SpringBoot 2.0 构建。
(3)动态路由:能够匹配任何请求属性;
(4)可以对路由指定 Predicate(断言)和 Filter(过滤器)。
(5)集成 Hystrix 的断路器功能。
(6)集成SpringCloud的服务发现功能。
(7)易于编写的 Predicate(断言)和 Filter(过滤器)。
(8)请求限流功能。
(9)支持路径重写。
Zuul:
(1)Zuul 1.x 是一个基于阻塞 I/O 的API网关
(2)Zuul 1.x 是基于Servlet2.5 使用阻塞架构,它不支持在任何长连接(如:WebSocket),Zuul的设计模式和Nginx较像,每次 I/O 操作都是从工作线程中选择一个执行,请求线程被阻塞到工作线程完成,但是差别是Nginx是用C++实现。Zuul用Java实现,而JVM本省会有第一次加载较慢的情况,使得Zuul的性能相对较差
(3)Zuul 2.x 理念更先进,想基于Netty 非阻塞和支持长连接,但SpringCloud目前还没有整合。Zuul 2.x 的性能较Zuul 1.x 有较大提升。在性能方面,根据官方提供的基准测试,SpringCloud Gateway 的 RPS(每秒请求数)是 Zuul 的1.6倍。
(4)SpringCloud Gateway 建立在Spring 5.0、Project Reactor 和 SpringBoot 2.0 之上,使用非阻塞API。
(5)SpringCloud Gateway还支持WebSocket,并且与spring紧密继承拥有更好的开发体验。

Gateway核心概念

一、总体概念图示:

1、路由(Route):路由是构建网关的基本模块,它由ID、目标URI,一系列的断言和过滤器组成,如果断言为true,则匹配该路由。
2、断言(predicate):参考Java8的java.util.function.Predicate。开发人员可以匹配HTTP请求中的所有内容(如:请求头、请求参数),如果请求与断言相匹配则进行路由
3、过滤器(Filter):指的是Spring框架中 GatewayFilter的实例,使用过滤器,可以在请求之前或者之后对请求进行修改。
在这里插入图片描述
web请求,通过与Predicate相匹配,定位到真正的服务节点,并在这个转发过程前后,进行一些精细化控制。Predicate就是请求的匹配条件。而FIlter,就可以理解为一个无所不能的拦截器。有了这两个元素,再加上目标URI,就可以实现一个具体的路由

二、Gateway工作流程图

在这里插入图片描述
客户端向 Spring Cloud Gateway 发出请求。如果网关处理程序映射确定请求与路由匹配,则将其发送到网关 Web 处理程序。此处理程序通过特定于请求的过滤器链运行请求。过滤器被虚线分隔的原因是过滤器可以在发送代理请求之前和之后运行逻辑。执行所有pre过滤器逻辑。然后进行代理请求。发出代理请求后,将运行post过滤器逻辑。
Filter在 pre类型的过滤器可以做参数校验、权限校验、流量监控、日志输出、协议转换等。post类型的过滤器中可以做相应内容、响应头的修改、日志输出、流量监控等非常重要的作用。

Gateway实例与配置

官网配置:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html

一、方式一:yml 配置文件配置
1、创建module:cloud-gateway-gateway9527
2、引入 pom 依赖

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

3、yml 配置文件

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

      - id: payment_route2
        uri: http://localhost:8001
        predicates:
        - Path=/payment/lb/**
            
#注册中心配置
eureka:
  client:
    #表示收将自己注册到EurekaServer,默认为true
    register-with-eureka: true
    #是否从EurekaServer抓取已有的注册信息,默认为true,单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    fetchRegistry: true
    service-url:
      #      defaultZone: http://localhost:7001/eureka
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
  instance:
    hostname: cloud-gateway-service
    instance-id: cloud-gateway-service9527
    prefer-ip-address: true   #访问路径可以显示IP地址

4、主启动类

@SpringBootApplication
@EnableEurekaClient
public class GatewayMain9527 {
    public static void main(String[] args) {
        SpringApplication.run(GatewayMain9527.class, args);
    }
}

5、测试

测试地址http://localhost:8001/payment/get/1http://localhost:9527/payment/get/1
测试发现两个地址的访问结果一致。

一、方式二:RouteLocator(路由定位)配置

@Configuration
public class GatewayConfig {

    /*路由国内新闻*/
    @Bean
    public RouteLocator customRouteRule(RouteLocatorBuilder routeLocatorBuilder) {
        RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
        routes.route("path_route_atguigu",
                route -> route.path("/guonei")
                        .uri("https://news.baidu.com/")).build();
        return routes.build();
    }
}

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

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

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

      - id: payment_route2
        #uri: http://localhost:8001
        uri: lb://cloud-payment-service #匹配后提供服务的路由地址
        predicates:
        - Path=/payment/lb/**
        - After=2021-09-29T11:42:42.915+08:00[Asia/Shanghai]


eureka:
  client:
    #表示收将自己注册到EurekaServer,默认为true
    register-with-eureka: true
    #是否从EurekaServer抓取已有的注册信息,默认为true,单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡
    fetchRegistry: true
    service-url:
      #defaultZone: http://localhost:7001/eureka
      defaultZone: http://eureka7001.com:7001/eureka,http://eureka7002.com:7002/eureka
  instance:
    hostname: cloud-gateway-service
    instance-id: cloud-gateway-service9527
    prefer-ip-address: true   #访问路径可以显示IP地址

需要注意的是:uri的协议为 lb,表示启用Gateway的负载均衡功能lb://micro-service-name是 SpringCloud Gateway 在微服务中自动为我们创建的负载均衡 uri。

路由断言工厂(Route Predicate Factories)

只有满足断言的匹配规则才能通过网关正常访问路由地址。
官网地址:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gateway-request-predicates-factories

一、After Route Predicate

spring:
  cloud:
    gateway:
      routes:
      - id: after_route
        uri: https://example.org
        predicates:
        - After=2017-01-20T17:42:47.789-07:00[America/Denver]

表示在指定时间之后可以对该路由地址进行访问,否则访问 404 Not Found,获取改时间格式可通过如下方式获取:
ZonedDateTime zonedDateTime = ZonedDateTime.now(); System.out.println(zonedDateTime); zonedDateTime就是我们需要配置的时间格式。

二、Before Route Predicate

spring:
  cloud:
    gateway:
      routes:
      - id: before_route
        uri: https://example.org
        predicates:
        - Before=2017-01-20T17:42:47.789-07:00[America/Denver]

表示在指定时间之前可以对该路由地址进行访问,否则访问 404 Not Found,获取时间格式同上。

三、Between Route Predicate

spring:
  cloud:
    gateway:
      routes:
      - id: between_route
        uri: https://example.org
        predicates:
        - Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]

表示在指定的时间区间内可以对改路由地址正常访问,否则访问 404 Not Found,获取时间格式同上。

四、Cookie Route Predicate

spring:
  cloud:
    gateway:
      routes:
      - id: cookie_route
        uri: https://example.org
        predicates:
        - Cookie=name,test

postman测试:
在这里插入图片描述
表示此路由匹配具有名为 name 的 cookie 的请求,该 cookie 的值与 test 正则表达式匹配时,才能够对改路由地址进行访问。否则访问 否则访问404 Not Found

五、Header Route Predicate

spring:
  cloud:
    gateway:
      routes:
      - id: header_route
        uri: https://example.org
        predicates:
        - Header=token,123

postman测试:
在这里插入图片描述
表示请求头中包含字段名(任意字段)token,并且值与123正则表达式匹配的请求,才能够对该路由地址进行访问。否则访问404 Not Found

六、Host Route Predicate

spring:
  cloud:
    gateway:
      routes:
      - id: host_route
        uri: https://example.org
        predicates:
        - Host=**.gateway.com,**.springcloudgateway.com

postman测试:
在这里插入图片描述
表示请求头中包含字段名Host,并且值与123正则表达式匹配的请求,才能够对该路由地址进行访问。否则访问404 Not Found

七、Method Route Predicate

spring:
  cloud:
    gateway:
      routes:
      - id: method_route
        uri: https://example.org
        predicates:
        - Method=GET,POST

在这里插入图片描述
表示该请求方法必须是与指定的断言中的Method匹配,才能对该路由地址进行访问,否则访问404 Not Found

八、Path Route Predicate

spring:
  cloud:
    gateway:
      routes:
      - id: path_route
        uri: https://example.org
        predicates:
        - Path=/red/{segment},/blue/{segment}

在这里插入图片描述
表示该请求的地址与指定断言Path匹配,才能对该路由地址进行访问,否则访问404 Not Found

九、Query Route Predicate

1、只要包含该查询参数就可以匹配成功

spring:
  cloud:
    gateway:
      routes:
      - id: query_route
        uri: https://example.org
        predicates:
        - Query=green

在这里插入图片描述

2、包含查询参数和值,其中值可以使用正则表达式匹配,只有key和value都匹配上才能对该路由地址正常访问。

spring:
  cloud:
    gateway:
      routes:
      - id: query_route
        uri: https://example.org
        predicates:
        - Query=red, gree.

在这里插入图片描述

十、RemoteAddress Route Predicate

1、如果请求的客户端的ip地址是192.168.1.1到192.168.1.24的范围。则路由会被转发到 https://example.org。

spring:
  cloud:
    gateway:
      routes:
      - id: remoteaddr_route
        uri: https://example.org
        predicates:
        - RemoteAddr=192.168.1.1/24

2、暂未解决

spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #开启从注册中心动态创建路由的功能,通过微服务名进行路由
      routes:
      - id: payment_route2
        uri: lb://cloud-payment-service #匹配后提供服务的路由地址
        predicates:
        - Path=/payment/lb/**
        - RemoteAddr=192.168.0.1/24

十一、Weight Route Predicate

spring:
  cloud:
    gateway:
      routes:
      - id: weight_high
        uri: https://weighthigh.org
        predicates:
        - Weight=group1, 8
      - id: weight_low
        uri: https://weightlow.org
        predicates:
        - Weight=group1, 2

表示该路由会将约 80% 的流量转发到 weighthigh.org,约 20% 的流量转发到 weightlow.org。

网关过滤器工厂(GatewayFilter Factories)

官网网关过滤器

官网配置:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gatewayfilter-factories

1、生命周期:prepost
2、种类:31种。见官网配置。

自定义网关过滤器

1、Filter配置类(例:全局网关过滤器)

package com.atguigu.springcloud.filter;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Date;

/**
 * @author 李宏伟
 * @version 1.0
 * @ClassName GlobalLogGatewayFilter
 * @Description
 * @date 2021年10月14日 10:14
 */
@Component
@Slf4j
public class GlobalLogGatewayFilter implements GlobalFilter, Ordered {

    /**
     * 全局日志网关过滤器:
     *  ServerWebExchange:表示Web请求服务,可获取请求HttpServletRequest、响应HttpServletResponse等
     *  chain:表示网关过滤连
     *
     * @return
     * @params
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        log.info("***********Come in GlobalLogGatewayFilter:" + new Date());

        ServerHttpRequest request = exchange.getRequest();
        String name = request.getQueryParams().getFirst("name");
        if (name == null) {

            log.info("***********用户名不能为空,非法用户");

            //验证失败,设置请求失败状态
            exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
            //最后终止向下一个过滤器方法提供委托,并返回响应
            return exchange.getResponse().setComplete();
        }
        // 放行,并将委托给过滤链中的下一个过滤器
        return chain.filter(exchange);
    }

    /**
     * 可以在全局网关过滤器配置类中加入 @Order 注解代替实现Ordered接口
     * 加载过滤器的优先级,数值越小,加载优先级越高;反之越小
     *
     * @return
     * @params
     */
    @Override
    public int getOrder() {
        return 0;
    }
}

2、测试:请求访问:http://localhost:9527/payment/lb?name=123能够正常返回,访问:http://localhost:9527/payment/lb?age=123是返回如下状态:
在这里插入图片描述

Gateway实现服务降级、熔断、限流

参考文章:https://blog.csdn.net/zhuyu19911016520/article/details/86499528

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐