Spring Cloud Gateway详细介绍以及实现动态路由

news2024/11/15 12:10:42

一. 简介

Spring Cloud Gateway
This project provides a libraries for building an API Gateway on top of Spring WebFlux or Spring WebMVC. Spring Cloud Gateway aims to provide a simple, yet effective way to route to APIs and provide cross cutting concerns to them such as: security, monitoring/metrics, and resiliency.

该项目提供了一个用于在 Spring WebFlux 或 Spring WebMVC 之上构建 API 网关的库。 Spring Cloud Gateway 旨在提供一种简单而有效的方法来路由到 API 并为其提供横切关注点,例如:安全性、监控/指标和弹性。

其中核心的路由功能 我们可能需要动态更新 例如说 某个服务的名称改变后 需要动态调整它路由

二. gateway的路由使用

gateway 路由的使用也比较简单

  • java配置类方式
    @SpringBootApplication
    public class GatewayApplication {
    	@Bean
    	public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
    		return builder.routes()
    			.route("path_route", r -> r.path("/get")
    				.uri("http://httpbin.org"))
    			.route("host_route", r -> r.host("*.myhost.org")
    				.uri("http://httpbin.org"))
    			.route("rewrite_route", r -> r.host("*.rewrite.org")
    				.filters(f -> f.rewritePath("/foo/(?<segment>.*)", "/${segment}"))
    				.uri("http://httpbin.org"))
    			.route("hystrix_route", r -> r.host("*.hystrix.org")
    				.filters(f -> f.hystrix(c -> c.setName("slowcmd")))
    				.uri("http://httpbin.org"))
    			.route("hystrix_fallback_route", r -> r.host("*.hystrixfallback.org")
    				.filters(f -> f.hystrix(c -> c.setName("slowcmd").setFallbackUri("forward:/hystrixfallback")))
    				.uri("http://httpbin.org"))
    			.route("limit_route", r -> r
    				.host("*.limited.org").and().path("/anything/**")
    				.filters(f -> f.requestRateLimiter(c -> c.setRateLimiter(redisRateLimiter())))
    				.uri("http://httpbin.org"))
    			.build();
    	}
    }
    
  • yaml配置文件方式
    如下配置标识 匹配 cookie 的 mycookie 属性 如果属性值 正则匹配mycookievalue的话则进行转发到 https://example.org
    spring:
      cloud:
        gateway:
          routes:
          - id: after_route
            uri: https://example.org
            predicates:
            - name: Cookie
              args:
                name: mycookie
                regexp: mycookievalue
    
    我们可以通过使用 路由断言工厂和 gateway filter 工厂 来作为规则 来进行路由命中判断

三. Route Predicate Factories

gateway自带的断言类 也有很多种,我们可以通过简单地方式就进行 使用

Spring Cloud Gateway 将路由作为 Spring WebFlux HandlerMapping 基础设施的一部分进行匹配。 Spring Cloud Gateway 包含许多内置的路由断言工厂。 所有这些断言都匹配 HTTP 请求的不同属性。 您可以将多个路由断言工厂与逻辑 and 语句组合起来。

The After Route Predicate Factory

After 路由断言工厂采用一个参数,即日期时间(这是一种 java ZonedDateTime)。 此谓词匹配指定日期时间之后发生的请求。 以下示例配置后路由谓词:

spring:
  cloud:
    gateway:
      routes:
      - id: after_route
        uri: https://example.org
        predicates:
        - After=2017-01-20T17:42:47.789-07:00[America/Denver]

The Before Route Predicate Factory

Before 路由断言工厂采用一个参数,即日期时间(这是一种 java ZonedDateTime)。 此谓词匹配在指定日期时间之前发生的请求。 以下示例配置 before 路由谓词:

spring:
  cloud:
    gateway:
      routes:
      - id: before_route
        uri: https://example.org
        predicates:
        - Before=2017-01-20T17:42:47.789-07:00[America/Denver]

The Between Route Predicate Factory

Between 路由断言工厂采用两个参数:datetime1 和 datetime2,它们是 java ZonedDateTime 对象。 此谓词匹配在 datetime1 之后和 datetime2 之前发生的请求。 datetime2 参数必须位于 datetime1 之后。 以下示例配置了 Between 路由谓词:

spring:
  cloud:
    gateway:
      routes:
      - id: between_route
        uri: https://example.org
        predicates:
        - Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]

The Cookie Route Predicate Factory

Cookie 路由谓词工厂采用两个参数:cookie 名称和正则表达式(这是一个 Java 正则表达式)。 此谓词匹配具有给定名称且其值与正则表达式匹配的 cookie。 以下示例配置 cookie 路由谓词工厂:

spring:
  cloud:
    gateway:
      routes:
      - id: cookie_route
        uri: https://example.org
        predicates:
        - Cookie=chocolate, ch.p

此路由匹配具有名为“chocolate”的 cookie 的请求,该 cookie 的值与 ch.p 正则表达式匹配。

The Header Route Predicate Factory

Header 路由断言工厂采用两个参数,即 header 和 regexp(这是一个 Java 正则表达式)。 此谓词与具有给定名称且其值与正则表达式匹配的标头匹配。 以下示例配置标头路由断言:

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

如上路由将会撇皮请求具有名为 X-Request-Id 的标头且其值与 \d+ 正则表达式匹配(即,它具有一位或多位数字的值),则此路由匹配。

The Host Route Predicate Factory

匹配host的断言

spring:
  cloud:
    gateway:
      routes:
      - id: host_route
        uri: https://example.org
        predicates:
        - Host=**.somehost.org,**.anotherhost.org

The Path Route Predicate Factory

匹配路径的路由断言

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

如果请求路径为/red/1 或/red/1/ 或/red/blue 或/blue/green,则此路由匹配。

RemoteAddr 、XForwardedRemoteAddr 等 断言 都与之类似

The Weight Route Predicate Factory

Weight 路由谓词工厂有两个参数:组和权重(一个 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

四. GatewayFilter Factories

路由过滤器允许以某种方式修改传入的 HTTP 请求或传出的 HTTP 响应。 路由过滤器的范围仅限于特定路由。 Spring Cloud Gateway 包含许多内置的 GatewayFilter Factory。
这个类似于过滤器,例如说 打印请求日志,权限校验等 也作为路由的条件判断的一种 例如说修改路径 或者重写请求路径 等

RewritePath GatewayFilter Factory

RewritePath GatewayFilter 工厂采用路径正则表达式参数和替换参数。 这使用 Java 正则表达式来灵活地重写请求路径。 以下清单配置了 RewritePath GatewayFilter:

spring:
  cloud:
    gateway:
      routes:
      - id: rewritepath_route
        uri: https://example.org
        predicates:
        - Path=/red/**
        filters:
        - RewritePath=/red/?(?<segment>.*), /$\{segment}

对于 /red/blue 的请求路径,这会在发出下游请求之前将路径设置为 /blue。 请注意,由于 YAML 规范,$ 应替换为 $\。

StripPrefix GatewayFilter Factory

StripPrefix GatewayFilter 工厂采用一个参数,parts。 parts 参数指示在向下游发送请求之前要从请求中剥离的路径中的部分数量。 以下清单配置了 StripPrefix GatewayFilter:

spring:
  cloud:
    gateway:
      routes:
      - id: nameRoot
        uri: https://nameservice
        predicates:
        - Path=/name/**
        filters:
        - StripPrefix=2

当通过网关向 /name/blue/red 发出请求时,对 nameservice 发出的请求类似于 nameservice/red。

==

五. Global Filters

GlobalFilter 接口与 GatewayFilter 具有相同的签名。 这些是有条件地应用于所有路由的特殊过滤器。

组合全局过滤器和网关过滤器排序

当请求与路由匹配时,过滤 Web 处理程序会将 GlobalFilter 的所有实例和 GatewayFilter 的所有特定于路由的实例添加到过滤器链中。 这个组合的过滤器链由 org.springframework.core.Ordered 接口排序,您可以通过实现 getOrder() 方法来设置。

六. 动态路由

路由规则主要存放于配置文件 或者 配置类中 配置类中的方式需要对源码有所了解,这里我们直接使用最简单的配置文件方式
我们使用nacos作为注册中心和配置中心
同时使用spring-cloud-alibabba的依赖来集成nacos

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

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

配置路由配置文件示例


spring:
  cloud:
    gateway:
      routes:
       - id: activityRoute                       
         uri: lb://activity-service      
         predicates:
          - Path=/activity-service/**       
         filters:
          - StripPrefix=1

uri: lb://activity-service
这是目标服务的地址,lb表示使用负载均衡的方式获取服务实例。activity-service是在服务注册中心注册的服务名称。

这段路由规则含义如下:
当请求路径匹配 /activity-service/** 时,该请求会被路由到 activity-service 服务实例。
在路由请求之前,会从请求路径中去除 /activity-service 前缀,然后将剩余路径发送给服务实例。

上述就实现了gateway的路由转发功能
接下来就是动态刷新了

好消息是 当我们集成spring cloud alibaba-nacos组件时 默认开启了自动刷新
官方文档地址: https://github.com/alibaba/spring-cloud-alibaba/blob/2022.x/spring-cloud-alibaba-examples/nacos-example/readme.md#more-configuration-items
在这里插入图片描述
所以当我们动态改变路由的配置的时 也会触发到nacos的配置更新 ,然后 springcloud gateway 会加载到变更后的路由配置 即完成了动态刷新功能

七. 优化点

根据第6六部分内容,我们已经实现了路由的动态刷新功能,但是一般生产环境上会有很多配置,我们为了避免影响到线上其他的配置 最好将配置区分开来,推荐作法,我们新建一个文件例如 gateway-service-routes.yaml 然后 在主配置中引用 从而实现配置的分离
springboot 中引入nacos配置

注意当nacos server开启鉴权后,导入config时没有加账号密码 则配置加载不到,报错信息非常不明显,因此很容易造成 gateway加载路由规则失败

spring:
  cloud:
    nacos:
      # nacos-server地址
      serverAddr: 127.0.0.1:8848
      # nacos-server开启了鉴权后需要账号密码 
      username: 'nacos'
      # nacos-server开启了鉴权后需要账号密码 
      password: 'nacos'
  config:
    import:
 	#导入gateway的路由配置 并开启刷新 
      - nacos:gateway-service-routes.yaml?refresh=true&group=DEFAULT_GROUP

八. 坑点

当开启locator.enabled 配置时

# Flag that enables DiscoveryClient gateway integration.
spring.cloud.gateway.discovery.locator.enabled=true

gateway会默认生成基于注册的服务名的路由规则
这时候我们在自定义路由 匹配的路径 和 默认生成的路由 路径那么就会到我们自定义的路由失效

源码参见: org.springframework.cloud.gateway.discovery.DiscoveryClientRouteDefinitionLocator

@Bean
@ConditionalOnProperty(name = "spring.cloud.gateway.discovery.locator.enabled")
public DiscoveryClientRouteDefinitionLocator   			    discoveryClientRouteDefinitionLocator(
		ReactiveDiscoveryClient discoveryClient, 
			DiscoveryLocatorProperties properties) {
	return new DiscoveryClientRouteDefinitionLocator(discoveryClient, properties);
}

最终在DiscoveryClientRouteDefinitionLocator 类中 创建了路由规则
源码参见: org.springframework.cloud.gateway.discovery.DiscoveryClientRouteDefinitionLocator#getRouteDefinitions

@Override
public Flux<RouteDefinition> getRouteDefinitions() {

	SpelExpressionParser parser = new SpelExpressionParser();
	Expression includeExpr = parser.parseExpression(properties.getIncludeExpression());
	Expression urlExpr = parser.parseExpression(properties.getUrlExpression());

	Predicate<ServiceInstance> includePredicate;
	if (properties.getIncludeExpression() == null || "true".equalsIgnoreCase(properties.getIncludeExpression())) {
		includePredicate = instance -> true;
	}
	else {
		includePredicate = instance -> {
			Boolean include = includeExpr.getValue(evalCtxt, instance, Boolean.class);
			if (include == null) {
				return false;
			}
			return include;
		};
	}

	return serviceInstances.filter(instances -> !instances.isEmpty()).flatMap(Flux::fromIterable)
			.filter(includePredicate).collectMap(ServiceInstance::getServiceId)
			// remove duplicates
			.flatMapMany(map -> Flux.fromIterable(map.values())).map(instance -> {
				RouteDefinition routeDefinition = buildRouteDefinition(urlExpr, instance);

				final ServiceInstance instanceForEval = new DelegatingServiceInstance(instance, properties);

				for (PredicateDefinition original : this.properties.getPredicates()) {
					PredicateDefinition predicate = new PredicateDefinition();
					predicate.setName(original.getName());
					for (Map.Entry<String, String> entry : original.getArgs().entrySet()) {
						String value = getValueFromExpr(evalCtxt, parser, instanceForEval, entry);
						predicate.addArg(entry.getKey(), value);
					}
					routeDefinition.getPredicates().add(predicate);
				}

				for (FilterDefinition original : this.properties.getFilters()) {
					FilterDefinition filter = new FilterDefinition();
					filter.setName(original.getName());
					for (Map.Entry<String, String> entry : original.getArgs().entrySet()) {
						String value = getValueFromExpr(evalCtxt, parser, instanceForEval, entry);
						filter.addArg(entry.getKey(), value);
					}
					routeDefinition.getFilters().add(filter);
				}

				return routeDefinition;
			});
}

在 Spring Cloud Gateway 中,如果存在多个路由规则的断言路径相同,但是转发的 URL 不同,那么 Gateway 将根据路由定义的顺序来选择第一个匹配的路由规则。

具体来说,Spring Cloud Gateway 在匹配路由规则时,会按照路由定义的顺序进行匹配,一旦匹配到第一个符合条件的路由规则,就会停止匹配,并使用该路由规则进行请求转发。

那这个时候 想让我们的自定义的路由生效 只要两种方式

  • 修改配置 让默认的路由规则不生效 (慎用)
  • 修改order 让我们自定义的 路由优先级最大 就会先匹配到我们的路由了
    示例如下
spring:
  cloud:
    gateway:
      discovery:
      	locator:
      	  # 默认会生成基于服务名称的路由规则
      	  enabled: true
      routes:
       - id: activity                       
         uri: lb://activity-service2    
         predicates:
          - Path=/activity-service/**       
         filters:
          - StripPrefix=1
         order: -1

activity-service 是我们的服务,因为开启了配置 默认会生成一条 路径为/activity-service/** 然后转发到lb://activity-service的路由 这我们做了迁移需要将其转发lb://activity-service2 上 故新增了此条路由 ,然后增加order属性 值为-1 我们此条路由才会生效。

spring cloud gateway 官方配置说明文档: https://docs.spring.io/spring-cloud-gateway/reference/appendix.html

the end !!!
good day

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1597099.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【读书笔记】自动驾驶与机器人中的SLAM技术——高翔

文章会对本书第五章节及以后章节进行总结概括。每日更新一部分。一起读书吧。 第五章——基础点云处理 重点&#xff1a;点云的相邻关系是许多算法的基础 5.1 激光雷达传感器与点云的数学模型 5.1.1激光雷达传感器的数学模型 雷达有两种&#xff1a;机械旋转式激光雷达&…

018——红外遥控模块驱动开发(基于HS0038和I.MX6uLL)

目录 一、 模块介绍 1.1 简介 1.2 协议 二、 驱动代码 三、 应用代码 四、 实验 五、 程序优化 一、 模块介绍 1.1 简介 红外遥控被广泛应用于家用电器、工业控制和智能仪器系统中&#xff0c;像我们熟知的有电视机盒子遥控器、空调遥控器。红外遥控器系统分为发送端和…

RISCV指令集体系简读之RV32M

RV32M向RV32I中添加了整数乘法和除法指令&#xff1b; RV32M具有有符号和无符号整数的除法指令&#xff1a;divide(div)和divide unsigned(divu)&#xff0c;它们将 商放入目标寄存器。在少数情况下&#xff0c;程序员需要余数而不是商&#xff0c;因此RV32M提供 remainder(rem…

46.HarmonyOS鸿蒙系统 App(ArkUI)网格布局

Grid(){GridItem(){Button(按钮1).fontSize(28)}.backgroundColor(Color.Blue)GridItem(){Text(数学).fontSize(28)}.backgroundColor(Color.Yellow)GridItem(){Text(语文).fontSize(28)}.backgroundColor(Color.Green)GridItem(){Text(英语).fontSize(28)}.backgroundColor(Co…

结构型模式--3.组合模式【草帽大船团】

1. 好大一棵树 路飞在德雷斯罗萨打败多弗朗明哥之后&#xff0c;一些被路飞解救的海贼团自愿加入路飞麾下&#xff0c;自此组成了草帽大船团&#xff0c;旗下有7为船长&#xff0c;分别是&#xff1a; 俊美海贼团75人 巴托俱乐部56人 八宝水军1000人 艾迪欧海贼团4人 咚塔塔海…

暴雨信息专场推介会亮相武汉国际创科展

4月14日&#xff0c;暴雨信息专场推介会亮相2024年武汉国际创科展。推介会以“智慧聚变新生”为主题&#xff0c;集中展示推介暴雨信息旗下多项颇具市场优势的智能化产品和解决方案&#xff0c;邀请多位专家分享在数字化转型领域的成果和实践经验&#xff0c;与业界同仁共同探讨…

osg渲染过程

目录 1、渲染最简单代码 2、详解run方法 3、详细过程 4、回调函数 5、Node Visitor 1、渲染最简单代码 2、详解run方法 3、详细过程 3.1 advance()方法 进行帧计数 3.2 eventTraversal() eventTraversal()响应用户操作,eventTraversal()遍历的是事件队列&#xff0c;而…

Java开发从入门到精通(二十):Java的面向对象编程OOP:Collection集合框架

Java大数据开发和安全开发 &#xff08;一&#xff09;Java的集合进阶1.1 集合体系结构1.2 Collection集合1.2.1 Collection集合包含哪些接口和实现类1.2.2 Collection集合特点1.2.3 为啥要先学Collection的常用方法?1.2.4 Collection集合的遍历1.2.4.1 迭代器1.2.4.1.1 迭代器…

基于SpringBoot+Vue实现的医院在线挂号系统(代码+万字文档)

系统介绍 基于SpringBootVue实现的医院在线挂号系统设计了三种角色&#xff0c;分别是管理员、医生、用户&#xff0c;每种角色对应不同的菜单 系统实现了个人信息管理、基础数据管理、论坛管理、用户管理、单页数据管理、医生管理及轮播图管理等功能模块&#xff0c;具体功能…

16.读取指定路径下的txt文档然后合并内容为一个txt文档。

1.题目要求 分别读取路径为 ./middle/phone/base/1_student_0.txt, ./middle/vr/base/1_teacher.txt, ./nearby/phone/base/1_student_0.txt, ./nearby/vr/base/1_teacher.txt, ./outside/phone/base/1_student_0.txt, ./outside/vr/base/1_teacher.txt 里面的文件&#xff…

【LeetCode: 3117. 划分数组得到最小的值之和 + 动态规划】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

linux shell脚本编写(2)

Shell: 命令转换器&#xff0c;高级语言转换成二进制语言。是Linux的一个外壳&#xff0c;它包在Lniux内核的外面&#xff0c;用户和内核之间的交互提供了一个接口。 内置命令&#xff1a;在shell内部不需要shell编辑 外置命令&#xff1a;高级语言要用shell转换成二进制语言 …

Numpy数组和列表list的区别

参考&#xff1a;Numpy Array vs List 在Python编程中&#xff0c;列表&#xff08;list&#xff09;和Numpy数组&#xff08;numpy array&#xff09;是两种常见的数据结构&#xff0c;它们都可以用来存储多个元素。但是它们在实际使用中有很大的区别&#xff0c;本文将详细比…

【尚硅谷】Git与GitLab的企业实战 学习笔记

目录 第1章 Git概述 1. 何为版本控制 2. 为什么需要版本控制 3. 版本控制工具 4. Git简史 5. Git工作机制 6. Git和代码托管中心 第2章 Git安装 第3章 Git常用命令 1. 设置用户签名 1.1 基本语法 1.2 案例实操 2. 初始化本地库 2.1 基本语法 2.2 案例实操 3. 查…

RabbbitMQ基本使用及其五种工作模型

初识MQ 同步通讯和异步通讯 什么是同步通讯呢&#xff1f;举个例子&#xff0c;你认识了一个小姐姐&#xff0c;聊的很火热&#xff0c;于是你们慢慢开始打电话&#xff0c;视频聊天&#xff0c;这种方式就成为同步通讯&#xff0c;那什么是一部通讯呢&#xff0c;同样的&…

P1706 全排列问题

原题链接:全排列问题 - 洛谷 目录 1. 题目描述 2. 思路分析 3. 代码实现 1. 题目描述 2. 思路分析 dfs典题 3. 代码实现 #define _CRT_SECURE_NO_WARNINGS 1 #include<bits/stdc.h> using namespace std; #define ll long long #define endl \n const int N 2…

电脑技巧:Bandicam班迪录屏介绍

目录 一、 软件简介 二、软件功能 2.1 屏幕录制 2.2 游戏录制 2.3 设备录制 2.4实时编辑与截图 2.5 轻量级软件 三、软件用途 3.1 教育培训 3.2 游戏直播与分享 3.3 企业办公 3.4 在线教学与知识分享 四、总结 今天给大家推荐一款非常实用的电脑录屏软件&#xf…

Java实现单点登录(SSO)详解:从理论到实践

✨✨谢谢大家捧场&#xff0c;祝屏幕前的小伙伴们每天都有好运相伴左右&#xff0c;一定要天天开心哦&#xff01;✨✨ &#x1f388;&#x1f388;作者主页&#xff1a; 喔的嘛呀&#x1f388;&#x1f388; ✨✨ 帅哥美女们&#xff0c;我们共同加油&#xff01;一起进步&am…

提高大型语言模型 (LLM) 性能的四种数据清理技术

原文地址&#xff1a;four-data-cleaning-techniques-to-improve-large-language-model-llm-performance 2024 年 4 月 2 日 检索增强生成&#xff08;RAG&#xff09;过程因其增强对大语言模型&#xff08;LLM&#xff09;的理解、为它们提供上下文并帮助防止幻觉的潜力而受…

java-字符串

一、String概述 java.lang.String类代表字符串&#xff0c;java中所有字符串文字都是该类的对象 字符串的内容是不会发生变化的&#xff0c;它的对象在创建之后就不能被更改 二、创建String对象 1、直接赋值 语法&#xff1a; String 变量名内容&#xff1b; 2、使用构造…