Gateway入门

1,095 阅读6分钟

一、Gateway简单介绍

在微服务中,通过使用Gateway网关能够统一管理对外公开的服务提供端口,起到服务提供内部和外部的隔离,保护服务提供方的安全性。通过使用Gateway网关,能够进行反向代理、鉴权、流量控制、熔断和日志监控等。

Gateway三大核心概念:

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

Gateway工作原理

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

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

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

二、基本使用(applicaiton.yml版)

1、添加依赖

<!--gateway-->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
    <version>5.2.4.RELEASE</version>
</dependency>
<!--eureka-client--> 
<dependency> 
    <groupId>org.springframework.cloud</groupId> 
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

注意: 如果加上以上依赖启动报如下错误:

An attempt was made to call a method that does not exist. The attempt was made from the following location:

    org.springframework.cloud.gateway.config.GatewayAutoConfiguration$NettyConfiguration.reactorNettyWebSocketClient(GatewayAutoConfiguration.java:748)

The following method did not exist:

    org.springframework.web.reactive.socket.client.ReactorNettyWebSocketClient.setHandlePing(Z)V

需要添加以下依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webflux</artifactId>
    <version>5.2.4.RELEASE</version>
</dependency>

另外, gateway的版本需要与SpringBoot版本相对应,可以添加SpringCloud版本来限制gateway的版本,SpringCloud版本与SpringBoot版本链接

image.png

添加SpringCloud依赖来限制gateway等组件版本

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.3.RELEASE</version>
    <relativePath/>
</parent>

<properties>
    <spring-cloud.version>Hoxton.SR7</spring-cloud.version>
</properties>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring-cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

2、添加配置

server:
  port: 9527

spring:
  application:
    name: cloud-gateway
  cloud:
      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

缺点:不利于应用于集群服务

集群服务配置(动态路由)

server:
  port: 9527

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

        - id: payment_routh2 #payment_route    #路由的ID,没有固定规则但要求唯一,建议配合服务名
          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

三、Predicate(断言)的使用

Predicate本质上就是路由匹配条件,通过匹配特定的Predicate条件,如果为true,则匹配到目标url。说白了,Predicate就是为了实现一组匹配规则,让请求过来找到对应的Route进行处理。

常用的Route Predicate:

  1. After Route Predicate
  2. Before Route Predicate
  3. Between Route Predicate
  4. Cookie Route Predicate
  5. Header Route Predicate
  6. Host Route Predicate
  7. Method Route Predicate
  8. Path Route Predicate
  9. Query Route Predicate

具体可查看官网文档

1、基于配置文件使用实例

spring:
  application:
    name: gateway-service
  cloud: 
    gateway: # gateway配置
      routes:
        - id: user_route
          uri: http://localhost:9021 # 转发地址
          predicates: # 断言配置
            - Path=/user/* # 路径前缀为user
        - id: order_route
          uri: http://localhost:9011
          predicates:
            - Path=/order/*
        - id: cookie_route
          uri: http://localhost:9011
          predicates:
            - name: Cookie # 请求中的Cookie携带key为userId,并且value满足正则表达式^[0-9]{1,10}
              args:
                name: userId
                regexp: ^[0-9]{1,10}
        - id: before_route
          uri: http://localhost:9011
          predicates:
            - Before=2024-03-23T14:47:00.983+08:00[Asia/Shanghai] # 在这个时间之前的请求都匹配,该时间值为ZonedDateTime类对象
            - Path=/time
        - id: after_route
          uri: http://localhost:9011
          predicates:
            - After=2024-03-23T14:48:00.983+08:00[Asia/Shanghai] # 在这个时间之后的请求都匹配
            - Path=/time
        - id: between_route
          uri: http://localhost:9021
          predicates:
            - Between=2024-03-23T14:47:00.983+08:00[Asia/Shanghai],2024-03-23T14:48:00.983+08:00[Asia/Shanghai] # 在这个时间段的请求都匹配
            - Path=/time
        - id: header_route
          uri: http://localhost:9011
          predicates:
            - Header=test,1000 # 请求携带请求头test,并且值为1000,如果值需要配置正则表达式,可以参考上面的Cookie断言配置

配置Before、After和Between断言的取值时,需要使用ZonedDateTime类,该类包括了日期时间时区,该类数据Java8时间API,如果需要获取时区列表可以使用ZoneId.getAvailableZoneIds()方法

ZonedDateTime now = ZonedDateTime.now();
String format = now.format(DateTimeFormatter.ISO_ZONED_DATE_TIME);

2、基于编码使用实例

import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class GateWayConfig {
    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder){
        RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
        routes.route("path_route_atguigu",
                r -> r.path("/guoji").uri("http://news.baidu.com/guonei")).build();
        return routes.build();
    }

}

四、Filter的使用

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

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

Filter种类有GatewayFilter(Gateway提供的) 、GlobalFilter(自定义的全局Filter)

1、内置路由过滤器使用实例

对地址为https://example.org的请求添加一个过滤器,该过滤器会给请求头添加两个请求头,分别是X-Request-redblue

spring:
  cloud:
    gateway:
      routes:
      - id: add_request_header_route
        uri: https://example.org
        filters:
        - AddRequestHeader=X-Request-red, blue

其它内置过滤器可查看官网文档

2、自定义过滤器

package com.hlt.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.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
@Component
public class MyLogGateWayFilter implements GlobalFilter, Ordered {
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String username = exchange.getRequest().getQueryParams().getFirst("username");
        if(username == null){
            exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }

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

五、扩展

1、路由配置获取源

可参考文档

在启动gateway程序时,路由配置信息会从多个配置源读取,并将每个路由配置信息配置成一个个RouteDefinition对象,这些数据源加载类顶层接口为RouteDefinitionLocator。其中读取application.yml和application.properties等配置文件是使用该接口实现类PropertiesRouteDefinitionLocator加载的。

RouteDefinitionLocator接口方法如下

public interface RouteDefinitionLocator {
    Flux<RouteDefinition> getRouteDefinitions();
}

RouteDefinitionLocator接口继承类如下 image.png