gateway

Spring Cloud Gateway是 Spring官方基于 Spring 5.0、Spring Boot 2.0 和 Project Reactor等技术开发的网关,Spring Cloud Gateway 旨在为微服务架构提供一种简单有效的、统一的 API 路由管理方式。Spring Cloud Gateway作为 Spring Cloud生态系中的网关,其目标是替代 Netflix公司的Zuul(网关),它不仅提供统一的路由方式,并且基于Filter 链的方式提供了网关基本的功能,例如:安全、监控/埋点和限流等。

Spring Cloud Gateway依赖 Spring Boot 和 Spring WebFlux,基于Netty运行。它不能在传统的 servlet 容器中工作,也不能构建成 war 包。

学习重点:英文+官方网站+理论+HTTP协议+正则表达式

官网参考文档:https://docs.spring.io/spring-cloud-gateway/docs/3.1.4/reference/html/#gateway-starter

核心概念

glossary(术语汇编)

在 Spring Cloud Gateway 中有如下几个核心概念需要我们了解∶

Ø Route(路由)

Route 是网关的基础元素,由 ID、目标URI、断言、过滤器组成。当请求到达网关时由 Gateway Handler Mapping 通过断言进行路由匹配(Mapping),当断言为真时,匹配到路由。

Ø Predicate(谓词)

Predicate(断言)又称谓词,用于条件判断,只有断言结果都为真,才会真正的执行路由。断言其本质就是定义路由转发的条件。

谓词是一种匹配,可以匹配HTTP请求中的所有内容,例如请求头或参数,如果谓词为true,则匹配路由,否则不匹配;

谓词函数:就是返回真假的函数。英语中的主谓宾,谓(am/is/are)

Ø Filter(过滤器)

Filter 是 Gateway 中的过滤器,可以在请求发出前后进行一些业务上的处理,比如修改请求和响应

工作原理

Spring Cloud Gateway 的工作理跟Zuul的差不多,最大的区别就是 Gateway的Filter 只有pre 和 post 两种。下面我们简单了解一下 Gateway 的工作原理。

客户端向 Spring Cloud Gateway 发出请求,如果请求与网关程序定义的路由匹配,则该请求就会被发送到网关 Web 处理程序,此时处理程序运行特定的请求过滤器链。

过滤器之间用虚线分开的原因是过滤器可能会在发送代理请求的前后执行逻辑。所有 pre 过滤器逻辑先执行,然后执行代理请求;代理请求完成后,执行post过滤器逻辑。

快速入门

1、创建两个springboot项目,gateway添加依赖

<!--添加spring-boot-starter-parent 作为parent-->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.6.13</version>
</parent>

<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
   <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
    <springcloud.version>2021.0.4</spring-cloud.version>
</properties>

<dependencies>
    <!-- gateway 依赖 -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>
</dependencies>

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

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

2、创建启动类,另一个controller类

@RestController
@Slf4j
public class TestController {
    @GetMapping("/red/test")
    public String redTest(){
        return "red method";
    }

    @GetMapping("/blue/test")
    public String blueTest(){
        return "blue method";
    }

    @GetMapping("/hello")
    public String hello(){
        return "hello !!!!";
    }
}

3、配置路由

application.yml中添加网关路由配置信息,如下

server:
  port: 8888

spring:
  application:
    name: gateway-program

  cloud:
    gateway:
      routes:
        - id: path_route
          uri: http://localhost:8080
          predicates:
            - Path=/red/{segment},/blue/{segment},/hello # 大括号代表一级路径

注意: Path 不要写成小写的path,否则会报错。

当访问http://localhost:8888/red/test或者http://localhost:8888/blue/test的时候就会转发到http://localhost:8080/red/test和http://localhost:8080/blue/test

访问http://localhost:8888/hello请求就会经过路由转发到http://localhost:8080/hello

如果我们要支持多级Path,在后面加上两个**号即可,比如

 - id: path_route
   uri: http://www.thomas.com
   predicates:
     - Path=/red/**

上面即可支持多级path,比如访问http://localhost:8888/red/book/1就会转发到

http://www.thomas.com/red/book/1

整合注册中心

整合Eureka

1、添加依赖

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

2、配置application.yml,把网关配置到注册中心

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka # 注册到注册中心

3、配置路由

spring:
  application:
    name: gateway-program
  cloud:
    gateway:
      routes:
        - id: goods-route
          uri: lb://goods-providers
          predicates:
            - Path=/goodsapp/**
          #StripPrefix=1 就代表截取路径的个数为1,这样配置后当请求/goodsapp/goods/5后端匹配到的请求路径,为 http://localhost:8888/goods/5
          filters: StripPrefix=1
server:
  port: 8888

uri以lb://开发(lb代表从注册中心获取服务),后面接的就是你需要转发到的服务名称,这个服务名称必须跟Eureka中的对应,否则会找不到服务。

4、整合Eureka的默认路由

我们在访问路径上指定要访问的服务名即可,通过这种方式就不用为每个服务都去配置转发规则,当新加了服务的时候,不用去配置路由规则和重启网关,Gateway会为所有服务都进行转发操作,通过配置即可开启,配置如下:

spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true

开启之后我们就可以通过地址去访问服务了,格式如下:

http://网关地址:网关的端口/服务名称(大写)/**
http://localhost:8888/GOODS-PROVIDER/goods/3

这个大写的名称还是有很大的影响,再增加一个配置即可使用小写服务名:

spring:
  cloud:
    gateway:
      discovery:
        locator:
          lower-case-service-id: true

注意:开启小写服务名称后大写的服务名称就不能使用,两者只能选其一

整合Nacos

1、添加依赖

<!--使用nacos作为注册中心-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba.nacos</groupId>
    <artifactId>nacos-client</artifactId>
    <version>2.1.1</version>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>

注意:添加loadbalancer依赖,因为网关要使用负载均衡功能

2、 将网关注册到注册中心

spring:
  cloud:
    #将网关注册到注册中心nacos
    nacos:
      discovery:
        namespace: 8b15a8a7-8237-4ff6-a28e-ac949e399a1f
        group: THOMAS_GROUP
        server-addr: 127.0.0.1:8848

3、配置路由

uri以lb://开发(lb代表从注册中心获取服务),后面接的就是你需要转发到的服务名称,这个服务名称必须跟Nacos中的对应,否则会找不到服务。

spring:
  cloud:
    #配置网关的路由
    gateway:
      routes:
        - id: goods_route
          uri: "lb://goods-provider"
          predicates:
            - Path=/**
        - id: order_route
          uri: "lb://order-consumer"
          predicates:
            - Path=/**

4、整合Nacos的默认路由

我们在访问路径上指定要访问的服务即可,通过这种方式就不用为每个服务都去配置转发规则,当新加了服务的时候,不用去配置路由规则和重启网关,Gateway会为所有服务都进行转发操作,通过配置即可开启,配置如下:

spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true

开启之后我们就可以通过地址去访问服务了,格式如下:

http://网关地址/服务名称(大写)/**
http://localhost:8888/GOODS-PROVIDER/goods/3

增加一个配置使用小写服务名即可:

spring:
  cloud:
    gateway:
      discovery:
        locator:
          lower-case-service-id: true

注意:开启小写服务名称后大写的服务名称就不能使用,两者只能选其一。

路由断言工厂

Spring Cloud Gateway内置了许多路由断言工厂,可以通过配置的方式直接使用,也可以组合使用多个路由断言工厂。下面介绍几个常用的路由断言工厂类。可以参照官方文档

Path路由断言工厂

Path路由断言工厂接收一个参数,根据Path定义好的规则来判断访问的URI是否匹配。

如果请求路径/red/1 or /red/1/ or /red/blue or /blue/green. 则路由可以匹配

参考: - Path=/demo/product/{id},/demo/red/{segment},/demo/blue/{segment},/demo/hello

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

Query路由断言工厂

Query路由断言工厂接收两个参数,一个必须的参数和一个可选的正则表达式。

如果请求包含一个请求参数,参数名为red,参数值为gree开头的五个字符的单词,则此路由将匹配。

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

Method路由断言工厂

Method路由断言工厂接收一个参数,即要匹配HTTP方法。

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

Header路由断言工厂

Header路由断言工厂接收两个参数,分别是请求头名称和正则表达式。

如果请求中带有请求头X-Request-Id,其值与\d+正则表达式匹配(值为1个或者多个数字),则此路由匹配。

注意:此处可以使用postman测试

spring:
  cloud:
    gateway:
      routes:
      - id: header_route
        uri: https://example.org
        predicates:
        - Header=X-Request-Id, \d+

过滤器工厂

路由过滤器允许以某种方式修改传入的HTTP请求或输出的HTTP响应,可以查看官网学习

AddRequestHeader

通过名称我们可以理解这个过滤器工厂的作用是添加请求头

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

RemoveRequestHeader

通过名称我们可以理解这个过滤器工厂的作用是移除请求头,可以在请求转发到后端服务之前进行Header的移除操作

spring:
  cloud:
    gateway:
      routes:
      - id: removerequestheader_route
        uri: https://example.org
        filters:
        - RemoveRequestHeader=X-Request-Foo

SetResponseHeader

通过名称我们可以理解这个过滤器工厂的作用是设置响应头

spring:
  cloud:
    gateway:
      routes:
      - id: setresponseheader_route
        uri: https://example.org
        filters:
        - SetResponseHeader=X-Response-Red, Blue

还有很多可以参考官网学习

自定义过滤器

Spring Cloud Gateway 中的 Filter分为两种类型,分别是Gateway Filter和Global Filter。过滤器将会对请求和响应进行处理.。比如添加参数、URL重写等。

GatewayFilter是一个接口,其有很多实现类,这是 Spring Cloud Gateway已经提供了的,使用的时候只需要在yml中配置即可。

自定义全局过滤器

自定义全局过滤器需要实现以下两个接口:GlobalFilter,ordered。通过全局过滤器可以实现权限校验,安全性验证等功能。

@Component
@Slf4j
public class RequestLogGlobalFilter implements GlobalFilter, Ordered {

    /**
     * @param exchange 网络交换机
     * @param chain    过滤器链
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 获取请求路径
        URI uri = exchange.getRequest().getURI();
        log.info("获取到请求路径:{}", uri.toString());
        log.info("error");
        return chain.filter(exchange);
    }

    /**
     * 设置过滤器执行顺序,数值越低,优先级越高
     *
     * @return
     */
    @Override
    public int getOrder() {
        return -50;
    }
}

加入跨域请求

同源策略

协议+域名+端口如果一模一样就是同源的

http://www.a.com/list.html

-------------------------------------------
https://www.a.com/detail.html 不同源
http://www.b.com/detai.html  不同源
http://www.a.com:8080/detai.html  不同源

http://www.a.com/detail.html 同源

为了使gateway能够支持前端访问,需要加入支持前端访问的跨域请求。

参考代码如下:

@Configuration
public class CorsConfig {
    @Bean
    public CorsWebFilter corsFilter() {
        CorsConfiguration config = new CorsConfiguration();
        config.addAllowedMethod("*");
        config.addAllowedOrigin("*");
        config.addAllowedHeader("*");

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
        source.registerCorsConfiguration("/**", config);

        return new CorsWebFilter(source);
    }
}

注意:跨域请求允许的范围太宽了,不安全,生产时把范围改小

方式二:通过配置文件配置

spring:
  cloud:
    gateway:
      globalcors: # 全局跨域请求
        cors-configurations: #跨域请求配置
          '[/**]':
            allowedOrigins: "http://localhost:8080" # 最好写前端的地址
            allowedMethods:
              - GET   # 放开多个方法
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐