文档章节

聊聊Spring Cloud Config的ConfigClientWatch

go4it
 go4it
发布于 2017/07/22 23:10
字数 786
阅读 37
收藏 1

Spring Cloud Config提供了一个ConfigClientWatch功能,可以定时轮询客户端配置的状态,如果状态发生变化,则refresh。

配置文件

spring:
  cloud:
    config:
      uri: http://localhost:8888
      watch:
        enabled: true
        initialDelay: 5000 ##default 180000 ms
        delay: 10000 ##default 500 ms

配置类

spring-cloud-config-client-1.3.1.RELEASE-sources.jar!/org/springframework/cloud/config/client/ConfigClientAutoConfiguration.java

@Configuration
	@ConditionalOnClass(ContextRefresher.class)
	@ConditionalOnBean(ContextRefresher.class)
	@ConditionalOnProperty(value = "spring.cloud.config.watch.enabled")
	protected static class ConfigClientWatchConfiguration {

		@Bean
		public ConfigClientWatch configClientWatch(ContextRefresher contextRefresher) {
			return new ConfigClientWatch(contextRefresher);
		}
	}

ConfigClientWatch

public class ConfigClientWatch implements Closeable, EnvironmentAware {

	private static Log log = LogFactory
			.getLog(ConfigServicePropertySourceLocator.class);

	private final AtomicBoolean running = new AtomicBoolean(false);
	private final ContextRefresher refresher;
	private Environment environment;

	public ConfigClientWatch(ContextRefresher refresher) {
		this.refresher = refresher;
	}

	@Override
	public void setEnvironment(Environment environment) {
		this.environment = environment;
	}

	@PostConstruct
	public void start() {
		this.running.compareAndSet(false, true);
	}

	@Scheduled(initialDelayString = "${spring.cloud.config.watch.initialDelay:180000}", fixedDelayString = "${spring.cloud.config.watch.delay:500}")
	public void watchConfigServer() {
		if (this.running.get()) {
			String newState = this.environment.getProperty("config.client.state");
            String oldState = ConfigClientStateHolder.getState();

			// only refresh if state has changed
			if (stateChanged(oldState, newState)) {
				ConfigClientStateHolder.setState(newState);
				this.refresher.refresh();
			}
		}
	}

	/* for testing */ boolean stateChanged(String oldState, String newState) {
		return (!hasText(oldState) && hasText(newState))
                || (hasText(oldState) && !oldState.equals(newState));
	}

	@Override
	public void close() {
		this.running.compareAndSet(true, false);
	}

}

依赖config.client.state的环境变量,来判断client端配置文件的状态 依赖ContextRefresher去刷新配置/实例

ContextRefresher

spring-cloud-context-1.2.2.RELEASE-sources.jar!/org/springframework/cloud/context/refresh/ContextRefresher.java

public synchronized Set<String> refresh() {
		Map<String, Object> before = extract(
				this.context.getEnvironment().getPropertySources());
		addConfigFilesToEnvironment();
		Set<String> keys = changes(before,
				extract(this.context.getEnvironment().getPropertySources())).keySet();
		this.context.publishEvent(new EnvironmentChangeEvent(keys));
		this.scope.refreshAll();
		return keys;
	}

这个refresh主要做两件事情:

  • 第一件是发布EnvironmentChangeEvent事件
  • 第二件是调用RefreshScope的refreshAll方法

EnvironmentChangeEvent的listener

spring-cloud-context-1.2.2.RELEASE-sources.jar!/org/springframework/cloud/context/properties/ConfigurationPropertiesRebinder.java

@Component
@ManagedResource
public class ConfigurationPropertiesRebinder
		implements ApplicationContextAware, ApplicationListener<EnvironmentChangeEvent> {

	private ConfigurationPropertiesBeans beans;

	private ConfigurationPropertiesBindingPostProcessor binder;

	private ApplicationContext applicationContext;

	private Map<String, Exception> errors = new ConcurrentHashMap<>();

	public ConfigurationPropertiesRebinder(
			ConfigurationPropertiesBindingPostProcessor binder,
			ConfigurationPropertiesBeans beans) {
		this.binder = binder;
		this.beans = beans;
	}

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

	/**
	 * A map of bean name to errors when instantiating the bean.
	 *
	 * @return the errors accumulated since the latest destroy
	 */
	public Map<String, Exception> getErrors() {
		return this.errors;
	}

	@ManagedOperation
	public void rebind() {
		this.errors.clear();
		for (String name : this.beans.getBeanNames()) {
			rebind(name);
		}
	}

	@ManagedOperation
	public boolean rebind(String name) {
		if (!this.beans.getBeanNames().contains(name)) {
			return false;
		}
		if (this.applicationContext != null) {
			try {
				Object bean = this.applicationContext.getBean(name);
				if (AopUtils.isCglibProxy(bean)) {
					bean = getTargetObject(bean);
				}
				this.binder.postProcessBeforeInitialization(bean, name);
				this.applicationContext.getAutowireCapableBeanFactory()
						.initializeBean(bean, name);
				return true;
			}
			catch (RuntimeException e) {
				this.errors.put(name, e);
				throw e;
			}
		}
		return false;
	}

	@SuppressWarnings("unchecked")
	private static <T> T getTargetObject(Object candidate) {
		try {
			if (AopUtils.isAopProxy(candidate) && (candidate instanceof Advised)) {
				return (T) ((Advised) candidate).getTargetSource().getTarget();
			}
		}
		catch (Exception ex) {
			throw new IllegalStateException("Failed to unwrap proxied object", ex);
		}
		return (T) candidate;
	}

	@ManagedAttribute
	public Set<String> getBeanNames() {
		return new HashSet<String>(this.beans.getBeanNames());
	}

	@Override
	public void onApplicationEvent(EnvironmentChangeEvent event) {
		rebind();
	}

}

RefreshScope的refreshAll

spring-cloud-context-1.2.2.RELEASE-sources.jar!/org/springframework/cloud/context/scope/refresh/RefreshScope.java

@ManagedOperation(description = "Dispose of the current instance of all beans in this scope and force a refresh on next method execution.")
	public void refreshAll() {
		super.destroy();
		this.context.publishEvent(new RefreshScopeRefreshedEvent());
	}

发布RefreshScopeRefreshedEvent事件

RefreshScopeRefreshedEvent事件listener

spring-cloud-netflix-eureka-client-1.3.1.RELEASE-sources.jar!/org/springframework/cloud/netflix/eureka/EurekaDiscoveryClientConfiguration.java

@Configuration
	@ConditionalOnClass(RefreshScopeRefreshedEvent.class)
	protected static class EurekaClientConfigurationRefresher {

		@Autowired(required = false)
		private EurekaClient eurekaClient;

		@Autowired(required = false)
		private EurekaAutoServiceRegistration autoRegistration;

		@EventListener(RefreshScopeRefreshedEvent.class)
		public void onApplicationEvent(RefreshScopeRefreshedEvent event) {
			//This will force the creation of the EurkaClient bean if not already created
			//to make sure the client will be reregistered after a refresh event
			if(eurekaClient != null) {
				eurekaClient.getApplications();
			}
			if (autoRegistration != null) {
				// register in case meta data changed
				this.autoRegistration.stop();
				this.autoRegistration.start();
			}
		}
	}

spring-cloud-netflix-core-1.2.6.RELEASE-sources.jar!/org/springframework/cloud/netflix/zuul/ZuulConfiguration.java

    @Bean
	public ApplicationListener<ApplicationEvent> zuulRefreshRoutesListener() {
		return new ZuulRefreshListener();
	}
private static class ZuulRefreshListener
			implements ApplicationListener<ApplicationEvent> {

		@Autowired
		private ZuulHandlerMapping zuulHandlerMapping;

		private HeartbeatMonitor heartbeatMonitor = new HeartbeatMonitor();

		@Override
		public void onApplicationEvent(ApplicationEvent event) {
			if (event instanceof ContextRefreshedEvent
					|| event instanceof RefreshScopeRefreshedEvent
					|| event instanceof RoutesRefreshedEvent) {
				this.zuulHandlerMapping.setDirty(true);
			}
			else if (event instanceof HeartbeatEvent) {
				if (this.heartbeatMonitor.update(((HeartbeatEvent) event).getValue())) {
					this.zuulHandlerMapping.setDirty(true);
				}
			}
		}

	}

小结

Spring Cloud Config的代码是有提供ConfigClientWatch,但是实际单纯使用git作为config server的时候,拉取配置的时候得到的state始终是null,因此客户端轮询是起不到刷新效果的。第二个就是这个refresh发布的RefreshScopeRefreshedEvent,eureka会先去更新注册信息为DOWN,然后再UP起来,这个频繁操作有点风险。

© 著作权归作者所有

共有 人打赏支持
go4it
粉丝 63
博文 735
码字总数 528939
作品 0
深圳
聊聊spring cloud gateway的GatewayFilter

序 本文主要研究一下spring cloud gateway的GatewayFilter GatewayFilter spring-cloud-gateway-core-2.0.0.RC2-sources.jar!/org/springframework/cloud/gateway/filter/GatewayFilter.jav......

go4it
06/09
0
0
聊聊ribbon的超时时间设置

序 本文主要研究一下ribbon的超时时间设置 配置 实例 RibbonClientConfiguration spring-cloud-netflix-ribbon-2.0.0.RELEASE-sources.jar!/org/springframework/cloud/netflix/ribbon/Ribb......

go4it
07/20
0
0
聊聊spring cloud的RequestHeaderToRequestUri

序 本文主要研究一下spring cloud的RequestHeaderToRequestUriGatewayFilter GatewayAutoConfiguration spring-cloud-gateway-core-2.0.0.RC2-sources.jar!/org/springframework/cloud/gate......

go4it
06/16
0
0
聊聊spring cloud gateway的RedirectToGatewayFilter

序 本文主要研究下spring cloud gateway的RedirectToGatewayFilter GatewayAutoConfiguration spring-cloud-gateway-core-2.0.0.RC2-sources.jar!/org/springframework/cloud/gateway/confi......

go4it
06/13
0
0
聊聊spring cloud gateway的SetStatusGatewayFilter

序 本文主要研究下spring cloud gateway的SetStatusGatewayFilter GatewayAutoConfiguration spring-cloud-gateway-core-2.0.0.RC2-sources.jar!/org/springframework/cloud/gateway/config......

go4it
06/15
0
0

没有更多内容

加载失败,请刷新页面

加载更多

【挑战剑指offer】系列03:逆序打印单链表

本系列的算法原题来自于“牛客网-剑指offer”,写这个板块,不仅仅是解决算法问题本身,更是手动提高难度、自行变式,思考更多的解决方案,以带给自己一些启发。 1. 【逆序打印单链表】原始题...

LinkedBear
10分钟前
1
0
Linux内存布局

今天这篇文章主要是我之前看Linux内核相关知识和博客Gustavo Duarte中。我主要是看了这篇博客,并且结合之前的知识,对内存管理的的理解又上升了一个档次。所以想通过这篇文章总结下。 我们先...

linuxprobe16
29分钟前
1
0
day94-20180921-英语流利阅读-待学习

记录死亡还是消费死者?自杀报道的媒体偏见 雪梨 2018-09-21 1.今日导读 自杀事件报道一直是新闻报道的重要部分,具有骇人听闻、吸引眼球的特点。可是在报道这些事件的时候,除了客观陈述事实...

飞鱼说编程
35分钟前
3
0
如何通过 J2Cache 实现分布式 session 存储

做 Java Web 开发的人多数都会需要使用到 session (会话),我们使用 session 来保存一些需要在两个不同的请求之间共享数据。一般 Java 的 Web 容器像 Tomcat、Resin、Jetty 等等,它们会在...

红薯
今天
3
0
C++ std::thread

C++11提供了std::thread类来表示一个多线程对象。 1,首先介绍一下std::this_thread命名空间: (1)std::this_thread::get_id():返回当前线程id (2)std::this_thread::yield():用户接口...

yepanl
今天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部