欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

spring cloud config源码 client(一)

程序员文章站 2022-07-03 19:54:01
...

spring cloud config源码 client(一)

整体架构模块

spring cloud config源码 client(一)

client整体包大体类结构介绍

spring cloud config源码 client(一)

client入口我们先看spring.factories


# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.config.client.ConfigClientAutoConfiguration
# Bootstrap components
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.config.client.ConfigServiceBootstrapConfiguration,\
org.springframework.cloud.config.client.DiscoveryClientConfigServiceBootstrapConfiguration


这个文件是自动配置和启动相关的配置文件,spring.factories 仿造的是java的spi机制(这个不是重点)。我们来关注下这两个入口类。先看ConfigClientAutoConfiguration.

ConfigClientAutoConfiguration

整体代码如下:

/**
 * 这个类的主要作用就是配置好自身要拉去远程resource的相关信息,是否开启心跳,及是否开启刷新监控
 **/
/**
 * Expose a ConfigClientProperties just so that there is a way to inspect the properties
 * bound to it. It won't be available in time for autowiring into the bootstrap context,
 * but the values in this properties object will be the same as the ones used to bind to
 * the config server, if there is one.
 *
 * @author Dave Syer
 * @author Marcos Barbero
 *
 */
@Configuration
public class ConfigClientAutoConfiguration {

	/**
	 * 第一个是获取连接到Configserver的相关配置  比如label profile applicaitonname这些属性都在ConfigClientProperties
	 * 如果bootstrap上下文有的话从 bootstrap上下文中获取到这个配置(涉及到bootstrap 和applicaiton区别 ps:bootstrap.yaml是applcation的父上下文)
	 * @param environment
	 * @param context
	 * @return
	 */
	@Bean
	public ConfigClientProperties configClientProperties(Environment environment,
			ApplicationContext context) {
		if (context.getParent() != null
				&& BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
						context.getParent(), ConfigClientProperties.class).length > 0) {
			return BeanFactoryUtils.beanOfTypeIncludingAncestors(context.getParent(),
					ConfigClientProperties.class);
		}
		ConfigClientProperties client = new ConfigClientProperties(environment);
		return client;
	}

	/**
	 *  心跳检测相关的配置
	 * @return
	 */
	@Bean
	public ConfigClientHealthProperties configClientHealthProperties() {
		return new ConfigClientHealthProperties();
	}

	/**
	 *  如果引入了actuator的话这里会成立 心跳指示器
	 */
	@Configuration
	@ConditionalOnClass(HealthIndicator.class)
	@ConditionalOnBean(ConfigServicePropertySourceLocator.class)
	@ConditionalOnProperty(value = "health.config.enabled", matchIfMissing = true)
	protected static class ConfigServerHealthIndicatorConfiguration {

		@Bean
		public ConfigServerHealthIndicator configServerHealthIndicator(
				ConfigServicePropertySourceLocator locator,
				ConfigClientHealthProperties properties, Environment environment) {
			return new ConfigServerHealthIndicator(locator, environment, properties);
		}
	}

	@Configuration
	@ConditionalOnClass(ContextRefresher.class)
	@ConditionalOnBean(ContextRefresher.class)
	@ConditionalOnProperty(value = "spring.cloud.config.watch.enabled")
	protected static class ConfigClientWatchConfiguration {
		/**
		 * 起定时任务观察是否发生状态配置变化,如果发现了会调用RefreshScope.refreshAll 完成全量刷新 (带@RefreshScope注解的bean)
		 * config.client.state
		 * @param contextRefresher
		 * @return
		 */
		@Bean
		public ConfigClientWatch configClientWatch(ContextRefresher contextRefresher) {
			return new ConfigClientWatch(contextRefresher);
		}
	}

}

ConfigServiceBootstrapConfiguration

/**
 * 引导启动类
 * @author Dave Syer
 * @author Tristan Hanson
 *
 */
@Configuration
@EnableConfigurationProperties
public class ConfigServiceBootstrapConfiguration {

	@Autowired
	private ConfigurableEnvironment environment;

	/**
	 * 配置要获取到config的相关配置类 bootstrap对应的上下文配置爱configclientprop
	 * @return
	 */
	@Bean
	public ConfigClientProperties configClientProperties() {
		ConfigClientProperties client = new ConfigClientProperties(this.environment);
		return client;
	}

	/**
	 * configService ps定位类 重点拉取远程配置的类 我们在下方贴出重点代码
	 * @param properties
	 * @return
	 */
	@Bean
	@ConditionalOnMissingBean(ConfigServicePropertySourceLocator.class)
	@ConditionalOnProperty(value = "spring.cloud.config.enabled", matchIfMissing = true)
	public ConfigServicePropertySourceLocator configServicePropertySource(ConfigClientProperties properties) {
		//改类内部封装了重试获取远程配置的方法
		ConfigServicePropertySourceLocator locator = new ConfigServicePropertySourceLocator(
				properties);
		return locator;
	}

	@ConditionalOnProperty(value = "spring.cloud.config.fail-fast")
	@ConditionalOnClass({ Retryable.class, Aspect.class, AopAutoConfiguration.class })
	@Configuration
	@EnableRetry(proxyTargetClass = true)
	@Import(AopAutoConfiguration.class)
	@EnableConfigurationProperties(RetryProperties.class)
	protected static class RetryConfiguration {

		/**
		 * 重试拦截器 这里暂时不是重点指导是什么就好
		 * @param properties
		 * @return
		 */
		@Bean
		@ConditionalOnMissingBean(name = "configServerRetryInterceptor")
		public RetryOperationsInterceptor configServerRetryInterceptor(
				RetryProperties properties) {
			return RetryInterceptorBuilder
					.stateless()
					.backOffOptions(properties.getInitialInterval(),
							properties.getMultiplier(), properties.getMaxInterval())
					.maxAttempts(properties.getMaxAttempts()).build();
		}
	}

}



ConfigServicePropertySourceLocator相关的重点代码及注释 这个方法locate获取到配置数据:
大体逻辑是通过configclientprop组装 /{applicaiton}/{profile}/{label} +上configclientprop对应的configserver的uri,然后请求获取配置,拉取配置后存入CompositePropertySource名字是"configService")。(重点这里通过ConfgiClientProperties里面的uir作为homepage,在启用discoveryclient的时候会被替换);

@Override
	@Retryable(interceptor = "configServerRetryInterceptor")
	public org.springframework.core.env.PropertySource<?> locate(
			org.springframework.core.env.Environment environment) {
		//从环境中重新获取覆盖
		ConfigClientProperties properties = this.defaultProperties.override(environment);
		//创建composite 这个类持有一个链表的PropertySource 本身也是PropertySource的一个实现
		CompositePropertySource composite = new CompositePropertySource("configService");
		//请求模板 用于请求Remote
		RestTemplate restTemplate = this.restTemplate == null
				? getSecureRestTemplate(properties)
				: this.restTemplate;
		Exception error = null;
		String errorBody = null;
		try {
			String[] labels = new String[] { "" };
			//把lables 拆出来成数组
			if (StringUtils.hasText(properties.getLabel())) {
				labels = StringUtils
						.commaDelimitedListToStringArray(properties.getLabel());
			}
			String state = ConfigClientStateHolder.getState();
			// Try all the labels until one works (字面意思一个个请求过去直到获取到其中一个)
			for (String label : labels) {
				//获取到Environment(springcloud封装的) 从configserver获取到
				Environment result = getRemoteEnvironment(restTemplate, properties,
						label.trim(), state);
				if (result != null) {
					log(result);

					if (result.getPropertySources() != null) { // result.getPropertySources()
																// can be null if using
																// xml
						for (PropertySource source : result.getPropertySources()) {
							@SuppressWarnings("unchecked")
							Map<String, Object> map = (Map<String, Object>) source
									.getSource();
							composite.addPropertySource(
									new MapPropertySource(source.getName(), map));
						}
					}

					if (StringUtils.hasText(result.getState())
							|| StringUtils.hasText(result.getVersion())) {
						HashMap<String, Object> map = new HashMap<>();
						putValue(map, "config.client.state", result.getState());
						putValue(map, "config.client.version", result.getVersion());
						composite.addFirstPropertySource(
								new MapPropertySource("configClient", map));
					}
					return composite;
				}
			}
		}
		catch (HttpServerErrorException e) {
			error = e;
			if (MediaType.APPLICATION_JSON
					.includes(e.getResponseHeaders().getContentType())) {
				errorBody = e.getResponseBodyAsString();
			}
		}
		catch (Exception e) {
			error = e;
		}
		if (properties.isFailFast()) {
			throw new IllegalStateException(
					"Could not locate PropertySource and the fail fast property is set, failing" +
						(errorBody == null ? "" : ": " + errorBody), error);
		}
		logger.warn("Could not locate PropertySource: " + (errorBody == null
				? error == null ? "label not found" : error.getMessage()
				: errorBody));
		return null;

	}

uml图如下:
ConfigServerLocator uml:
spring cloud config源码 client(一)

接下来第三个引导类

DiscoveryClientConfigServiceBootstrapConfiguration

我们来看下面的注释及对应的代码

**
 * 配置客户端引导配置通过discovery来查找configserver,
 * Bootstrap configuration for a config client that wants to lookup the config server via
 * discovery.
 *
 * @author Dave Syer
 */
@ConditionalOnProperty(value = "spring.cloud.config.discovery.enabled", matchIfMissing = false)
@Configuration
@Import({ UtilAutoConfiguration.class })//主要引入两个类本地网络配置的两个类
@EnableDiscoveryClient
public class DiscoveryClientConfigServiceBootstrapConfiguration {

	private static Log logger = LogFactory
			.getLog(DiscoveryClientConfigServiceBootstrapConfiguration.class);

	@Autowired
	private ConfigClientProperties config; 

	@Autowired
	private ConfigServerInstanceProvider instanceProvider;

	private HeartbeatMonitor monitor = new HeartbeatMonitor();
	//通过注册中心 获取到configserver列表
	@Bean
	public ConfigServerInstanceProvider configServerInstanceProvider(  <1>
			DiscoveryClient discoveryClient) {
		return new ConfigServerInstanceProvider(discoveryClient);
	}

	/**
	 * 监听上下文刷新事件,远程通知的时候会被调用这个事件
	 * @param event
	 */
	@EventListener(ContextRefreshedEvent.class)
	public void startup(ContextRefreshedEvent event) {
		refresh();
	}

	/**
	 * 心跳检测事件
	 * @param event
	 */
	@EventListener(HeartbeatEvent.class)
	public void heartbeat(HeartbeatEvent event) {
		if (monitor.update(event.getValue())) {
			refresh();
		}
	}

	/**
	 * 刷新ConfigClientProperties对应的uri(configserver)  <2>
	 */
	private void refresh() {
		try {
			String serviceId = this.config.getDiscovery().getServiceId();
			List<String> listOfUrls = new ArrayList<>();
			List<ServiceInstance> serviceInstances = this.instanceProvider
					.getConfigServerInstances(serviceId);

			for (int i = 0; i < serviceInstances.size(); i++) {

				ServiceInstance server = serviceInstances.get(i);
				String url = getHomePage(server);

				if (server.getMetadata().containsKey("password")) {
					String user = server.getMetadata().get("user");
					user = user == null ? "user" : user;
					this.config.setUsername(user);
					String password = server.getMetadata().get("password");
					this.config.setPassword(password);
				}

				if (server.getMetadata().containsKey("configPath")) {
					String path = server.getMetadata().get("configPath");
					if (url.endsWith("/") && path.startsWith("/")) {
						url = url.substring(0, url.length() - 1);
					}
					url = url + path;
				}

				listOfUrls.add(url);
			}

			String[] uri = new String[listOfUrls.size()];
			uri = listOfUrls.toArray(uri);
			this.config.setUri(uri);

		}
		catch (Exception ex) {
			if (config.isFailFast()) {
				throw ex;
			}
			else {
				logger.warn("Could not locate configserver via discovery", ex);
			}
		}
	}

	private String getHomePage(ServiceInstance server) {
		return server.getUri().toString() + "/";
	}

}

主要配置说明:1.ConfigserverProvider 这里通过DiscoveryClient封装获取到ConfigServer对应的实例列表。
2.refresh在启动时被调用:调用的时候会将ConfigclientProperty这个对象里面的uri转换成现有的configserver实例对应的homepage “," 隔开的uri。这样通过

configclient的代码就上方这么点,如果要获取配置就可以通过ConfigServicePropertySourceLocator完成配置,本身这块代码也很简单,后续我们补充下cloud context是怎么通过这个类完成远程配置拉取的。

相关标签: springcloud config