百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分类 > 正文

springcloud实践二:gateway网关详解

ztj100 2024-10-29 18:18 34 浏览 0 评论

微服务框架当前大行其道,网关在微服务架构中是一个非常重要的部分,网关一般作为项目的统一请求入口提供给前端开发人员,前端开发人员不用知道每个微服务的请求地址。网关可以统一对所有请求做过滤、限流、负载均衡、监控等处理,而不必在每个微服务项目重复处理请求。网关配合注册中心也可以很好的实现微服务的动态扩容,只需要在网关将请求路由转发到注册中心的微服务上即可,由注册中心进行负载均衡处理。

Spring Cloud Gateway旨在提供一种简单而有效的方法来路由到api,并为它们提供额外功能,例如:安全性、监控和弹性控制。

Spring Cloud Gateway基于Spring Boot 2.x, Spring WebFlux, 由Spring Webflux提供Netty的运行环境。它不能在传统Servlet容器中工作,也不能在构建为WAR时工作。

Spring Cloud Gateway底层通信框架基于Netty,不同于第一代网关Zuul,gateway是非阻塞的,并且是spring官方开发的,所以拥有强劲的性能。

下面介绍下Spring Cloud Gateway的实际使用。

spring-boot和spring-cloud版本

  • spring-cloud Hoxton.SR8
  • spring-boot 2.3.3.RELEASE
  • spring-cloud-starter-gateway 2.2.5.RELEASE(不需要在pom指定,跟随spring cloud即可)

下面我会演示下实际的项目代码以及效果,我想尽量把需要的代码都贴出来,所以会写的比较细,如果已经掌握的部分可以跳着看即可。

我们现在新建了一个简单的父项目和网关子项目

项目结构

其中父项目只有一个pom文件,引入了一个公共工具依赖,定义了spring boot和spring cloud的版本,子项目中就不需要定义spring子项目的版本了。

父项目pom.xml文件

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.huangtl</groupId>
    <artifactId>springcloud-parent</artifactId>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>gateway</module>
    </modules>
    <packaging>pom</packaging>

    <properties>
        <java.version>1.8</java.version>
    </properties>

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

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR8</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.1.3</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

    <!--因为有些依赖仅在Spring Milestones中提供,所以要配置这个仓库地址-->
    <repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>
</project>

gateway项目

1.引入spring-cloud-starter-gateway依赖

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>springcloud-parent</artifactId>
        <groupId>com.huangtl</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>gateway</artifactId>

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

注意,spring-cloud-gateway是不支持spring-boot-starter-web依赖的,一般我们会把网关单独建一个项目。

2.配置文件

在resources目录下新建application.yml,我们将在这里配置大部分的gateway配置项,在配置这些配置项之前,我们先了解几个gateway配置项的概念。

  • Route 路由配置项,包含一个id(唯一标志)、uri(请求转发到该uri)、predicates(请求的匹配条件)、filters(过滤请求)
  • Predicate 请求的匹配条件,可以配置请求路径、请求头或参数、请求时间等的匹配条件,若符合条件则匹配路由
  • Filter 可以修改请求和响应数据,比如修改请求路径、修改请求头、删除请求参数、修改响应头、设置断路器等等

predicate的配置项有快捷和完整两种写法:

快捷写法格式:- 表达式名称 = 参数(多参数可逗号隔开)

spring:
  cloud:
    gateway:
      routes:
      - id: after_route
        uri: https://example.org
        predicates:
        - Cookie=mycookie,mycookievalue

完整写法用 - name 加上 args参数来配置,上面快捷写法对应的完整写法如下:

spring:
  cloud:
    gateway:
      routes:
      - id: after_route
        uri: https://example.org
        predicates:
        - name: Cookie
          args:
            name: mycookie
            regexp: mycookievalue

我们接下去的测试都会用快捷的写法。

3.新建一个springboot启动类

package com.huangtl.gateway;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

网关项目基本结构完成。

下游测试项目

1.我们新建一个项目用于网关的下游项目,即网关会将匹配的请求转发到该项目。我们新建一个user-service的服务项目。

我们在user-service项目pom.xml文件只引入一个springboot web的依赖,用于访问接口即可。

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

2.配置文件

在resources目录下新建application.yml,简单配置下端口

server:
  port: 8092

3.新建一个启动类

@SpringBootApplication
public class UserServiceApp {

    public static void main(String[] args) {
        SpringApplication.run(UserServiceApp.class, args);
    }
}

整体项目结构

下面开始测试gateway的各种配置及效果

为了方便测试我们在用户服务下游项目中建立两个接口类

PrefixController.java (带“/user-api”前缀)

@RestController
@RequestMapping("/user-api")
public class PrefixController {

    @RequestMapping("/")
    public String hello(){
        return "这是用户/user-api服务";
    }

    @RequestMapping("/a")
    public String a(){
        return "这是用户/user-api/a服务";
    }

    @RequestMapping("/b")
    public String b(){
        return "这是用户/user-api/b服务";
    }

    @RequestMapping("/get/a")
    public String geta(){
        return "这是用户/user-api/get/a服务";
    }

    @RequestMapping("/delete/a")
    public String deletea(){
        return "这是用户/user-api/delete/a服务";
    }
}

RootController.java (根目录)

@RestController
@RequestMapping("/")
public class RootController {

    @RequestMapping("/")
    public String hello(){
        return "这是用户/服务";
    }

    @RequestMapping("/a")
    public String a(){
        return "这是用户/a服务";
    }

    @RequestMapping("/b")
    public String b(){
        return "这是用户/b服务";
    }

    @RequestMapping("/get/a")
    public String geta(){
        return "这是用户/get/a服务";
    }

    @RequestMapping("/delete/a")
    public String deletea(){
        return "这是用户/delete/a服务";
    }
}

用户服务项目完成了,启动用户项目,我们开始在网关项目测试配置请求匹配表达式和过滤器,我就不贴网关项目的端口8091配置了,贴主gateway配置相关的代码。

predicates请求匹配表达式配置示例

  • 表达式一:Path
spring:
  cloud:
    gateway:
      routes:
      - id: user_route
        uri: http://localhost:8092 #具体地址方式
        predicates:
        - Path=/user-api/**

效果:匹配请求为/user-api/开头的请求,并转发到http://localhost:8092/user-api/**

可以逗号隔开匹配多个请求

- Path=/user-api/get/**,/user-api/delete/**
  • 表达式二:Method
spring:
  cloud:
    gateway:
      routes:
      - id: user_route
        uri: http://localhost:8092 #具体地址方式
        predicates:
        - Method=GET,POST #匹配请求方法

匹配请求的http方法为get和post的请求


  • 表达式三:Host
spring:
  cloud:
    gateway:
      routes:
      - id: user_route
        uri: http://localhost:8092 #具体地址方式
        predicates:
        - Host=**.somehost.org,**.anotherhost.org 

匹配请求的主机头白名单,多个逗号隔开


  • 表达式四:Header
spring:
  cloud:
    gateway:
      routes:
      - id: user_route
        uri: http://localhost:8092 #具体地址方式
        predicates:
        - Header=X-Request-Id, \d+ 

请求头部匹配,需要两个参数逗号隔开,后一个参数为正则表达式

  • 表达式五:Cookie
spring:
  cloud:
    gateway:
      routes:
      - id: user_route
        uri: http://localhost:8092 #具体地址方式
        predicates:
        - Cookie=mycookie,mycookievalue

匹配cookie名称为mycookie且值为mycookievalue的请求,后一个参数为正则表达式

  • 表达式六:Before
spring:
  cloud:
    gateway:
      routes:
      - id: user_route
        uri: http://localhost:8092 
        predicates:
        - Before=2017-01-20T17:42:47.789-07:00[Asia/Shanghai]

匹配2017年1月20日之前发出的请求,很适合做如临时的维护接口

  • 表达式七:After
spring:
  cloud:
    gateway:
      routes:
      - id: user_route
        uri: http://localhost:8092 
        predicates:
        - After=2017-01-20T17:42:47.789-07:00[Asia/Shanghai]

匹配2017年1月20日以后发出的请求

  • 表达式八:Between
spring:
  cloud:
    gateway:
      routes:
      - id: user_route
        uri: http://localhost:8092 #具体地址方式
        predicates:
        - Between=2017-01-20T17:42:47.789-07:00[Asia/Shanghai], 2017-01-21T17:42:47.789-07:00[Asia/Shanghai]

此路由匹配在2017年1月20日17点42分之后和2017年1月21日17点42分之前发出的请求。这对于维护窗口非常有用。

  • 表达式九:Query
spring:
  cloud:
    gateway:
      routes:
      - id: user_route
        uri: http://localhost:8092 #具体地址方式
        predicates:
        - Query=name

匹配查询参数包含name的请求如 http://localhost:8091/user-api/delete/a?name=11 。

也可以设置同时匹配查询参数和参数值,如下配置意味着匹配查询参数为name,值为zhangsan的请求

- Query=name, zhangsan
  • 表达式十:RemoteAddr
spring:
  cloud:
    gateway:
      routes:
      - id: user_route
        uri: http://localhost:8092 #具体地址方式
        predicates:
        - RemoteAddr=192.168.1.1/24

请求ip匹配,ip/子网掩码

  • 表达式十一:自定义条件

我们模仿Path条件来自定义一个表达式

spring:
  cloud:
    gateway:
      routes:
      - id: user_route
        uri: http://localhost:8092 #具体地址方式
        predicates:
        - My=/user-api/get/a

注意我们自定义表达式命名为“My”,我们需要新建一个类名必须为MyRoutePredicateFactory的条件表达式工厂类,需继承AbstractRoutePredicateFactory抽象类,重写apply方法。

package com.huangtl.gateway.factory;

import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

/**
* 自定义路由匹配表达式
* @Description 类名以前缀+RoutePredicateFactory的方式,比如MyRoutePredicateFactory,配置里
* @Author huangtl
**/
@Component
public class MyRoutePredicateFactory extends AbstractRoutePredicateFactory<MyRoutePredicateFactory.Config> {

    public MyRoutePredicateFactory() {
        super(Config.class);
    }

    /**
     * 可以参考官方自带的一些表达式{@link org.springframework.cloud.gateway.handler.predicate}
     * @param config
     * @return
     */
    @Override
    public Predicate<ServerWebExchange> apply(Config config) {
        // grab configuration from Config object
        return exchange -> {
            //根据exchange的一些信息判断是否通过
            ServerHttpRequest request = exchange.getRequest();
            //判断请求参数是否和配置的信息匹配
            if(request.getPath().value().equals(config.getCustomPath())){
                return true;
            }
            return false;
        };
    }

    //需要加此方法apply方法的config才能获取到配置的值,官方文档示例坑爹
    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("customPath");
    }

    public static class Config {
        //Put the configuration properties for your filter here
        private String customPath;
      
        public String getCustomPath() {
            return customPath;
        }
        public void setCustomPath(String customPath) {
            this.customPath = customPath;
        }

        @Override
        public String toString() {
            return customPath;
        }
    }
}

注意继承AbstractRoutePredicateFactory类时的泛型指定了本类的Config配置。Config类定义了一个customPath属性,对应配置文件中的My对应的值

- My=/user-api/get/a

在apply重写方法中可以通过config.getCustomPath()取到该配置值,然后根据请求地址和该值做匹配判断即可。注意需要重写shortcutFieldOrder方法把customPath参数注册进去才可以。

filters过滤器配置示例

  • 过滤器一:PrefixPath
spring:
  cloud:
    gateway:
      routes:
      - id: user_route
        uri: http://localhost:8092 #具体地址方式
        predicates:
        - Path=/get/**
        filters: 
        - PrefixPath=/user-api

会在请求地址前面加上PrefixPath设置的值,如 /get/** 会转发到下游项目的接口/user-api/get/** 。

可以看到我们请求http://localhost:8091/get/a,并没有转发到下游的/get/a接口,而是请求到了/user-api/get/a接口。

  • 过滤器二:RewritePath
spring:
  cloud:
    gateway:
      routes:
      - id: user_route
        uri: http://localhost:8092 
        predicates:
        - Path=/user-api/**
        filters: 
        - RewritePath=/user-api, /

两个参数逗号隔开,请求路径上第一个参数字符串替换为第二个参数字符串,如上配置的效果:将 /user-api/get/a 变成 /get/a

  • 过滤器三:StripPrefix
spring:
  cloud:
    gateway:
      routes:
      - id: user_route
        uri: http://localhost:8092
        predicates:
        - Path=/user-api/**
        filters:
        - StripPrefix=1

清除掉路径前缀数量:

值为1效果:/user-api/get/a ==> /get/a

值为2效果:/user-api/get/a ==> /a

  • 过滤器四:SetPath
spring:
  cloud:
    gateway:
      routes:
      - id: user_route
        uri: http://localhost:8092
        predicates:
        - Path=/user-api/**
        filters:
        - SetPath=/a

重新设置路径,效果:/user-api/a 变成 /a

可以看到,匹配的请求路径都被转发到下游项目的 /a 接口去了

  • 过滤器五:CircuitBreaker
spring:
  cloud:
    gateway:
      routes:
      - id: user_route
        uri: http://localhost:8092
        predicates:
        - Path=/user-api/**
        filters:
        - name: CircuitBreaker
          args:
            name: myCircuitBreaker
            fallbackUri: forward:/fallback2

断路器配置,这里用的是完整写法,当请求下游项目发生异常或无法请求时,会改成请求fallbackUri配置的地址代替,上例中会请求到网关项目的/fallback2接口。

@RequestMapping("/fallback2")
public String fallback2(){
    return "这是fallback2服务";
}

注意- name对应的CircuitBreaker不能更改,需要添加spring-cloud-starter-circuitbreaker-reactor-resilience4j依赖。

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-circuitbreaker-reactor-resilience4j</artifactId>
</dependency>

也有Hystrix断路器的用法,需要添加spring-cloud-starter-netflix-hystrix依赖,因为Netflix已经将Hystrix置于仅维护模式,spring官方建议使用Resilience4J。

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

Hystrix断路器配置

- name: Hystrix
  args:
    name: fallbackcmd
    fallbackUri: forward:/fallback2

测试效果,我们不启动user-service下游服务项目,请求网关时会请求到断路器配置接口。


  • 过滤器六:AddRequestHeader
spring:
  cloud:
    gateway:
      routes:
      - id: user_route
        uri: http://localhost:8092
        predicates:
        - Path=/user-api/**
        filters:
        - AddRequestHeader=X-Request-name, huangtl

添加请求头,逗号隔开,第一个值为请求头名称,第二个值为请求头的值。

  • 过滤器七:AddRequestParameter
spring:
  cloud:
    gateway:
      routes:
      - id: user_route
        uri: http://localhost:8092
        predicates:
        - Path=/user-api/**
        filters:
        - AddRequestParameter=name, huangtl

添加请求参数,逗号隔开,第一个值为请求参数名称,第二个值为请求参数的值。

  • 过滤器八:AddResponseHeader
spring:
  cloud:
    gateway:
      routes:
      - id: user_route
        uri: http://localhost:8092
        predicates:
        - Path=/user-api/**
        filters:
        - AddResponseHeader=X-Response-name, huangtl

添加响应头,逗号隔开,第一个值为响应头名称,第二个值为响应头的值。

  • 过滤器九:MapRequestHeader
spring:
  cloud:
    gateway:
      routes:
      - id: user_route
        uri: http://localhost:8092
        predicates:
        - Path=/user-api/**
        filters:
        - MapRequestHeader=host, host1

修改请求头,上例中将host请求头替换成host1请求头重新生成内容

  • 过滤器十:RemoveRequestHeader
spring:
  cloud:
    gateway:
      routes:
      - id: user_route
        uri: http://localhost:8092
        predicates:
        - Path=/user-api/**
        filters:
        - RemoveRequestHeader=x-forwarded-for

删除请求头,在转发到下游项目前删除设置的请求头。


  • 过滤器十一:RemoveResponseHeader
spring:
  cloud:
    gateway:
      routes:
      - id: user_route
        uri: http://localhost:8092
        predicates:
        - Path=/user-api/**
        filters:
        - RemoveResponseHeader=Content-Length

删除响应头,下游项目响应回来后删除设置的响应头。

  • 过滤器十二:RemoveRequestParameter
spring:
  cloud:
    gateway:
      routes:
      - id: user_route
        uri: http://localhost:8092
        predicates:
        - Path=/user-api/**
        filters:
        - RemoveRequestParameter=name

删除请求参数。

  • 过滤器十三:SetRequestHeader
spring:
  cloud:
    gateway:
      routes:
      - id: user_route
        uri: http://localhost:8092
        predicates:
        - Path=/user-api/**
        filters:
        - SetRequestHeader=X-Request-Red, Blue

替换所有请求头,逗号隔开,前一个是请求头key,第二个是请求头替换后的的值。

  • 过滤器十四:SetResponseHeader
spring:
  cloud:
    gateway:
      routes:
      - id: user_route
        uri: http://localhost:8092
        predicates:
        - Path=/user-api/**
        filters:
        - SetResponseHeader=X-Response-Red, Blue

替换所有响应头,逗号隔开,前一个是响应头key,第二个是响应头替换后的的值。

好了,gateway网关的大部分配置内容就介绍完了,spring cloud gateway的官方文档总体上比其他子项目的要容易理解一些,有兴趣的可以自己查阅。

希望本文对你有所帮助。

相关推荐

sharding-jdbc实现`分库分表`与`读写分离`

一、前言本文将基于以下环境整合...

三分钟了解mysql中主键、外键、非空、唯一、默认约束是什么

在数据库中,数据表是数据库中最重要、最基本的操作对象,是数据存储的基本单位。数据表被定义为列的集合,数据在表中是按照行和列的格式来存储的。每一行代表一条唯一的记录,每一列代表记录中的一个域。...

MySQL8行级锁_mysql如何加行级锁

MySQL8行级锁版本:8.0.34基本概念...

mysql使用小技巧_mysql使用入门

1、MySQL中有许多很实用的函数,好好利用它们可以省去很多时间:group_concat()将取到的值用逗号连接,可以这么用:selectgroup_concat(distinctid)fr...

MySQL/MariaDB中如何支持全部的Unicode?

永远不要在MySQL中使用utf8,并且始终使用utf8mb4。utf8mb4介绍MySQL/MariaDB中,utf8字符集并不是对Unicode的真正实现,即不是真正的UTF-8编码,因...

聊聊 MySQL Server 可执行注释,你懂了吗?

前言MySQLServer当前支持如下3种注释风格:...

MySQL系列-源码编译安装(v5.7.34)

一、系统环境要求...

MySQL的锁就锁住我啦!与腾讯大佬的技术交谈,是我小看它了

对酒当歌,人生几何!朝朝暮暮,唯有己脱。苦苦寻觅找工作之间,殊不知今日之事乃我心之痛,难道是我不配拥有工作嘛。自面试后他所谓的等待都过去一段时日,可惜在下京东上的小金库都要见低啦。每每想到不由心中一...

MySQL字符问题_mysql中字符串的位置

中文写入乱码问题:我输入的中文编码是urf8的,建的库是urf8的,但是插入mysql总是乱码,一堆"???????????????????????"我用的是ibatis,终于找到原因了,我是这么解决...

深圳尚学堂:mysql基本sql语句大全(三)

数据开发-经典1.按姓氏笔画排序:Select*FromTableNameOrderByCustomerNameCollateChinese_PRC_Stroke_ci_as//从少...

MySQL进行行级锁的?一会next-key锁,一会间隙锁,一会记录锁?

大家好,是不是很多人都对MySQL加行级锁的规则搞的迷迷糊糊,一会是next-key锁,一会是间隙锁,一会又是记录锁。坦白说,确实还挺复杂的,但是好在我找点了点规律,也知道如何如何用命令分析加...

一文讲清怎么利用Python Django实现Excel数据表的导入导出功能

摘要:Python作为一门简单易学且功能强大的编程语言,广受程序员、数据分析师和AI工程师的青睐。本文系统讲解了如何使用Python的Django框架结合openpyxl库实现Excel...

用DataX实现两个MySQL实例间的数据同步

DataXDataX使用Java实现。如果可以实现数据库实例之间准实时的...

MySQL数据库知识_mysql数据库基础知识

MySQL是一种关系型数据库管理系统;那废话不多说,直接上自己以前学习整理文档:查看数据库命令:(1).查看存储过程状态:showprocedurestatus;(2).显示系统变量:show...

如何为MySQL中的JSON字段设置索引

背景MySQL在2015年中发布的5.7.8版本中首次引入了JSON数据类型。自此,它成了一种逃离严格列定义的方式,可以存储各种形状和大小的JSON文档,例如审计日志、配置信息、第三方数据包、用户自定...

取消回复欢迎 发表评论: