当前位置: 首页 > news >正文

springcloud-gateway 路由加载流程

问题

Spring Cloud Gateway版本是2.2.9.RELEASE,原本项目中依赖服务自动发现来自动配置路由到微服务的,但是发现将spring.cloud.gateway.discovery.locator.enabled=false 启动之后Gateway依然会将所有微服务自动注册到路由中,百思不得其解,遂调试代码观察期启动过曾,记录此文以供参考,官方文档
在这里插入图片描述
可以看到此处明确表示,可以通过设置该值来讲微服务中服务自动添加到配置中。按照这个线索,找到了
org.springframework.cloud.gateway.discovery.DiscoveryClientRouteDefinitionLocator#getRouteDefinitions
这个类在之前的文章includeExpression中有提及过,在构造函数中对成员变量

public class DiscoveryClientRouteDefinitionLocator implements RouteDefinitionLocator {private final DiscoveryLocatorProperties properties;...private Flux<List<ServiceInstance>> serviceInstances;/*** Kept for backwards compatibility. You should use the reactive discovery client.* @param discoveryClient the blocking discovery client* @param properties the configuration properties* @deprecated kept for backwards compatibility*/@Deprecatedpublic DiscoveryClientRouteDefinitionLocator(DiscoveryClient discoveryClient,DiscoveryLocatorProperties properties) {this(discoveryClient.getClass().getSimpleName(), properties);serviceInstances = Flux.defer(() -> Flux.fromIterable(discoveryClient.getServices())).map(discoveryClient::getInstances).subscribeOn(Schedulers.boundedElastic());}public DiscoveryClientRouteDefinitionLocator(ReactiveDiscoveryClient discoveryClient,DiscoveryLocatorProperties properties) {this(discoveryClient.getClass().getSimpleName(), properties);serviceInstances = discoveryClient.getServices().flatMap(service -> discoveryClient.getInstances(service).collectList());}...

这里会对serviceInstances进行初始化,从注册中心获取所有已注册的微服务实例进行填充,serviceInstance的获取结束了,在DiscoveryClientRouteDefinitionLocator还有一个重要的方法

#DiscoveryClientRouteDefinitionLocator@Overridepublic 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()).map(instances -> instances.get(0)).filter(includePredicate).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;});}

根据includeExpr 配置的sl表达式判断当前的serviceInstance是否服务转发要求。填充好predicate,filter之后返回routeDefinition,urlExpr的默认值是"lb://"+serverId,predicate匹配的path就等于serverId,有配置可以转换为小写spring.cloud.gateway.discovery.locator.lower-case-service-id=true因为eureka默认会uppercase serverId,filter中会StripPrefix=1,将路径中的serverId移除再转发给下游。路由定义的部分就这里了,他的刷新逻辑,依靠SpringRefreshContext 事件,具体的定义逻辑在org.springframework.cloud.gateway.config.GatewayAutoConfiguration

	@Bean@ConditionalOnClass(name = "org.springframework.cloud.client.discovery.event.HeartbeatMonitor")public RouteRefreshListener routeRefreshListener(ApplicationEventPublisher publisher) {return new RouteRefreshListener(publisher);}

RouteRefreshListener 实现了ApplicationListener<ApplicationEvent>,监控以下几个事件,并按照条件进行逻辑执行

  • ContextRefreshEvent 启动容器,或者beans重新加载
  • RefreshScopeRefreshdEvent 是 Spring Cloud 中的一个事件,当 @RefreshScope 注解的 beans 被重新加载时发布。这个事件主要用于 Spring Cloud 的配置刷新机制,当配置中心的配置信息发生变化时,会触发该事件以刷新 beans。
  • ParentHeartbeatEvent 是 Spring Cloud Bus 中的一个事件,用于在父上下文(通常是 Spring Cloud Config Server)发生心跳时发布。这个事件用于通知子上下文(如微服务)关于父上下文的健康状态。
  • HeartbeatEvent 是 Spring Cloud Bus 中的另一个事件,用于在服务实例之间广播心跳消息,以检测和维持各个服务的健康状态。
	public void onApplicationEvent(ApplicationEvent event) {if (event instanceof ContextRefreshedEvent) {ContextRefreshedEvent refreshedEvent = (ContextRefreshedEvent) event;if (!WebServerApplicationContext.hasServerNamespace(refreshedEvent.getApplicationContext(), "management")) {reset();}}else if (event instanceof RefreshScopeRefreshedEvent|| event instanceof InstanceRegisteredEvent) {reset();}else if (event instanceof ParentHeartbeatEvent) {ParentHeartbeatEvent e = (ParentHeartbeatEvent) event;resetIfNeeded(e.getValue());}else if (event instanceof HeartbeatEvent) {HeartbeatEvent e = (HeartbeatEvent) event;resetIfNeeded(e.getValue());}}

容器启动完成之后,该监听器接收到ContextRefreshedEvent ,会执行reset方法,reset方法主要功能是发送一个RefreshRoutesEvent 事件,定义如下

public class RefreshRoutesEvent extends ApplicationEvent {public RefreshRoutesEvent(Object source) {super(source);}
}

同样在AutoConfig中声明的CachingRouteLocator接收该参数,

tip:另外还有个CachingRouteDefinitionLocator也生命监听 RefreshRoutesEvent事件,但是默认配置下并不会产生任何逻辑

	@Bean@Primary@ConditionalOnMissingBean(name = "cachedCompositeRouteLocator")// TODO: property to disable composite?public RouteLocator cachedCompositeRouteLocator(List<RouteLocator> routeLocators) {return new CachingRouteLocator(new CompositeRouteLocator(Flux.fromIterable(routeLocators)));}

event invoke方法如下

	public void onApplicationEvent(RefreshRoutesEvent event) {try {fetch().collect(Collectors.toList()).subscribe(list -> Flux.fromIterable(list).materialize().collect(Collectors.toList()).subscribe(signals -> {applicationEventPublisher.publishEvent(new RefreshRoutesResultEvent(this));cache.put(CACHE_KEY, signals);}, throwable -> handleRefreshError(throwable)));}catch (Throwable e) {handleRefreshError(e);}}

CompositeRouteLocator的目的是混合 多个来源的Routers定义比如配置文件,接口,注册中心,通过@Bean形式定义的路由。可以理解成mixed route locator。
fetch方法就是获取所有RouteLocator的路由定义
private Flux<Route> fetch() { return this.delegate.getRoutes().sort(AnnotationAwareOrderComparator.INSTANCE); }
这里其实就是指定,之前org.springframework.cloud.gateway.discovery.DiscoveryClientRouteDefinitionLocator#getRouteDefinitions的方法也就是获取具体的路由定义,获取成功之后放入缓存,Fire RefreshRoutesResultEvent事件就解决了,开发人员可以通过 监听RefreshRoutesResultEvent 主动感知当前的路由变化。路由加载流程到此结束,

后语

我的问题是通过设置spring.cloud.gateway.discovery.locator.enabled依然没有办法让微服务注册失效,在整个流程中并没有看到使用该数值的地方,难道这是一个bug?转念一想DiscoveryClientRouteDefinitionLocator只需要阻止该Bean的注册即可取消掉从eureka获取服务实例的能力,通过引用关系可知

  • org.springframework.cloud.gateway.discovery.GatewayDiscoveryClientAutoConfiguration.ReactiveDiscoveryClientRouteDefinitionLocatorConfiguration
  • org.springframework.cloud.gateway.discovery.GatewayDiscoveryClientAutoConfiguration.BlockingDiscoveryClientRouteDefinitionLocatorConfiguration

这里两处找到了其定义,一个是为reactive非阻塞io准备的,一个是为阻塞io准备的,根据源代码可知,具体执行提供那个Locator取决于spring.cloud.discovery.reactive.enabled,我的项目中是通过VM Options参数的形式传入的-Dspring.cloud.config.discovery.enabled=true,所以我在这两个方法处都打了断点。

		@Configuration(proxyBeanMethods = false)@ConditionalOnProperty(value = "spring.cloud.discovery.reactive.enabled",matchIfMissing = true)public static class ReactiveDiscoveryClientRouteDefinitionLocatorConfiguration {@Bean@ConditionalOnProperty(name = "spring.cloud.gateway.discovery.locator.enabled")public DiscoveryClientRouteDefinitionLocator discoveryClientRouteDefinitionLocator(ReactiveDiscoveryClient discoveryClient,DiscoveryLocatorProperties properties) {return new DiscoveryClientRouteDefinitionLocator(discoveryClient, properties);}}@Configuration(proxyBeanMethods = false)@Deprecated@ConditionalOnProperty(value = "spring.cloud.discovery.reactive.enabled",havingValue = "false")public static class BlockingDiscoveryClientRouteDefinitionLocatorConfiguration {@Bean@ConditionalOnProperty(name = "spring.cloud.gateway.discovery.locator.enabled")public DiscoveryClientRouteDefinitionLocator discoveryClientRouteDefinitionLocator(DiscoveryClient discoveryClient, DiscoveryLocatorProperties properties) {return new DiscoveryClientRouteDefinitionLocator(discoveryClient, properties);}}

当我准备要执行调试模式想看看具体是哪个Bean被初始化的时候,恍然大悟看到了DiscoveryClientRouteDefinitionLocator的依赖,我自己在GatewayApplication 中手动注册了Bean

    @Beanpublic DiscoveryClientRouteDefinitionLocator discoveryClientRouteLocator(ReactiveDiscoveryClient discoveryClient,DiscoveryLocatorProperties discoveryLocatorProperties) {return new DiscoveryClientRouteDefinitionLocator(discoveryClient, discoveryLocatorProperties);}

真的是笑死,出这个问题的原因是因为最开始的代码使用的出c4o老师生成的,一开始并不明确该代码的作用,后面查看文档才知道用于微服务发现。已经不在关注这里了。

相关文章:

springcloud-gateway 路由加载流程

问题 Spring Cloud Gateway版本是2.2.9.RELEASE&#xff0c;原本项目中依赖服务自动发现来自动配置路由到微服务的&#xff0c;但是发现将spring.cloud.gateway.discovery.locator.enabledfalse 启动之后Gateway依然会将所有微服务自动注册到路由中&#xff0c;百思不得其解&a…...

双减期末考试成绩怎么公布?

考试一直是衡量学生学习成果的重要手段。不过&#xff0c;随着"双减"政策的实施&#xff0c;我们就不得不重新审视传统的成绩公布方式。期末考试成绩&#xff0c;这个曾经让无数学生心跳加速的数字&#xff0c;如今该如何以一种更加合理、公正的方式呈现给学生和家长…...

2, 搭建springCloud 项目 测试demo

上篇文章 新建了父依赖服务&#xff0c;这篇文章就建两个demo测试服务。 因为后面需要做服务间的通讯测试&#xff0c;所以至少需要建两个服务 建个子模块 同样的方式建连个demo服务 给java 和resources目录添加属性 在resources目录下建一个applications.yml文件&#xff0c;…...

RabbitMQ消息积压比较厉害,然后突然丢弃

RabbitMQ中的消息积压陡降通常表明某些突发事件或操作已经显著减少了队列中的消息数量。这种现象可能由多种原因引起&#xff0c;以下是一些可能的原因及其解释&#xff1a; 消费者处理速度突然增加 原因: 你的消费者&#xff08;消费者应用或服务&#xff09;可能在某个时间点…...

QT中的样式表.qss文件

一、前言 qt中样式表的改变有几种方法&#xff0c;第一种就是直接在ui界面对应的组件右键修改样式表&#xff0c;还有一种就是直接在程序里面修改样式表&#xff0c;我知道的还有一种就是qss文件&#xff0c;这个文件就是将在程序中写的修改样式表的语句写道qss文件中&#xff…...

HTML图片链接缓存问题解决

关于解决HTML使用图片链接出现的缓存问题处理 1、项目上明明替换了图片却没发现更新&#xff0c;得去浏览器设置清除浏览器缓存或者其它一些操作才能解决&#xff0c;这也太麻烦了&#xff01;加载过一次不会再加载第二次&#xff0c;其实这时候就存在浏览器图片缓存情况&…...

一个人 三个月 干了二十万

相信很多人是被这个标题吸引进来的&#xff0c;但我并不是标题党&#xff0c;我也很讨厌标题党&#xff0c;这篇文章也不在乎流量&#xff0c;更多的是想记录下。 出来创业三个多月了&#xff0c;给大家汇报一下这段时间的业绩吧。一个人&#xff0c;三个多月&#xff0c;干了…...

设计模式之【工厂模式、适配器模式】

工厂模式&#xff08;Factory Pattern&#xff09; 定义&#xff1a; 工厂模式是一种创建型设计模式&#xff0c;它提供了一个创建对象的接口&#xff0c;但由子类决定要实例化的类是哪一个。工厂方法使一个类的实例化延迟到其子类。 主要类型&#xff1a; 简单工厂模式&…...

云计算:重塑数字时代的基石

目录 一、引言 二、云计算的定义与特点 三、云计算的发展历程 四、云计算的应用场景 五、云计算面临的挑战 六、云计算的未来发展趋势 七、结语 一、引言 随着信息技术的飞速发展&#xff0c;云计算已经逐渐渗透到我们生活的方方面面。从个人用户的在线存储、在线办公&…...

C# SocketUDP服务器,组播

SocketUDP 自己即是服务器又是客户端 &#xff0c;在发消息只需要改成对方ip和端口号即可 前提对方必须开启服务器 socket.Bind(new IPEndPoint(IPAddress.Parse("192.168.107.72"), 8080)); 控件&#xff1a;Button,TextBox,RichTextBox 打开自己服务器 public…...

上市公司绿色投资者原始数据+计算代码(2008-2022年)

数据简介&#xff1a;“绿色”信号&#xff0c;意味着潜在环境风险更低&#xff0c;从而绿色投资者降低了对绿色债券的风险补偿要求&#xff0c;推动了信用利差的收窄。因此&#xff0c;绿色投资者会通过投资者风险意识影响债券信用风险。绿色投资者在推动企业绿色可持续发展方…...

Redis-主从复制-测试主从模式下的读写操作

文章目录 1、在主机6379写入数据2、在从机6380上写数据报错3、从机只能读数据&#xff0c;不能写数据 1、在主机6379写入数据 127.0.0.1:6379> keys * (empty array) 127.0.0.1:6379> set uname jim OK 127.0.0.1:6379> get uname "jim" 127.0.0.1:6379>…...

Linux系统应用与设置(3):串口调试(minicom)

目录 1. 简述 2. 安装minicom 3. 配置串口参数 4. 打开相应的通信端口 5. 设置 6. 发送字符数据 7. 发送HEX&#xff08;十六进制&#xff09; 1. 简述 在Linux系统中&#xff0c;minicom是一个功能强大的串口通信工具&#xff0c;可用于与外部设备进行字符和HEX数据的收…...

Qt | windows Qt6.5.3安卓环境搭建成功版(保姆级教程)

01、第一章 Qt6.5.3安装 资源 Qt 国内下载地址清华大学开源软件镜像站https://mirrors.tuna.tsinghua.edu.cn/qt/archive/online_installers/Qt 阿里云盘下载Qt 安卓开发https://www.alipan.com/s/kNaues6CHaG点击链接保存,或者复制本段内容,打开「阿里云盘」APP ,无需下载极…...

Chrome Extension如何让work flow在页面刷新、跳转继续执行任务

从Electron到Chrome Extension:流程自动化的跨平台探索 在之前的项目中,我使用Electron成功实现了一个流程自动化应用,它极大地提高了工作效率。然而,当面对Chrome扩展程序(Extension)的需求时,我意识到需要一种新的策略来应对浏览器环境下的限制。特别是Chrome Extens…...

数据库调优厂商 OtterTune 宣布停止运营

昨天刷到消息&#xff0c;得知数据库优化厂商 OtterTune 停止了运营。OtterTune 的成员主要来自 CMU Andy Pavlo 教授领导的数据库实验室。公司正式成立于 2021 年 5 月&#xff0c;融资了 1450 万美金。 按照 Andy 教授的说法&#xff0c;公司是被一个收购 offer 搞砸了。同时…...

4、面向对象-typescript

从面向过程转向面向对象&#xff0c;是一个由简易到复杂的过程&#xff0c;主要更多的考虑到对象的属性&#xff0c;方法&#xff0c;行为等。 一、类&#xff0c;对象 类&#xff1a;更偏向于模板&#xff0c;属于抽象类型&#xff0c;指向大类或泛指。 对象&#xff…...

大数据学习之分布式数据采集系统Flume学习

分布式数据采集系统Flume学习 一、Flume架构 1.1 Hadoop业务开发流程 1.2 Flume概述 flume是一个分布式、可靠、和高可用的海量日志采集、聚合和传输的系统。 支持在日志系统中定制各类数据发送方&#xff0c;用于收集数据; 同时&#xff0c;Flume提供对数据进行简单处理&…...

谁用谁夸,为什么BI零售数据分析方案这么受欢迎?

在当今数字化时代&#xff0c;零售行业竞争激烈&#xff0c;如何快速准确地获取数据洞察&#xff0c;成为企业制胜的关键。奥威BI零售数据分析方案&#xff0c;凭借其全面、高效、智能的特点&#xff0c;赢得了广大零售企业的青睐&#xff0c;成为市场上的热门选择。 奥威BI零…...

多路h265监控录放开发-(14)通过PaintCell自定义日历控件继承QCalendarWidget的XCalendar类

首先创建一个新类XCalendar继承QCalendarWidget类&#xff0c;然后在UI视图设计器中把日历提升为XCalendar&#xff0c;通过这个函数自己设置日历的样式 xcalendar.h #pragma once #include <QCalendarWidget> class XCalendar :public QCalendarWidget { public:XCal…...

从WWDC看苹果产品发展的规律

WWDC 是苹果公司一年一度面向全球开发者的盛会&#xff0c;其主题演讲展现了苹果在产品设计、技术路线、用户体验和生态系统构建上的核心理念与演进脉络。我们借助 ChatGPT Deep Research 工具&#xff0c;对过去十年 WWDC 主题演讲内容进行了系统化分析&#xff0c;形成了这份…...

《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)

CSI-2 协议详细解析 (一&#xff09; 1. CSI-2层定义&#xff08;CSI-2 Layer Definitions&#xff09; 分层结构 &#xff1a;CSI-2协议分为6层&#xff1a; 物理层&#xff08;PHY Layer&#xff09; &#xff1a; 定义电气特性、时钟机制和传输介质&#xff08;导线&#…...

Qt Widget类解析与代码注释

#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码&#xff0c;写上注释 当然可以&#xff01;这段代码是 Qt …...

SpringCloudGateway 自定义局部过滤器

场景&#xff1a; 将所有请求转化为同一路径请求&#xff08;方便穿网配置&#xff09;在请求头内标识原来路径&#xff0c;然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...

tree 树组件大数据卡顿问题优化

问题背景 项目中有用到树组件用来做文件目录&#xff0c;但是由于这个树组件的节点越来越多&#xff0c;导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多&#xff0c;导致的浏览器卡顿&#xff0c;这里很明显就需要用到虚拟列表的技术&…...

使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台

🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...

【生成模型】视频生成论文调研

工作清单 上游应用方向&#xff1a;控制、速度、时长、高动态、多主体驱动 类型工作基础模型WAN / WAN-VACE / HunyuanVideo控制条件轨迹控制ATI~镜头控制ReCamMaster~多主体驱动Phantom~音频驱动Let Them Talk: Audio-Driven Multi-Person Conversational Video Generation速…...

嵌入式学习笔记DAY33(网络编程——TCP)

一、网络架构 C/S &#xff08;client/server 客户端/服务器&#xff09;&#xff1a;由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序&#xff0c;负责提供用户界面和交互逻辑 &#xff0c;接收用户输入&#xff0c;向服务器发送请求&#xff0c;并展示服务…...

从面试角度回答Android中ContentProvider启动原理

Android中ContentProvider原理的面试角度解析&#xff0c;分为​​已启动​​和​​未启动​​两种场景&#xff1a; 一、ContentProvider已启动的情况 1. ​​核心流程​​ ​​触发条件​​&#xff1a;当其他组件&#xff08;如Activity、Service&#xff09;通过ContentR…...

上位机开发过程中的设计模式体会(1):工厂方法模式、单例模式和生成器模式

简介 在我的 QT/C 开发工作中&#xff0c;合理运用设计模式极大地提高了代码的可维护性和可扩展性。本文将分享我在实际项目中应用的三种创造型模式&#xff1a;工厂方法模式、单例模式和生成器模式。 1. 工厂模式 (Factory Pattern) 应用场景 在我的 QT 项目中曾经有一个需…...