gateway-nacosconfig动态配置路由原理详解
nacos动态配置路由Route原理解析我们知道,在gateway中,用RouteDefinitionLocator接口来存储配置RouteDefinition(可以从文件、内存、远程remote都可以获取到)用RouteLocator接口来创建Route对象。(不用想,那肯定是通过RouteDefinition来创建咯)RouteDefinitionLocator接口在GatewayAutoCo
nacos动态配置路由Route原理解析
我们知道,在gateway中,
- 用RouteDefinitionLocator接口来存储配置RouteDefinition(可以从文件、内存、远程remote都可以获取到)
- 用RouteLocator接口来创建Route对象。(不用想,那肯定是通过RouteDefinition来创建咯)
在GatewayAutoConfiguration自动配置类中,可以发现如下几个RouteDefinitionLocator接口实现
最后容器中存在就是CompositeRouteDefinitionLocator对象(也就是从PropertiesRouteDefinitionLocator和InMemoryRouteDefinitionRepository 读取所有的RouteDefinition)
@Bean
@ConditionalOnMissingBean
public PropertiesRouteDefinitionLocator propertiesRouteDefinitionLocator(
GatewayProperties properties) {
return new PropertiesRouteDefinitionLocator(properties);
}
@Bean
@ConditionalOnMissingBean(RouteDefinitionRepository.class)
public InMemoryRouteDefinitionRepository inMemoryRouteDefinitionRepository() {
return new InMemoryRouteDefinitionRepository();
}
@Bean
@Primary
public RouteDefinitionLocator routeDefinitionLocator(
List<RouteDefinitionLocator> routeDefinitionLocators) {
return new CompositeRouteDefinitionLocator(
Flux.fromIterable(routeDefinitionLocators));
}
-
RouteLocator接口
- 所以容器存在的就是CachingRouteLocator(CompositeRouteLocator(RouteDefinitionRouteLocator
- 装饰模式,其实最终工作work的基本就是最后一个,装饰模式,大家都懂得(跟mybatis的鸡肋二级缓存cache差不多)
- 而RouteDefinitionRouteLocator最终的创建Route,其实很明显最终就是从GatewayProperties中获取
@Bean
public RouteLocator routeDefinitionRouteLocator(GatewayProperties properties,
List<GatewayFilterFactory> gatewayFilters,
List<RoutePredicateFactory> predicates,
RouteDefinitionLocator routeDefinitionLocator,
ConfigurationService configurationService) {
return new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates,
gatewayFilters, properties, configurationService);
}
@Bean
@Primary
@ConditionalOnMissingBean(name = "cachedCompositeRouteLocator")
// TODO: property to disable composite?
public RouteLocator cachedCompositeRouteLocator(List<RouteLocator> routeLocators) {
return new CachingRouteLocator(
new CompositeRouteLocator(Flux.fromIterable(routeLocators)));
}
-
这里重要介绍一个CachingRouteLocator类,因为它是一个RefreshRoutesEvent的监听器
-
主要做的事:就是重新调用所有的RouteLocator,就是重新获取一次RouteDefinition并转换为Route对象。
(从最终的RouteDefinitionRouteLocator#convertToRoute方法可以看出。)疑问1 这有什么用呢?这个能动态刷新路由么?
回答:因为RouteDefinitionRouteLocator这个类,就是将配置RouteDefinition转换为Route对象。其实最终也就是获取的对象GatewayPropertie的routes属性。 只要我们保证GatewayPropertie属性能够重新设置绑定,就可以解决动态刷新路由
疑问1.1 那么既然有用,哪里会发布事件RefreshRoutesEvent呢?
难不成需要我们自己手动发布? 答案当然是否定的咯,接着往下看!(当然,你要手动发布,也可以。) 答案就是:RouteRefreshListener
在Gateway包中GatewayAutoConfiguration配置发现一个RouteRefreshListener类
- 它是一个支持很多事件的一个监听器,这里主要介绍RefreshScopeRefreshedEvent这个相关的事件
- 在事件处理中,它就会发布一个RefreshRoutesEvent事件
也就是说,只要容器中发布了一个RefreshScopeRefreshedEvent事件,就会发布一个RefreshRoutesEvent事件,
即CachingRouteLocator就会重新创建路由Route对象了。疑问2. 哪里会发布事件RefreshScopeRefreshedEvent呢?
答案就是:RefreshEventListener
通过发现,在springcloud包下的ConfigurationPropertiesRebinderAutoConfiguration这个自动配置类,找到一个ConfigurationPropertiesRebinder类
-
ConfigurationPropertiesRebinder作用是对标识了@ConfigurationProperties进行属性重新绑定的配置类。(为什么要重新绑定?说明spring环境中的属性,会发生变更的呗)
-
看代码,发现ConfigurationPropertiesRebinder,是一个EnvironmentChangeEvent的事件监听器
所以只要在容器中发布了一个EnvironmentChangeEvent事件,就可以重新绑定属性了,即上面的疑问1就可以解决了。疑问3: 哪里发布一个EnvironmentChangeEvent事件?
难不成需要我们自己手动发布? 答案当然是否定的咯,接着往下看!(当然,你要手动发布,也可以。) 答案就是:RefreshEventListener
不好意思,我在代码springcloud的RefreshAutoConfiguration 又发现了一个RefreshEventListener对象
- 它是一个SmartApplicationListener监听器,支持ApplicationReadyEvent和RefreshEvent两种事件的监听器
- ApplicationReadyEvent这个代表springboot应用已经启动事件,RefreshEvent代表刷新事件(设计之初,springcloud针对@RefreshScope作用域的bean刷新用的)
- 在RefreshEvent事件的处理中,调用ContextRefresher#refresh方法,并且详细可以发现
1. 在方法refreshEnvironment会发布一个EnvironmentChangeEvent事件
2. 在scope#refreshAll会发布一个RefreshScopeRefreshedEvent事件
3. 记住顺序,先EnvironmentChangeEvent,后RefreshScopeRefreshedEvent事件
public synchronized Set<String> refresh() {
Set<String> keys = refreshEnvironment();
this.scope.refreshAll();
return keys;
}
也就是说,只要容器中发布了一个RefreshEvent事件,就会随之而来一个EnvironmentChangeEvent事件,即GatewayPropertie属性就会重新绑定了。
疑问3:哪里发布一个RefreshEvent事件?
难不成需要我们自己手动发布? 答案当然是否定的咯,接着往下看!(当然,你要手动发布,也可以。)
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
既然我们利用nacosconfig来充当配置中,所以需要依赖对应的pom,然后我在NacosConfigAutoConfiguration配置类中发现了一个NacosContextRefresher
- 这个类主要做的功能:在容器启动之后,对nacosconfig中配置自动刷新为true的dataId,groupId的配置(即isRefreshable=true)添加一个监听器Listener。(ApplicationReadyEvent监听器)
- 这个Listener在接收到配置发现变更的进行receiveConfigInfo回调的时候,就会发布一个RefreshEvent事件(是不是很神奇,万事万物存在都是有价值的)
在nacosconfig的配置发生变更时候,nacosconfig监听器就会发布一个RefreshEvent事件
总结
- nacosconfig的监听器发布RefreshEvent
- RefreshEventListener(RefreshAutoconfiguration中)监听RefreshEvent ,发布如下两个事件
* EnvironmentChangeEvent(springcloud-context事件)
ConfigurationPropertiesRebinder监听EnvironmentChangeEvent事件,然后对容器中@ConfigurationProperties进行重新绑定(包括GatewayProperties也会被重新绑定)
* RefreshScopeRefreshedEvent(springcloud-context事件)
RouteRefreshListener(org.springframework.cloud.gateway包) 监听RefreshScopeRefreshedEvent之后,发布RefreshRoutesEvent - CachingRouteLocator,监听到RefreshRoutesEvent事件,然后重新对GatewayProperties路由进行转换
代码
- 说了这么多,最后贴上一份配置,就可以利用nacosconfig直接动态配置路由了,而不需要其他操作。
其实就是简单的配置中心的配置,也就是将配置从nacos获取到,配置
spring:
cloud:
nacos:
config:
group: middleware-gateway
name: route
serverAddr: ****:8848
#这个 file-extension:默认为properties后缀
file-extension: yaml
#默认就为true,这个是全局开关,如果这个为false, 则extensionConfigs和sharedConfigs里面的refresh即使为true也没有用了,也就是不支持动态刷新,即没有nacos的监听器了
refresh-enabled: true
更多推荐
所有评论(0)