gateway-nacosconfig动态配置路由原理详解

nacos动态配置路由Route原理解析

我们知道,在gateway中,

  • 用RouteDefinitionLocator接口来存储配置RouteDefinition(可以从文件、内存、远程remote都可以获取到)
  • 用RouteLocator接口来创建Route对象。(不用想,那肯定是通过RouteDefinition来创建咯)
  1. RouteDefinitionLocator接口

在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));
	}
  1. 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)));
	}
  1. CachingRouteLocator

  • 这里重要介绍一个CachingRouteLocator类,因为它是一个RefreshRoutesEvent的监听器

  • 主要做的事:就是重新调用所有的RouteLocator,就是重新获取一次RouteDefinition并转换为Route对象。
    (从最终的RouteDefinitionRouteLocator#convertToRoute方法可以看出。)

    疑问1 这有什么用呢?这个能动态刷新路由么?

    回答:因为RouteDefinitionRouteLocator这个类,就是将配置RouteDefinition转换为Route对象。其实最终也就是获取的对象GatewayPropertie的routes属性。 只要我们保证GatewayPropertie属性能够重新设置绑定,就可以解决动态刷新路由

    疑问1.1 那么既然有用,哪里会发布事件RefreshRoutesEvent呢?

      难不成需要我们自己手动发布? 答案当然是否定的咯,接着往下看!(当然,你要手动发布,也可以。)
      答案就是:RouteRefreshListener
    
  1. RouteRefreshListener

在Gateway包中GatewayAutoConfiguration配置发现一个RouteRefreshListener类

  • 它是一个支持很多事件的一个监听器,这里主要介绍RefreshScopeRefreshedEvent这个相关的事件
  • 在事件处理中,它就会发布一个RefreshRoutesEvent事件
    也就是说,只要容器中发布了一个RefreshScopeRefreshedEvent事件,就会发布一个RefreshRoutesEvent事件,
    即CachingRouteLocator就会重新创建路由Route对象了。

    疑问2. 哪里会发布事件RefreshScopeRefreshedEvent呢?

    答案就是:RefreshEventListener
  1. ConfigurationPropertiesRebinder

通过发现,在springcloud包下的ConfigurationPropertiesRebinderAutoConfiguration这个自动配置类,找到一个ConfigurationPropertiesRebinder类

  • ConfigurationPropertiesRebinder作用是对标识了@ConfigurationProperties进行属性重新绑定的配置类。(为什么要重新绑定?说明spring环境中的属性,会发生变更的呗)

  • 看代码,发现ConfigurationPropertiesRebinder,是一个EnvironmentChangeEvent的事件监听器

    所以只要在容器中发布了一个EnvironmentChangeEvent事件,就可以重新绑定属性了,即上面的疑问1就可以解决了。

    疑问3: 哪里发布一个EnvironmentChangeEvent事件?

    难不成需要我们自己手动发布? 答案当然是否定的咯,接着往下看!(当然,你要手动发布,也可以。)
     答案就是:RefreshEventListener
    
  1. 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事件?

  难不成需要我们自己手动发布? 答案当然是否定的咯,接着往下看!(当然,你要手动发布,也可以。)
  1. NacosContextRefresher

		<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事件

总结

  1. nacosconfig的监听器发布RefreshEvent
  2. RefreshEventListener(RefreshAutoconfiguration中)监听RefreshEvent ,发布如下两个事件
    * EnvironmentChangeEvent(springcloud-context事件)
           ConfigurationPropertiesRebinder监听EnvironmentChangeEvent事件,然后对容器中@ConfigurationProperties进行重新绑定(包括GatewayProperties也会被重新绑定)
    * RefreshScopeRefreshedEvent(springcloud-context事件)
            RouteRefreshListener(org.springframework.cloud.gateway包) 监听RefreshScopeRefreshedEvent之后,发布RefreshRoutesEvent
  3. 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 
     
  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值