聊聊Spring Cloud Gateway 动态路由及通过Apollo的实现

news2025/1/12 18:53:27

在之前我们了解的Spring Cloud Gateway配置路由方式有两种方式

1.通过配置文件

spring:
  cloud:
    gateway:
      routes:
        - id: test
          predicates:
            - Path=/ms/test/*
          filters:
            - StripPrefix=2
          uri: http://localhost:9000

2.通过JavaBean

    @Bean
    public RouteLocator routeLocator(RouteLocatorBuilder builder) {
        return builder.routes()
                .route(r -> r.path("/ms/test/**")
                .filters(f -> f.stripPrefix(2))
                .uri("http://localhost:9000"))
                .build();
    }

但是遗憾的是这两种方式都不支持动态路由,都需要重启服务。 所以我们需要对Spring Cloud Gateway进行改造,在改造的时候我们就需要看看源码了解下Spring Cloud Gateway的路由加载

路由的加载

我们之前分析了路由的加载主要在GatewayAutoConfiguration的 routeDefinitionRouteLocator方法加载的

实际上最终获取的路由信息都是在GatewayProperties这个配置类中

所以我们在动态路由的时候修改GatewayProperties中的属性即可,即

  1. List<RouteDefinition> routes

  2. List<FilterDefinition> defaultFilters

恰巧Spring Cloud Gateway也提供了相应的get、set方法

​实际如果我们修改了该属性我们会发现并不会立即生效,因为我们会发现还有一个RouteLocator就是CachingRouteLocator,并且在配置Bean的时候加了注解@Primary,说明最后使用额RouteLocator实际是CachingRouteLocator

CachingRouteLocator最后还是使用 RouteDefinitionRouteLocator类加载的,也是就我们上面分析的,看CachingRouteLocator就知道是缓存作用

这里引用网上一张加载图片

参考 www.jianshu.com/p/490739b18…

所以看到这里我们知道我们还需要解决的一个问题就是更新缓存,如何刷新缓存呢,这里Spring Cloud Gateway利用spring的事件机制给我提供了扩展

所以我们要做的事情就是这两件事:

  1. GatewayProperties

  2. 刷新缓存

实现动态路由

这里代码参考 github.com/apolloconfi…

@Component
@Slf4j
public class GatewayPropertiesRefresher implements ApplicationContextAware, ApplicationEventPublisherAware {
	
	private static final String ID_PATTERN = "spring\\.cloud\\.gateway\\.routes\\[\\d+\\]\\.id";

	private static final String DEFAULT_FILTER_PATTERN = "spring\\.cloud\\.gateway\\.default-filters\\[\\d+\\]\\.name";

	private ApplicationContext applicationContext;

	private ApplicationEventPublisher publisher;

	@Autowired
	private GatewayProperties gatewayProperties;

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.applicationContext = applicationContext;
	}


	@Override
	public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
		this.publisher = applicationEventPublisher;
	}

	@ApolloConfigChangeListener(value = "route.yml",interestedKeyPrefixes = "spring.cloud.gateway.")
	public void onChange(ConfigChangeEvent changeEvent) {
		refreshGatewayProperties(changeEvent);
	}

	/***
	 * 刷新org.springframework.cloud.gateway.config.PropertiesRouteDefinitionLocator中定义的routes
	 *
	 * @param changeEvent
	 * @return void
	 * @author ksewen
	 * @date 2019/5/21 2:13 PM
	 */
	private void refreshGatewayProperties(ConfigChangeEvent changeEvent) {
		log.info("Refreshing GatewayProperties!");
		preDestroyGatewayProperties(changeEvent);
		this.applicationContext.publishEvent(new EnvironmentChangeEvent(changeEvent.changedKeys()));
		refreshGatewayRouteDefinition();
		log.info("GatewayProperties refreshed!");
	}

	/***
	 * GatewayProperties没有@PreDestroy和destroy方法
	 * org.springframework.cloud.context.properties.ConfigurationPropertiesRebinder#rebind(java.lang.String)中destroyBean时不会销毁当前对象
	 * 如果把spring.cloud.gateway.前缀的配置项全部删除(例如需要动态删除最后一个路由的场景),initializeBean时也无法创建新的bean,则return当前bean
	 * 若仍保留有spring.cloud.gateway.routes[n]或spring.cloud.gateway.default-filters[n]等配置,initializeBean时会注入新的属性替换已有的bean
	 * 这个方法提供了类似@PreDestroy的操作,根据配置文件的实际情况把org.springframework.cloud.gateway.config.GatewayProperties#routes
	 * 和org.springframework.cloud.gateway.config.GatewayProperties#defaultFilters两个集合清空
	 *
	 * @param
	 * @return void
	 * @author ksewen
	 * @date 2019/5/21 2:13 PM
	 */
	private synchronized void preDestroyGatewayProperties(ConfigChangeEvent changeEvent) {
		log.info("Pre Destroy GatewayProperties!");
		final boolean needClearRoutes = this.checkNeedClear(changeEvent, ID_PATTERN, this.gatewayProperties.getRoutes()
				.size());
		if (needClearRoutes) {
			this.gatewayProperties.setRoutes(new ArrayList<>());
		}
		final boolean needClearDefaultFilters = this.checkNeedClear(changeEvent, DEFAULT_FILTER_PATTERN, this.gatewayProperties.getDefaultFilters()
				.size());
		if (needClearDefaultFilters) {
			this.gatewayProperties.setDefaultFilters(new ArrayList<>());
		}
		log.info("Pre Destroy GatewayProperties finished!");
	}

	private void refreshGatewayRouteDefinition() {
		log.info("Refreshing Gateway RouteDefinition!");
		this.publisher.publishEvent(new RefreshRoutesEvent(this));
		log.info("Gateway RouteDefinition refreshed!");
	}

	/***
	 * 根据changeEvent和定义的pattern匹配key,如果所有对应PropertyChangeType为DELETED则需要清空GatewayProperties里相关集合
	 *
	 * @param changeEvent
	 * @param pattern
	 * @param existSize
	 * @return boolean
	 * @author ksewen
	 * @date 2019/5/23 2:18 PM
	 */
	private boolean checkNeedClear(ConfigChangeEvent changeEvent, String pattern, int existSize) {
		return changeEvent.changedKeys().stream().filter(key -> key.matches(pattern))
				.filter(key -> {
					ConfigChange change = changeEvent.getChange(key);
					return PropertyChangeType.DELETED.equals(change.getChangeType());
				}).count() == existSize;
	}
}

然后我们在apollo添加namespace:route.yml

配置内容如下:

spring:
  cloud:
    gateway:
      routes:
        - id: test
          predicates:
            - Path=/ms/test/*
          filters:
            - StripPrefix=2
          uri: http://localhost:9000

然后我们可以通过访问地址: http:localhost:8080/ms/test/health

看删除后是否是404,加上后是否可以正常动态路由

值得注意的是上面@ ApolloConfigChangeListener中如果没有添加新的namespace,value可以不用填写,如果配置文件是yml配置文件,在监听的时候需要指定文件后缀

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

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

相关文章

最新科目一攻略(新规)

一、*新规题 1、学法减分学习和满分教育 学法减分学习网上学习3日内累计满30分钟且考试合格&#xff0c;一次扣减1分现场学习满1小时且考试合格&#xff0c;一次扣减2分参加组织的交通安全公益活动的&#xff0c;满1小时&#xff0c;一次扣减1分【易考】饮酒后受过处罚&#xf…

[内排序]八大经典排序合集

文章目录1 排序的基本概念1.1 什么是排序1.2 排序的稳定性1.3 内排序和外排序2 插入排序2.1 直接插入排序1. 排序思路2. 直接插入排序实例3. 排序算法4. 算法分析5. 折半插入排序 / 二分插入排序5.1 排序思路5.2 排序算法5.3 算法分析2.2 希尔排序1. 排序思路2. 希尔排序实例3.…

免费查题接口

免费查题接口 本平台优点&#xff1a; 多题库查题、独立后台、响应速度快、全网平台可查、功能最全&#xff01; 1.想要给自己的公众号获得查题接口&#xff0c;只需要两步&#xff01; 2.题库&#xff1a; 查题校园题库&#xff1a;查题校园题库后台&#xff08;点击跳转&a…

NodeJs实战-待办列表(7)-connect组件简化代码

NodeJs实战-待办列表7-connect组件简化代码什么是connectconnect demo 程序conenct 应用到服务端验证添加完成什么是connect connect demo 程序 安装 conncet、connect-query 组件 npm install connect npm install connect-query编写 demo 程序&#xff0c;保存到 test_conn…

阿里高工内产的 SpringBoot 实战派手册仅发布一天霸榜Github

近年来&#xff0c;Spring Boot 是整个Java社区中最有影响力的项目之一&#xff0c;常常被人看作是Java EE( Java Platform Enterprise Edition )开发的颠覆者&#xff0c;它将逐渐替代传统SSM ( Java EE互联网轻量级框架整合开发——Spring MvCSpringMyBatis&#xff09;架构。…

低代码平台的核心价值与优势

数字化时代的到来&#xff0c;迫使企业跳出舒适圈&#xff0c;坚定地踏上数字化转型的征程。不断飙升的用户需求&#xff0c;加上专业开发人员的显著缺口&#xff0c;让我们不得不承认&#xff0c;过去几十年的应用开发方式已经无法满足需求。低代码革命已经悄然开始&#xff0…

智慧防汛解决方案-最新全套文件

智慧防汛解决方案-最新全套文件一、建设背景行业痛点&#xff1a;1、家底不清&#xff0c;责权不分2、状态不明难以监管3、内外业脱节4、主观防涝二、建设思路面临的挑战&#xff1a;三、建设方案四、获取 - 智慧防汛全套最新解决方案合集一、建设背景 随着城市的快速发展&…

数据库的一级、二级、三级封锁协议

0、内容补充 X锁&#xff08;排他锁、写锁&#xff09; S锁&#xff08;共享锁、读锁&#xff09; 一、一级封锁协议 一级封锁协议是指&#xff0c;事务T在修改数据R之前必须先对其加X锁&#xff0c;直到事务结束才释放。事务结束包括正常结束&#xff08;COMMIT&#xff09;…

Kotlin Flow啊,你将流向何方?

前言 前边一系列的协程文章铺垫了很久&#xff0c;终于要分析Flow了。如果说协程是Kotlin的精华&#xff0c;那么Flow就是协程的精髓。 通过本篇文章&#xff0c;你将了解到&#xff1a; 什么是流&#xff1f;为什么引进Flow?Fow常见的操作为什么说Flow是冷流? 1. 什么是流 …

你必须要了解的国产数据库——OceanBase

文章目录前言1、什么是OceanBase&#xff1f;2、OceanBase名字的由来3、OceanBase 发展历程4、OceanBase优势5、OceanBase的核心特性6、应用场景7、未来展望前言 一直以来&#xff0c;外国企业在数据库领域保持高市占率&#xff0c;主流的数据库系统大多数都是使用外国的产品。…

读文章笔记-OSSFS-DD

读文章笔记-OSSFS-DD文章代码文章主要贡献内容公式(5)(6)的详细推倒对比其他7种算法算法思考文章 Online Scalable Streaming Feature Selection via Dynamic Decision 代码 https://github.com/doodzhou/OSFS 文章主要贡献 将特征分成选择&#xff0c;丢弃和延迟。提出基…

《第一行代码》核心知识点:Android的脸面之UI控件

《第一行代码》核心知识点&#xff1a;Android的脸面之UI控件前言三、Android的脸面之UI控件3.1 常用控件介绍3.1.1 TextView3.1.2 Button3.1.3 EditText3.1.4 ImageView3.1.5 ProgressBar3.1.6 AlertDialog3.1.7 ProgressDialog3.2 四种基本布局介绍3.2.1 线性布局3.2.2 相对布…

更强悍 更智能!飞凌嵌入式FET3588-C核心板震撼发布!

为提升AIoT领域终端设备的处理能力以及智能化程度&#xff0c;飞凌嵌入式与瑞芯微强强联手&#xff0c;正式推出FET3588-C核心板&#xff01; 1、国芯之光 品质之选 FET3588-C核心板基于Rockchip新一代旗舰RK3588处理器开发设计&#xff0c;采用先进的8nm制程工艺&#xff0c; …

【软考软件评测师】第三十一章 操作系统综合知识

【软考软件评测师】第三十一章 操作系统综合知识 第三十一章 操作系统综合知识【软考软件评测师】第三十一章 操作系统综合知识第一部分 知识点集锦1.嵌入式操作系统1&#xff09;可裁剪性2&#xff09;实时性3&#xff09;统一的接口4&#xff09;弱交互性5&#xff09;更好的…

EasyExcel使用详解与源码解读

EasyExcel使用详解1、EasyExcel简单介绍64M内存20秒读取75M(46W行25列)的Excel&#xff08;3.0.2版本&#xff09;2、EasyExcel和POI数据处理能力对比3、使用EasyExcel读写Excel、web上传/下载3.1、写EXCEL3.2、读EXCEL3.3、web上传、下载4、EasyExcel源码解读5、总结1、EasyEx…

AdsPower VS Dolphin,哪款指纹浏览器性价比更高?

现在指纹浏览器已经成为了跨境人必备的账号安全工具&#xff0c;市面上的指纹浏览器也非常多&#xff0c;但其实真正专业的寥寥无几。现在市场上比较出名的浏览器有AdsPower、Dolphin、multilogin。为了比较出AdsPower和Dolphin功能上的差异&#xff0c;龙哥特地去下载使用了这…

三、nginx设置浏览器缓存[expires]

一、浏览器缓存 解释&#xff1a;浏览器缓存极大缓解了带宽压力&#xff0c;提高了用户体验。 访问页面-查看是否有缓存&#xff0c;如果没有过期&#xff0c;直接击中缓存&#xff0c;不向服务器发送请求访问页面-查看是否有缓存&#xff0c;如果过期&#xff0c;向服务器发送…

如何给async await批量添加try…catch?

如何给async await批量添加try…catch&#xff1f;async和await是什么&#xff1f;如何给async await批量添加try…catchbabel插件的实现思路插件的基本格式示例寻找await节点向上查找 async 函数添加用户选项babel插件的安装使用其他参考资料async和await是什么&#xff1f; …

深紫色粉末BHQ-1 NHS,916753-61-2,NHS修饰是合成后与一个伯氨基的共轭

英文名称&#xff1a;BHQ-1 NHS CAS&#xff1a;916753-61-2 外观&#xff1a;深紫色粉末 分子式&#xff1a;C30H31N7O7 分子量&#xff1a;601.62 储存条件&#xff1a;-20C&#xff0c;避光避湿 结构式&#xff1a; 凯新生物产品简介&#xff1a; NHS修饰是合成后与一…

程序员必知必会网络传输之TCP/IP协议族,共864页的详解文档让你原地起飞!

我们现实网络无处不在&#xff0c;我们被庞大的虚拟网络包围&#xff0c;但我们却对它是怎样把我们的信息传递并实现通信的&#xff0c;我们并没有了解过&#xff0c;那么当我们在浏览器中出入一段地址&#xff0c;按下回车这背后都会发生什么&#xff1f; 比如说一般场景下&a…