SpringCloud——网关 Gateway

1. 概述

1.1 Gateway 是什么

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

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

1.2 Gateway 作用

  1. 反向代理
  2. 鉴权
  3. 流量控制
  4. 熔断
  5. 日志监控

1.3 微服务中网关的位置

 

1.4 Gateway 特性:

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

1.5 Gateway 与 Zuul 的区别

  • Zuul 1.x 是一个基于阻塞 I/ 0 的API Gateway
  • Zuul 1.x 基于 Servlet 2. 5 使用阻塞架构它不支持任何长连接(如WebSocket) 。Zuul 的设计模式和 Nginx 较像,每次|/ 0操作都是从工作线程中选择一个执行, 请求线程被阻塞到工作线程完成,但是差别是 Nginx 用 C++ 实现,Zuul 用Java实现,而JVM本身会有第一次加载较慢的情况,使得Zuul的性能相对较差。
  • Zuul 2.x理念更先进,想基于 Netty 非阻塞和支持长连接,但 SpringCloud 目前还没有整合。Zuul 2.x 的性能较 Zuul 1.x 有较大提升。
  • Spring Cloud Gateway 建立在 Spring Framework 5、Project Reactor和Spring Boot2之上,使用非阻塞API。
  • Spring Cloud Gateway 还支持WebSocket,并且与Spring紧密集成拥有 更好的开发体验。

 

传统的 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.6 三大核心概念

1. Route(路由)

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

2. Predicate(断言)

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

3. Filter(过滤)

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

 

1.7 Gateway 工作流程

 

客户端向 Spring Cloud Gateway 发出请求。然后在 Gateway Handler Mapping 中找到与请求相匹配的路由,将其发送到Gateway Web Handler。Handler 再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。

过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前( "pre" )或之后( "post")执行业务逻辑。Filter 在 "pre"  类型的过滤器可以做参数校验、权限校验、流量监控、日志输出、协议转换等。在 “post” 类型的过滤器中可以做响应内容、响应头的修改,日志的输出, 流量监控等有着非常重要的作用。

核心逻辑:路由转发+执行过滤器链

2. 实现

2.1 基本配置

创建 module: cloud-gateway-gateway9527

pom:

<!--新增gateway-->
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-test</artifactId>
	<scope>test</scope>
</dependency>

YML:

server:
  port: 9527
spring:
  application:
    name: cloud-gateway
  cloud:
    gateway:
      routes:
      - id: payment_routh # 路由的ID
        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
  client:
    service-url:
      register-with-eureka: true
      fetch-registry: true
      defaultZone: http://eureka7001.com:7001/eureka

启动类:

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

测试:

 

2.2 Gateway网关路由两种配置方式

1. 在配置文件 yml 中配置(同上)

2. 代码中注入 RouteLocator 的 Bean

@Configuration
public class GateWayConfig {
    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder) {
        RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
        routes.route("path_rote_zth", r -> r.path("/payment/lb").uri("http://localhost:8001")).build();
        return routes.build();
    }
}

 

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

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

YML;

server:
  port: 9527
spring:
  application:
    name: cloud-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
      routes:
      - id: payment_routh # 路由的ID
        uri: lb://CLOUD-PAYMENT-SERVICE
        predicates:
        - Path=/payment/get/**   # 断言,路径相匹配的进行路由
      - id: payment_routh2
        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

测试:

 

3. Predicate 

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。

3.1 常用 Route Predicate

After Route Predicate Factory:

此谓词匹配当前日期时间之后发生的请求。

yml:

spring:
  application:
    name: cloud-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
      routes:
      - id: payment_routh # 路由的ID
        uri: lb://CLOUD-PAYMENT-SERVICE
        predicates:
        - After=2020-07-08T12:44:53.402+08:00[Asia/Shanghai]

时间未到请求:

时间到了请求:

【注】获取时间串

ZonedDateTime zonedDateTime = ZonedDateTime.now();
System.out.println(zonedDateTime);

 

 Cookie Route Predicate:

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

YML:

spring:
  application:
    name: cloud-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
      routes:
      - id: payment_routh # 路由的ID
        uri: lb://CLOUD-PAYMENT-SERVICE
        predicates:
        - Cookie=name,zth

测试: 

所有 Route Predicate:

spring:
  application:
    name: cloud-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
      routes:
      - id: payment_routh # 路由的ID
        uri: lb://CLOUD-PAYMENT-SERVICE
        predicates:
        - Path=/payment/lb/**   
        #- After=2020-03-08T10:59:34.102+08:00[Asia/Shanghai]
        #- Cookie=name,zth 
        #- Header=X-Request-Id, \d+ # 请求头中要有 X-Request-Id 属性并且值为整数的正则表达式
        #- Host=**.zth.com
        #- Method=GET
        #- Query=username, \d+  # 要有参数名称并且是正整数才能路由

 

4. Filter

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

过滤器类型:

Spring Cloud Gateway 的 Filter 从作用范围可分为另外两种 GatewayFilter 与 GlobalFilter。
GatewayFilter :应用到单个路由或者一个分组的路由上。
GlobalFilter :应用到所有的路由上。

官网地址:https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1.RELEASE/reference/html/#gatewayfilter-factories


4.1 局部过滤器


 

示例:添加响应头

YML:

spring:
  application:
    name: cloud-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #开启从注册中心动态创建路由的功能,利用微服务名进行路由
      routes:
      - id: payment_routh # 路由的ID
        uri: lb://CLOUD-PAYMENT-SERVICE
        predicates:
        - Path=/**
        filters:
        - AddResponseHeader=X-Response-Red, Blue

测试:

 

4.1.1 实现限流

限流算法(传送门

pom:

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

配置类:

@Configuration
public class RequestRateLimiterConfig {
    @Bean
    @Primary
    KeyResolver apiKeyResolver() {
        //按URL限流,即以每秒内请求数按URL分组统计,超出限流的url请求都将返回429状态
        return exchange -> Mono.just(exchange.getRequest().getPath().toString());
    }

    @Bean
    KeyResolver userKeyResolver() {
        //按用户限流
        return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("user"));
    }

    @Bean
    KeyResolver ipKeyResolver() {
        //按IP来限流
        return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
    }
}

YML:

server:
  port: 9527
spring:
  redis:
    host: localhost
    port: 6379
    database: 0
    password: redis
  application:
    name: cloud-gateway
  cloud:
    gateway:
      routes:
      - id: payment_routh # 路由的ID
        uri: http://localhost:8001
        predicates:
        - Path=/**
        filters:
         - name: RequestRateLimiter
           args:
             # 令牌桶每秒填充平均速率,即行等价于允许用户每秒处理多少个请求平均数
             redis-rate-limiter.replenishRate: 1
             # 令牌桶的容量,允许在一秒钟内完成的最大请求数
             redis-rate-limiter.burstCapacity: 3
             # 用于限流的键的解析器的 Bean 对象的名字。它使用 SpEL 表达式根据#{@beanName}从 Spring 容器中获取
             key-resolver: "#{@ipKeyResolver}"

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

测试:

redis:

 

jemert 压测:

 

4.2 全局过滤器

全局过滤器(GlobalFilter)作用于所有路由,Spring Cloud Gateway 定义了Global Filter接口,用户可以自定义 Global Filter。通过全局过滤器可以实现对权限的统一校验,安全性验证等功能。

4.2.1 自定义 全局过滤器

@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 name = exchange.getRequest().getQueryParams().getFirst("name");
        if (null == name){
            log.info("***********用户名为空********");
            exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

测试:

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值