二Gateway的项目搭建与配置

Posted 竹峰的风

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了二Gateway的项目搭建与配置相关的知识,希望对你有一定的参考价值。

文章目录


Gateway的项目搭建与配置

工作流程

下图提供了 Spring Cloud Gateway 如何工作的高级概述:

核心概念:

客户端向 Spring Cloud Gateway 发出请求。如果Gateway Handler Mapping确定请求与路由匹配,则将其发送到Gateway Web Handler 处理程序。此处理程序通过特定于请求的Fliter链运行请求。Fliter被虚线分隔的原因是Fliter可以在发送代理请求之前(pre)和之后(post)运行逻辑。执行所有pre过滤器逻辑。然后发出代理请求。发出代理请求后,将运行“post”过滤器逻辑。

注意:在没有端口的路由中定义的uri对于HTTP和HTTPS uri分别获得默认端口值80和443。

过滤器作用:

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

核心点

  • Route(路由)

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

  • Predicate(断言)

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

  • Filter(过滤)

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

三个核心点连起来:

当用户发出请求到达Gateway,Gateway会通过一些匹配条件,定位到真正的服务节点,并在这个转发过程前后,进行一些及细化控制。其中Predicate就是我们匹配的条件,而Filter可以理解为一个拦截器,有了这两个点,再加上目标URI,就可以实现一个具体的路由了。

总结

Gateway核心的流程就是:路由转发与执行过滤器链

Gateway配置路由的两种方式

(1)通过YML配置

项目搭建

注意:该项目需配合Nacos一起使用

项目代码:https://download.csdn.net/download/m_lonel/86242475

  1. 新建一个Maven项目:spring-cloud-gateway,java版本选择8

  1. 删除项目的src目录, 并新建两个Module,分别为cloud-alibaba-gateway-9999和springcloudalibaba-nacos-9001

  1. 导入对应依赖,因为Gateway属于SpringCloud的,所以一定要注意版本关系:

    版本对应地址:https://spring.io/projects/spring-cloud

    父级项目spring-cloud-gateway的pom文件:

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.3.12.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.example</groupId>
        <artifactId>spring-cloud-gateway</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>spring-cloud-gateway</name>
        <packaging>pom</packaging>
        <description>Demo project for Spring Boot</description>
        <properties>
            <java.version>1.8</java.version>
            <spring-cloud-version>Hoxton.SR12</spring-cloud-version>
            <spring-cloud-alibaba-version>2.2.8.RELEASE</spring-cloud-alibaba-version>
        </properties>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
        <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>
    
                <dependency>
                    <groupId>com.alibaba.cloud</groupId>
                    <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                    <version>$spring-cloud-alibaba-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>
    
        <modules>
            <module>cloud-alibaba-gateway-9999</module>
            <module>springcloudalibaba-nacos-9001</module>
            <module>demo-9002</module>
        </modules>
    
    </project>
    

子级项目spring-cloud-gateway:

pom文件:

注意:引入Gateway一定要删除spring-boot-starter-web依赖,否则会有冲突无法启动

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.example</groupId>
        <artifactId>spring-cloud-gateway</artifactId>
        <version>0.0.1-SNAPSHOT</version>
<!--        <relativePath/> &lt;!&ndash; lookup parent from repository &ndash;&gt;-->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>cloud-alibaba-gateway-9999</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>cloud-alibaba-gateway-9999</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>

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

</project>

由于此项目也是配合Nacos使用,所以启动器要加上@EnableDiscoveryClient注解:

application.yml:

server:
  port: 9999
spring:
  application:
    name: cloud-gateway-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    gateway:
      discovery:
        locator:
          enabled: true #开启注册中心路由功能
      routes:  # 路由
        - id: nacos-provider #路由ID,没有固定要求,但是要保证唯一,建议配合服务名
          uri: http://localhost:9001/nacos-provider # 匹配提供服务的路由地址
          predicates: # 断言
            - Path=/demo/** # 断言,路径相匹配进行路由

注意:gateway.discovery.locator.enabled = true 是开启自动路由功能,根据服务名称自动创建routes,因为我们后面有配置routes,所以可以不要(不要的话默认为false)。routes中的uri其实最后是不需要服务名称的,这个位置其实只需要指定的localhost:9001即可

所以这个位置我们可以把当前的配置优化为以下配置,它是一样可以启动的(项目中为了更直观,还是采用了上面的配置):

server:
  port: 9999
spring:
  application:
    name: cloud-gateway-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
    gateway: #(去掉了gateway.discovery.locator.enabled配置)
      routes:  # 路由
        - id: nacos-provider #路由ID,没有固定要求,但是要保证唯一,建议配合服务名
          uri: http://localhost:9001 # 匹配提供服务的路由地址(去掉了/nacos-provider)
          predicates: # 断言
            - Path=/demo/** # 断言,路径相匹配进行路由

子级项目springcloudalibaba-nacos-9001:

pom文件:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.example</groupId>
        <artifactId>spring-cloud-gateway</artifactId>
        <version>0.0.1-SNAPSHOT</version>
<!--        <relativePath/> &lt;!&ndash; lookup parent from repository &ndash;&gt;-->
    </parent>
    <groupId>com.son</groupId>
    <artifactId>springcloudalibaba-nacos-9001</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>springcloudalibaba-nacos-9001</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
    </dependencies>

</project>

由于此项目也是配合Nacos使用,所以启动器要加上@EnableDiscoveryClient注解:

application.yml:

server:
  port: 9001
spring:
  application:
    name: nacos-provider
  cloud:
    nacos:
      discovery:
         server-addr: localhost:8848
management:
  endpoint:
    web:
      exposure:
        include: '*'

DemoController.java:

package com.son.springcloudalibabanacos9001.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/demo") //添加路由路径
public class DemoController 

    @Value("$server.port")
    private String serverPort;

    @GetMapping(value = "/hello")
    public String getServerPort()
        return "Hello! port: "+ serverPort;
    

测试

  1. 启动Nacos(Windows环境)

本地Nacos启动命令(standalone代表着单机模式运行,非集群模式):

startup.cmd -m standalone

  1. 启动9999网关和springcloudalibaba-nacos-9001服务,同时查看Nacos控制台,发现Nacos控制台成功注册Gateway网关

  1. 启动完成之后,访问接口:http://localhost:9999/demo/hello ,此时接口访问成功

(2)通过代码的方式进行配置

通过配置类进行配置,通过@Bean注入一个RouteLocator:

package com.example.cloudalibabagateway9999.config;

import org.springframework.cloud.gateway.route.RouteLocator;
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) 
        // 构建多个路由routes
        RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
        // 具体路由地址
        routes.route("path_demo",r -> r.path("/demo/**").uri("http://localhost:9001/nacos-provider")).build();
        // 返回所有路由规则
        return routes.build();
    


yml文件routes配置屏蔽,并将gateway.discovery.locator.enabled设置为false

我们在9001的DemoController中添加一个测试方法

@GetMapping(value = "/code")
public String codeTest()
    return "代码配置类网关配置测试";

启动9999网关和springcloudalibaba-nacos-9001服务,启动完成之后,访问接口:http://localhost:9999/demo/code ,此时接口就可以转发到9001中具体的接口中了

重学SpringCloud系列九微服务网关-GateWay

重学SpringCloud系列九微服务网关-GateWay


还有必要学习Zuul么?

一、什么是API网关

在开始讲解Spring Cloud GateWay之前呢,有必要说明一下什么是API网关。网关这个词,最早是出现在网络设备中,比如在彼此隔离的两个局域网中间的起到路由功能、隔离功能、安全验证功能的网络设备,通常被称为“网关”。

在软件开发方面,网关通常是用来隔离用户端和服务端的软件应用,通常被称为API网关。

所以使用API的好处是:

  • 面向前端开发人员更加友好,前端开发人员面向的入口减少,便于维护
  • 服务访问的认证鉴权更加方便,可以放在API网关统一去做。避免分散造成的开发及维护成本。
  • 访问日志、限流等公共服务也可以在网关上集中完成。避免分散造成的开发及维护成本。

说了API网关的这么多好处,那么有没有坏处呢?也是有的,而且很重要。

  • 当你使用了API网关之后,所有的请求都要多一次转发,造成一定程度上的响应时长的延长
  • 当你使用了API网关之后,意味着网关作为流量入口需要承担比微服务更多的流量负载。所以网关本身的架构性能及稳定性非常重要。

虽然我们可以在网关的前面再去加一层nginx或者haproxy等负载均衡器,但是仍旧很难改变网关在一定程度上的流量集中的问题。

所以,笔者在很多场合下呼吁不要滥用微服务网关。你要权衡一下你当前的架构是否真的需要一个网关。衡量性能、稳定性以及维护成本之间关系,去决定要不要使用服务网关。

二、聊一聊Zuul

正如笔者所说网关本身的架构性能及稳定性非常重要。然而性能就是Zuul的短板,因为它是基于servlet的阻塞IO模型开发的(下一节我会专门介绍Zuul和Spring Cloud GateWay IO模型的差异)。

  • Zuul 1.0在netflix官方已经进入了维护阶段,netflix对Spring Cloud社区的支持也已经基本属于“88了您哎”的状态
  • 虽然Netflix很早就宣称了要对zuul进行升级改造,也就是Zuul 2.0,但是目前与Spring Cloud的整合也处于难产状态
  • 基于以上的原因,Spring 社区自己开发了Spring Cloud gateWay。采用了spring 官方的响应式非阻塞框架webflux。官网测试结果性能是Zuul的1.6倍。

综上所述:笔者觉得目前Zuul已经没有任何学习的必要了。


简介与非阻塞异步IO模型

一、简介

Spring Cloud GateWay 是由Spring 官方社区开发的API 服务网关,在新一代的开发技术中使用到了Spring WebFlux的全新的响应式的非阻塞IO框架。相对于Spring Cloud第一代的网关组件zuul,性能有了长足的进步(宣称性能提升1.6倍)。WebFlux底层是基于高性能的非阻塞IO通信框架Netty实现的。

核心功能特性

笔者在上一节已经为大家介绍过,API服务网关的主要作用有三个:

  • 统一流量入口,面向前端更加友好。减少分散入口配置,降低客户端与服务端的耦合度。
  • 统一认证鉴权,避免多个服务分散鉴权造成的维护与开发的成本升高
  • 访问日志、限流、过滤、缓存、监控等公共服务也可以在网关上集中完成。避免分散造成的开发及维护成本

二、阻塞IO与异步非阻塞IO的区别

笔者用相对通俗的话为大家说明一下阻塞IO与非阻塞IO之间的区别。我们以软件开发团队的工作方式来做一个比喻。作为软件开发人员,我们肯定知道软件开发的基本流程:

  • 项目立项与可行性研究
  • 需求分析与设计
  • 代码开发
  • 迭代测试
  • 上线及配置管理、运维

在以Spring MVC或者struct为代表的框架都是基于sevlet的,其底层IO模型是阻塞IO模型。这种模型就好像你是公司的一个开发人员,上面的所有的5项工作全都由你一个人完成。如果公司有10个人,最多就只能同时进行10个需求。客户需求增多了也没有办法,只能让他们等着。如下图:一个请求占用一个线程,当线程池内的线程都被占用后新来的请求就只能等待。

spring 社区为了解决Spring MVC的阻塞模型在高并发场景下的性能瓶颈的问题,推出了Spring WebFlux,WebFlux底层实现是久经考验的netty非阻塞IO通信框架。该框架的请求处理与线程交互关系图如下:

boosGroup用于Accetpt连接建立事件并分发请求, workerGroup用于处理I/O读写事件和业务逻辑
每个Boss NioEventLoop循环执行的任务包含3步:

  • 1 轮询accept事件
  • 2 处理accept I/O事件,与Client建立连接,生成NioSocketChannel,并将NioSocketChannel注册到某个Worker NioEventLoop的Selector上
  • 3 处理任务队列中的任务,runAllTasks。任务队列中的任务包括用户调用eventloop.execute或schedule执行的任务,或者其它线程提交到该eventloop的任务。

每个Worker NioEventLoop循环执行的任务包含3步:

  • 1 轮询read、write事件;
  • 2 处I/O事件,即read、write事件,在NioSocketChannel可读、可写事件发生时进行处理
  • 3 处理任务队列中的任务,runAllTasks。

如果通俗的将上图中的各个任务池、线程池的组合比做一个软件开发公司,那么:

  • 项目立项及可研,由公司项目经理及顾问来完成
  • 需求分析与设计,由产品经理和架构师来完成
  • 代码研发,由项目经理带领开发人员来完成
  • 迭代测试,由测试团队来完成
  • 上线及配置管理、运维,可能由专门的devops团队来完成

这样一个公司内的所有人完成分工,就能在有限的资源的情况下,去接触更多的客户,谈更多的需求,合理的分配人力资源,达到并发处理能力最大化的极限水平。相比于一个员工从头到位的负责一个项目,它的组织性更强,分工更明确,合理的利用空闲资源,专业的人最专业的事。
这种人力资源的合理利用及组织方式和非阻塞IO模型有异曲同工之处,通过合理的将请求处理线程及任务进行分类,合理的利用系统的内存、CPU资源,达到单位时间内处理能力的最大化就是异步非阻塞IO的核心用意!
所以非阻塞IO模型的核心意义在于:提高了有限资源下的服务请求的并发处理能力,而不是缩短了单个服务请求的响应时长。 由于API 服务网关集中的承载了微服务系统内的流量进行转发,所以他的并发处理能力至关重要的原因,也是netflix Zuul被淘汰的根本原因!


GateWay概念与流程

一、Spring Cloud Gateway的处理流程

Spring Cloud的工作原理图如下:

  • 客户端向Spring Cloud Gateway发送请求,当请求的路径与网关定义的路由映射规则相匹配。该请求就会被发送到网关的Web Handler进行处理,执行特定的过滤器链。
  • 大家注意图中的虚线,虚线左侧表示在请求处理之前的执行逻辑(Pre过滤器),虚线右侧表示请求处理之后的执行逻辑(post过滤器)

二、核心概念

  • route(路由):路由实网关的基础元素,由id、目标url、predicate和filter组成。当请求通过网关的时候,由Gateway Handler Mapping通过predicate判断是否与路由匹配,当predicate=true的时候,匹配到对应的路由。
  • predicate(谓词逻辑):是java8中提供的一个函数,允许开发人员根据其定义规则匹配请求。比如根据请求头、请求参数来匹配路由。可以认为它就是一个匹配条件的定义。

国内有很多的人把这个翻译成“断言”,实际上这个词作为名词是“谓词”的意思,作为动词才是断言,官网上这个词是一个名词。

  • filter(过滤器):对请求处理之前之后进行一些统一的业务处理、比如:认证、审计、日志、访问时长统计等。

新建一个GateWay项目

一、Gateway网关搭建

引入gateway依赖

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

因为spring-cloud-starter-gateway包含spring-boot-starter-webflux,所以可以将项目中spring-boot-starter-webflux的maven坐标从pom文件中删除。

如果是新建的子模块module,做好父子项目的pom文件中的module配置关系。

项目的配置文件使用方法仍然和之前一致

server:
  port: 8777

spring:
  application:
    name: zimug-server-gateway
  cloud:
    gateway:
      routes:
        - id: dhy           # 路由 ID,唯一
          uri: http://baidu.com/   # 目标 URI,路由到微服务的地址
          predicates:              # 请求转发判断条件
            - Path=/baidu/**    # 匹配对应 URL 的请求,将匹配到的请求追加在目标 URI 之后
  • routes指的是配置路由转发规则,可以配置多个
  • 每一个route有一个id,标识该路由的唯一性
  • uri指的是请求转发的目标
  • predicates是请求转发的判断条件,我们的例子使用的Path条件判断

上面的路由配置的含义是当我们访问:http://<gateway-ip>:8777/baidu/的时候,请求被转发到http://baidu.com/baidu/,其中**匹配任意字符。

二、需要注意的点

我们搭建的Spring Cloud Gateway(dhy-server-gateway)项目虽然是一个web项目,但是底层已经使用的是spring-boot-starter-webflux,而不是spring-boot-starter-web。所以下面的这个坐标不要在引入gateway项目了,会报错!

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

并且之前在spring-boot-starter-web体系下做的代码集成工作,在spring-boot-starter-webflux基础上都会有所变化,涉及到我们再去讲。

三、路由转发测试

在上面的配置文件中,我们已经完成了一个简单的路由转发配置。含义是当我们访问:http://<gateway-ip>:8777/baidu/的时候,请求被转发到http://baidu.com/baidu/,其中**匹配任意字符。
下面我们就来做一下实验,我们把gateway项目在本机启动,然后浏览器访问如下网址。

http://localhost:8777/baidu/course

然后请求被转发至如下的网址:

http://baidu.com/baidu/course

通用Predicate的使用

一、Predicate路由判断条件介绍

Predicate 来源于 Java 8,是 Java 8 中引入的一个函数,Predicate 接受一个输入参数,返回一个布尔值结果。该接口包含多种默认方法来将 Predicate 组合成其他复杂的逻辑(比如:与,或,非)。

在 Spring Cloud Gateway 中 Spring 利用 Predicate 的特性实现了各种路由匹配规则,有通过 Header、请求参数等不同的条件来进行作为条件匹配到对应的路由。

说白了 Predicate 就是为了实现一组匹配规则,方便让请求过来找到对应的 Route 进行处理,接下来我们接下 Spring Cloud GateWay 内置几种 Predicate 的使用。

二、Predicate路由判断条件的使用方法

在之前的章节举例中,我们已经介绍了Path Predicate的匹配条件决定路由的转发规则。下面我们为大家介绍其他的多种 Predicate的匹配条件!

2.1.通过日期时间条件匹配

After Route Predicate Factory使用的是时间作为匹配规则,只要当前时间大于设定时间,路由才会匹配请求。以下After规则配置:在东8区的2020-05-17T16:31:47之后,所有请求都转发到dhy.com

spring:
  cloud:
    gateway:
      routes:
      - id: after_route
        uri: http://www.dhy.com
        predicates:
        - After=2020-05-17T16:31:47.789+08:00

Before Route Predicate Factory也是使用时间作为匹配规则,只要当前时间小于设定时间,路由才会匹配请求。以下Before规则配置:在东8区的2020-05-17T19:53:42之前,所有请求都转发到dhy.com

spring:
  cloud:
    gateway:
      routes:
      - id: before_route
        uri: http://www.dhy.com
        predicates:
        - Before=2020-05-17T19:53:42.789+08:00

Between Route Predicate Factory也是使用两个时间作为匹配规则,只要当前时间大于第一个设定时间,并小于第二个设定时间,路由才会匹配请求。以下Between规则配置:在东8区的2020-05-17T16:31:47之后,2020-05-17T19:53:42之前的时间段内,所有请求都转发到dhy.com

spring:
  cloud:
    gateway:
      routes:
      - id: between_route
        uri: http://www.dhy.com
        predicates:
        - Between=2020-05-17T16:31:47.789+08:00, 2020-05-17T19:53:42.789+08:00

2.2.通过 Cookie 匹配

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

spring:
  cloud:
    gateway:
      routes:
      - id: cookie_route
        uri: http://www.dhy.com
        predicates:
        - Cookie=cookiename, cookievalue

使用 curl 测试,命令行输入:curl http://localhost:8777 --cookie "cookiename=cookievalue",则会返回zimug.com页面代码。也就是说当我们的请求携带了指定的cookie键值对的时候,请求才向正确的uri地址转发。

2.3.通过 Header 属性匹配

Header Route Predicate 和 Cookie Route Predicate 一样,也是接收 2 个参数,一个 header 中属性名称和一个正则表达式value,这个属性值和正则表达式value匹配的时候才进行路由转发。

spring:
  cloud:
    gateway:
      routes:
      - id: header_route
        uri: http://www.dhy.com
        predicates:
        - Header=X-Request-Id, \\d+

上面的配置规则表示路由匹配存在名为X-Request-Id,内容为数字的header的请求,将请求转发到 dhy.com
使用 curl 测试,命令行输入:curl http://localhost:8777 -H "X-Request-Id:88",则返回页面代码证明匹配成功。将参数-H "X-Request-Id:88"改为-H "X-Request-Id:somestr"再次执行时返回404证明没有匹配。

2.4.通过 Host 匹配

Host Route Predicate 接收一组参数,一组匹配的域名列表,这个模板是一个 ant 风格的模板,用逗号作为分隔符。它通过参数中的主机地址作为匹配规则。

spring:
  cloud:
    gateway:
      routes:
      - id: host_route
        uri: http://www.dhy.com
        predicates:
        - Host=**.somehost.org,**.anotherhost.org

路由会匹配Http的Host诸如:www.somehost.orgbeta.somehost.orgwww.anotherhost.org的请求。

2.5.通过请求Method匹配

通过HTTP的method是 POST、GET、PUT、DELETE 等不同的请求方式来进行路由。

spring:
  cloud:
    gateway:
      routes:
      - id: method_route
        uri: http://www.dhy.com
        predicates:
        - Method=GET

以上规则决定:路由会匹配到所有GET方法的请求,其他的HTTP方法不做匹配。
使用 curl 测试,命令行输入:

  • curl 默认是以 GET 的方式去请求,curl http://localhost:8777,测试返回页面代码,证明匹配到路由predicate规则
  • 指定curl使用POST方法发送请求,curl -X POST http://localhost:8777,返回 404 没有找到,证明没有匹配

2.6.通过请求参数匹配

Query Route Predicate 支持传入两个参数,一个是属性名一个为属性值,属性值可以是正则表达式。

spring:
  cloud:
    gateway:
      routes:
      - id: query_route
        uri: http://www.dhy.com
        predicates:
        - Query=foo, ba.

路由会匹配所有包含foo,并且foo的内容为诸如:barbaz等符合ba.正则规则的请求。

使用 curl 测试,命令行输入:curl localhost:8777?foo=bax测试可以返回页面代码,将 foo的属性值改为 bazx再次访问就会报 404,证明路由需要匹配正则表达式才会进行路由。

2.7.通过请求 ip 地址进行匹配

Predicate 也支持通过设置某个 ip 区间号段的请求才会路由,RemoteAddr Route Predicate 接受 cidr 符号(IPv4 或 IPv6 )字符串的列表(最小大小为1),例如 192.168.0.1/16 (其中 192.168.0.1 是 IP 地址,16 是子网掩码)。

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

可以将此地址设置为本机的 ip (192.168.1.4)地址进行测试,curl localhost:8080,则此路由将匹配。

2.8.通过权重分流匹配

通过权重weight分流匹配的predicate有两个参数:groupweight(一个int)。权重是按组计算的。以下示例配置权重路由谓词:

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。

2.9.组合使用

spring:
  cloud:
    gateway:
      routes:
        - id: multi-predicate
          uri: https://www.dhy.com
          order: 0
          predicates:
            - Host=**.foo.org
            - Path=/headers
            - Method=GET
            - Header=X-Request-Id, \\d+
            - Query=foo, ba.
            - Query=baz
            - Cookie=chocolate, ch.p

各种 Predicates 同时存在于同一个路由时,请求必须同时满足所有的条件才被这个路由匹配。


自定义PredicateFactory

一、自定义Predicate Factory

虽然官方为我们提供了诸多的Predicate Factory(上一节介绍的),能够满足我们大部分的场景需求。但是不排除有些情况下,Predicate路由匹配条件比较复杂,这时就需要我们来自定义实现。

需求

本节我们通过自定义实现一个简单的需求,所有Path以"/sysuser"、"/sysorg"、"/sysrole"、"/sysmenu"、"/sysdict"、"/sysapi"开头的Http请求都转发到本机的http://localhost:8401/提供的aservice-rbac服务。

实现步骤

  • 自定义路由predicate工厂需要继承 AbstractRoutePredicateFactory 类,重写 apply 方法的逻辑。
  • apply 方法中可以通过 exchange.getRequest() 拿到 ServerHttpRequest 对象,从而可以获取到请求的参数、请求方式、请求头等信息。
@Component
public class RbacAuthRoutePredicateFactory
        extends AbstractRoutePredicateFactory<RbacAuthRoutePredicateFactory.Config> 


  public RbacAuthRoutePredicateFactory() 
    super(Config.class);
  

  @Override
  public Predicate<ServerWebExchange> apply(Config config) 
    return exchange -> 
      String requestURI = exchange.getRequest().getURI().getPath();
      if (config.getFlag().equals("rbac")
        &&(requestURI.startsWith("/sysuser")
          ||requestURI.startsWith("/sysorg")
          ||requestURI.startsWith("/sysrole")
          ||requestURI.startsWith("/sysmenu")
          ||requestURI.startsWith("/sysdict")
          ||requestURI.startsWith("/sysapi"))) 

        return true;  //表示匹配成功
      
      return false;   //表示匹配失败
    ;
  
  //自定义参数args配置类
  public static class Config 
    private String flag; //该参数对应配置文件的args

    public String getFlag() 
      return flag;
    

    public void setFlag(String flag) 
      this.flag = flag;
    
  

  • 类的命名需要以 RoutePredicateFactory 结尾,比如 RbacAuthRoutePredicateFactory,那么在配置文件中使用该predicate的时候 RbacAuth就是这个路由predicate工厂的名称。
  • 我们还可以为apply 方法传参数,如代码中的Config,flag字段和配置文件中的args字段名称是一一对应的。
spring:
  application:
    name: zimug-server-gateway
  cloud:
    gateway:
      routes:
        - id: rbsc-service
          uri: http://localhost:8401/
          predicates:
            - name: RbacAuth
              args:
                flag: rbac

二、测试一下

前提:启动本机aservice-rbac服务及与其相关的其他公共Spring Cloud组件
访问http://127.0.0.1:8777/sysuser/pwd/reset,请求正确的被gateway接收,并按照我们自定义的路由规则转发给本机的aservice-rbac服务。


编码方式构建静态路由

一、回顾一下配置文件的方式

server:
  port: 8777

spring:
  application:
    name: dhy-server-gateway
  cloud:
    gateway:
      routes:
        - id: dhy           # 路由 ID,唯一
          uri: http://baidu.com/   # 目标 URI,路由到微服务的地址
          predicates:              # 请求转发判断条件
            - Path=/baidu/**    # 匹配对应 URL 的请求,将匹配到的请求追加在目标 URI 之后
  • routes指的是配置路由转发规则,可以配置多个
  • 每一个route有一个id,标识该路由的唯一性
  • uri指的是请求转发的目标
  • predicates是请求转发的判断条件,我们的例子使用的Path条件判断

上面的路由配置的含义是当我们访问:http://<gateway-ip>:8777/baidu/**的时候,请求被转发到http://www.baidu.com/baidu/**,其中**匹配任意字符。

二、编码方式实现路由

下面的代码可以实现和配置文件实现方式一样的效果,所有的在配置文件中可以实现的predicates匹配规则,RouteLocatorBuilder 都有对应的api函数提供实现方法。

@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) 
   return builder.routes()
         .route("dhy", r -> r.path("/baidu/**")
         .uri("http://baidu.com"))
         .build();

route方法的第一个参数是路由id,第二个参数是一个Function(函数式编程),通过传入lambda表达式来判断匹配规则。

编码方式实现Predicate路由匹配规则,比配置文件的方式更繁琐一些,但是它也不是一无是处!配置文件方式的多个predicates组合只能表达“and并且”的关系,而编码方式还可以表达“or或者”的关系。 如下图所示:

但是笔者一般工作中很少使用编码方式实现路由的配置,因为编码代表着“写死”,也就是静态的。我们更希望配置是可以动态更新的,配置文件的方式结合nacos可以实现路由配置的动态更新,后面的章节我们再去介绍!


Filter过滤器介绍与使用

一、过滤器简介

微服务网关经常需要对请求进行一些过滤操作,比如:鉴权之后添加Header携带令牌等。在过滤器中可以

  • 为为请求增加请求头、增加请求参数 、增加响应头等等功能
  • 鉴权、记录审计日志、统计请求响应时长等共性服务操作

微服务系统中有很多的服务,我们不希望在每个服务上都去开发鉴权、记录审计日志、统计请求响应时长等共性服务操作。所以对于这样的重复开发或继承类工作,放在gateway上面统一去做是最好不过了。

二、Filter的生命周期

Spring Cloud Gateway 的 Filter 的生命周期很简单,只有两个:“pre” 和 “post”。

  • PRE: 这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。
  • POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的 HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。

三、Filter的分类

Spring Cloud Gateway 的 Filter 从作用范围可分为:

  • GatewayFilter:应用到单个路由或者一个分组的路由上。
  • GlobalFilter:应用到所有的路由上

笔者并不建议你去花很多的时间去学习下面的这些Filter都是如何使用,下面的这些Filter笔者几乎没有用到过。因为我已经介绍过了,Filter的作用就是在某些需求场景下去修改HTTP的请求头、路径、参数等等,只要你对HTTP协议足够的熟悉,所有的过滤器需求你都可以自定义实现,比起使用内置的Filter往往更加灵活。

3.1.Gateway filter

过滤器工厂作用参数
AddRequestHeader为原始请求添加HeaderHeader的名称及值
AddRequestParameter为原始请求添加请求参数参数名称及值
AddResponseHeader为原始响应添加HeaderHeader的名称及值
DedupeResponseHeader剔除响应头中重复的值需要去重的Header名称及去重策略
Hystrix为路由引入Hystrix的断路器保护HystrixCommand的名称
FallbackHeaders为fallbackUri的请求头中添加具体的异常信息Header的名称
PrefixPath为原始请求路径添加前缀前缀路径
PreserveHostHeader为请求添加一个preserveHostHeader=true的属性,路由过滤器会检查该属性以决定是否要发送原始的Host
RequestRateLimiter用于对请求限流,限流算法为令牌桶keyResolver、rateLimiter、statusCode、denyEmptyKey、emptyKeyStatus
RedirectTo将原始请求重定向到指定的URLhttp状态码及重定向的url
RemoveHopByHopHeadersFilter为原始请求删除IETF组织规定的一系列Header默认就会启用,可以通过配置指定仅删除哪些Header
RemoveRequestHeader为原始请求删除某个HeaderHeader名称
RemoveResponseHeader为原始响应删除某个HeaderHeader名称
RewritePath重写原始的请求路径原始路径正则表达式以及重写后路径的正则表达式
RewriteResponseHeader重写原始响应中的某个HeaderHeader名称,值的正则表达式,重写后的值
SaveSession在转发请求之前,强制执行WebSession::save操作
SecureHeaders为原始响应添加一系列起安全作用的响应头无,支持修改这些安全响应头的值
SetPath修改原始的请求路径修改后的路径
SetResponseHeader修改原始响应中某个Header的值Header名称,修改后的值
SetStatus修改原始响应的状态码HTTP 状态码,可以是数字,也可以是字符串
StripPrefix用于截断原始请求的路径使用数字表示要截断的路径的数量
Retry针对不同的响应进行重试retries、statuses、methods、series
RequestSize设置允许接收最大请求包的大小。如果请求包大小超过设置的值,则返回 413 Payload Too Large请求包大小,单位为字节,默认值为5M
ModifyRequestBody在转发请求之前修改原始请求体内容修改后的请求体内容
ModifyResponseBody修改原始响应体的内容修改后的响应体内容
Default为所有路由添加过滤器过滤器工厂名称及值

每个过滤器工厂都对应一个实现类,并且这些类的名称必须以GatewayFilterFactory结尾,这是Spring Cloud Gateway的一个约定,例如AddRequestHeader对应的实现类为AddRequestHeaderGatewayFilterFactory。对源码感兴趣的小伙伴就可以按照这个规律拼接出具体的类名,以此查找这些内置过滤器工厂的实现代码

Filter并不如Predicate那么常用,更多的时候我们需要自定义Filter,所以官方内置的Filter我们就不一一介绍了。我们选两个例子来说明一下:

  • AddRequestHeader GatewayFilter Factory
spring:
  cloud:
    gateway:
      routes:
      - id: add_request_header_route
        uri: http://httpbin.org:80/get
        filters:
        - AddRequestHeader=X-Request-Foo, Bar
        predicates:
        - Method=GET

过滤器工厂会在匹配的HTTP的请求加上一个Header,名称为X-Request-Foo,值为Bar。

  • RewritePath GatewayFilter Factory

在Nginx服务启中有一个非常强大的功能就是重写路径,Spring Cloud Gateway默认也提供了这样的功能。

spring:
  cloud:
    gateway:
      routes:
      - id: rewritepath_route
        uri: http://httpbin.org
        predicates:
        - Path=/foo/**
        filters:
        - RewritePath=/foo/(Java之 Spring Cloud 微服务搭建网关SpringCloud Gateway微服务网关GateWay(第三个阶段)SpringBoot项目实现商品服务器端是调用

Java之 Spring Cloud 微服务搭建网关SpringCloud Gateway微服务网关GateWay(第三个阶段)SpringBoot项目实现商品服务器端是调用

springcloud~gateway网关

SpringCloud 学习笔记总结

SpringCloud 学习笔记总结

Golang Gateway API 搭建教程