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

news/2024/5/18 12:36:04

一. 简介

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 {@Beanpublic 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_routeuri: https://example.orgpredicates:- name: Cookieargs:name: mycookieregexp: 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_routeuri: https://example.orgpredicates:- 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_routeuri: https://example.orgpredicates:- 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_routeuri: https://example.orgpredicates:- 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_routeuri: https://example.orgpredicates:- Cookie=chocolate, ch.p

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

The Header Route Predicate Factory

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

spring:cloud:gateway:routes:- id: header_routeuri: https://example.orgpredicates:- Header=X-Request-Id, \d+

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

The Host Route Predicate Factory

匹配host的断言

spring:cloud:gateway:routes:- id: host_routeuri: https://example.orgpredicates:- Host=**.somehost.org,**.anotherhost.org

The Path Route Predicate Factory

匹配路径的路由断言

spring:cloud:gateway:routes:- id: path_routeuri: https://example.orgpredicates:- 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_highuri: https://weighthigh.orgpredicates:- Weight=group1, 8- id: weight_lowuri: https://weightlow.orgpredicates:- 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_routeuri: https://example.orgpredicates:- 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: nameRooturi: https://nameservicepredicates:- 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: trueroutes:- id: activity                       uri: lb://activity-service2    predicates:- Path=/activity-service/**       filters:- StripPrefix=1order: -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.mrgr.cn/p/25556002

相关文章

JavaBean知识

“感谢您阅读本篇博客!如果您觉得本文对您有所帮助或启发,请不吝点赞和分享给更多的朋友。您的支持是我持续创作的动力,也欢迎留言交流,让我们一起探讨技术,共同成长!谢谢!🚀✨”JavaBean 使用前端HTML代码和Java 代码分离,业务逻辑单独封装,然后在JSP页面中调用,就降…

JavaBean-cnblog

JavaBean 使用前端HTML代码和Java 代码分离,业务逻辑单独封装,然后在JSP页面中调用,就降低了耦合度,让JSP更简介,更易维护复用,这样的类就是一个JavaBean组件类JavaBean分为可视化组件和非可视化组件两种,可视化组件可以是简单的GUI元素,如按钮 文本框可以是报表组件,非可视化没…

ctf知识积累

(1)url解码:python解码函数:from urllib.parse import unquote(quote:编码)url_code=""url_code1=unquote(url_code)print(url_code1)Edge 浏览器URL解码:开发人员工具输入:decodeURIComponent(‘’)(2)Unicode解码:from urllib.parse import unquote_plu…

机器人实验室CNRS-AIST JRL, IRL介绍

一、背景 作为搞机器人方向的学生&#xff0c;必须时常关注国际上顶尖实验室的研究成果&#xff0c;以免自己做的方向out&#xff0c;除了大家耳熟能详的Boston Dynamics&#xff0c;还有许多非常厉害的机器人实验室值得我们关注&#xff0c;如日本的CNRS-AIST JRL, IRL实验室…

数据结构PT1——线性表/链表

1&#xff1a;顺序存储实现(数组实现) Data&#xff1a; a1 a2 .....ai ai1 .... an .... typedef struct LNode *List; //指向LNode的指针&#xff0c;这是typedef的&#xff0c;你可以随时声明&#xff0c;而不加typedef只是创建一个 struct LNode{ //结构体成员ElementT…

初中中考阅读理解难题一网打尽!句子结构深度解析+答案揭秘,助你轻松冲刺中考高分!-009

PDF格式公众号回复关键字:ZKYDT009原文1 How did the lot look at the beginning of the story? 解析 1 How 怎么样 did ,the lot 场地, look 看起来,at the beginning of the story?在故事的开头 故事开始时,那个场地看起来怎么样? 2 This place looks like a dump. 这…

30 天精通 RxJS (28):Scheduler 基本观念

不晓得读者们还记不记得,我们在前面的文章中有提到 Scheduler 是为了解决 RxJS 衍生的最后一个问题,而我们现在就在揭晓这个谜底。本系列仅作为学习记录所用,摘录自30 天精通 Rxjs!强烈推荐!膜拜大佬!

sublime text的json快捷键

系统 macos 配置 sublime Text->Settings->Key Bindings 效果 可以看到&#xff0c;按&#xff1a;shiftcommandp&#xff0c;会出现快捷键窗口&#xff0c;打pretty&#xff0c;会出现Format JSON&#xff0c;最右侧显示⌘J&#xff0c;说明只需要macos的⌘和J同时按…

2024红明谷杯——Misc 加密的流量

2024红明谷杯——Misc 加密的流量 写在前面&#xff1a; 这里是贝塔贝塔&#xff0c;照例来一段闲聊 打比赛但赛前一波三折&#xff0c;又是成功签到的一个比赛 说起来比赛全名叫红明谷卫星应用数据安全场景赛&#xff0c;但好像真的跟卫星的关系不大&#xff0c;没有bin方…

顺序表和链表的练习题

顺序表 题目一:题目分析: 该题目需要先对顺序表进行遍历至元素x正确插入位置,再对顺序表完成插入操作。因此涉及到for循环与if语句的使用 代码实现 /******************************************************************** * * name : SequenceList_insert * function : …

JZ8 二叉树的下一个结点

#include <cstddef> class Solution { public:vector<TreeLinkNode*> nodes; //用户得到的输入只有一个 子树根节点TreeLinkNode* GetNext(TreeLinkNode* pNode) {TreeLinkNode* root = pNode;//获取根节点while(root->next )root = root->next;//中序遍历用…

没闲着系列 20

现已将dashboard页面加入了需求的图表和点击panel. 如下图:且现在这个图表是根据迭代出现的,及哪个迭代中出现了多少个需求的story(包括变更、新增、删减、确认), 之后要做的就是将breakdown story和issues连起来,当然在story需求中也可以直接更改状态到完成,而无需创建issues.…

C++:多态

目录 概念&#xff1a; 多态产生的条件&#xff1a; 虚函数的重写&#xff1a; 虚函数&#xff1a;即被virtual修饰的类成员函数称为虚函数 虚函数重写的两个例外&#xff1a; 协变(基类与派生类虚函数返回值类型不同) 析构函数 而为什么没有调用到子类呢&#xff1f; …

查找链表中倒数第k(k为正整数)个位置上的结点,查找成功输出该结点的data值,并返回1,否则只返回0

/******************************************************** name : FindKNode* function : 查找链表中倒数第k(k为正整数)个位置上的结点* 查找成功输出该结点的data值,并返回1,否则只返回0* argument* @head : 链表头结点的地址*…

杂货铺 | KVM虚拟化环境的配置 初步实现两种Guest OS虚拟机的部署

文章目录 &#x1f4da;在ubuntu系统的虚拟机上挂载CentOS操作系统的客户机&#x1f407;下载镜像并配置虚拟机&#x1f407;开启虚拟机&#xff0c;检查CPU是否支持虚拟化&#x1f407;查看是否加载KVM模块&#x1f407;关闭selinux&#x1f407;安装KVM相关软件包&#x1f40…

DRF之View和APIView

【零】DRF在Django项目中的使用 【1】导入 # DRF需要使用pip install 安装 pip install djangorestframeworkDRF(Django Rest Framework)是一个用于构建 Web API 的工具包,它是基于 Django 框架的一个第三方应用(app) 在 Django 项目中,一个应用(app)通常是一个具有特定…

删除顺序表L中下标为p(0≤p≤length-1)的元素,成功返回1,否则返回0,并将被删除元素的值赋给e

删除顺序表L中下标为p(0≤p≤length-1)的元素,成功返回1,否则返回0,并将被删除元素的值赋给e/******************************************************************************************************** * * file name: Zqh_splist_4.22.2.c * author : keyword2024…

HarmonyOS NEXT应用开发案例—使用弹簧曲线实现抖动动画及手机振动效果案例

介绍 本示例介绍使用vibrator.startVibration方法实现手机振动效果,用animateTo显示动画实现点击后的抖动动画。 效果图预览使用说明加载完成后显示登录界面,未勾选协议时点击一键登录按钮会触发手机振动效果和提示文本的抖动动画。实现思路创建一个函数startVibrate()调用vi…

删除最小值结点

/******************************************************** name : DelTargetNode* function : 删除单链表L(有头结点)中的一个最小值结点* argument* @L :链表头结点的地址** retval : None* author : Dazz* date : 2024/4/22* …

【C++】适配器· 优先级队列 仿函数 反向迭代器

目录 适配器&#xff1a;适配器的应用&#xff1a;1. 优先级队列&#xff1a;仿函数&#xff1a;更深入的了解仿函数&#xff1a;一个关于不容易被注意的知识点&#xff1a; 2. 反向迭代器&#xff1a;&#xff08;list为例&#xff09; 适配器&#xff1a; 我们先来谈来一下容…