SpringCloud\u6e90\u7801\u89e3\u6790 — RestTemplate\u4e0e@LoadBalanced

SpringCloud\u6e90\u7801\u89e3\u6790 — Spring Cloud Config\u4e0e@RefreshScope

SpringCloud\u6e90\u7801\u89e3\u6790 — Zuul\u5b9e\u73b0\u539f\u7406

SpringCloud\u6e90\u7801\u89e3\u6790 — Spring Cloud Sleuth\u539f\u7406\u63a2\u7a76

SpringCloud\u6e90\u7801\u89e3\u6790 — Eureka\u539f\u7406\u63a2\u7a76

This article shares the implementation principles of Spring Cloud Config and @RefreshScope by reading the source code. Source code analysis based on Spring Cloud Hoxton

Before reading this article, it is best to know the Environment, PropertySources, refer to SpringBoot source code parsing, Logging, Environment boot

Config Server

We all know that SpringCloud Config Server can be accessed via a URL to retrieve configuration content once it is started. In this case, there should be Controller in Config Server, that is, EnvironmentController.

The HTTP interface provided by EnvironmentController returns all the Environment, and the real configuration is stored in Environment#PropertySources. The EnvironmentController gets the corresponding Environment using EnvironmentRepository#findOne. EnvironmentRepository is an Environment repository that can fetch configuration content from different configuration centers, such as JDBC, SVN, GIT, and so on.

Is used in the EnvironmentController EnvironmentEncryptorEnvironmentRepository, it will be to decrypt the content of the Environment, EnvironmentController EnvironmentEncryptorEnvironmentRepository# delegate is CompositeEnvironmentRepository, obviously, the combination of class, Among them the real processing class is MultipleJGitEnvironmentRepository, he can manage multiple git configuration center, actual through JGitEnvironmentRepository access to a certain git configuration center on the configuration of the content.

JGitEnvironmentRepository#findOne -> AbstractScmEnvironmentRepository#findOne

public synchronized Environment findOne(String application, String profile,
		String label, boolean includeOrigin) {
	NativeEnvironmentRepository delegate = new NativeEnvironmentRepository(
			getEnvironment(), new NativeEnvironmentProperties());
	// #1
	Locations locations = getLocations(application, profile, label);	
	delegate.setSearchLocations(locations.getLocations());
	// #2
	Environment result = delegate.findOne(application, profile, "", includeOrigin);	
	result.setVersion(locations.getVersion());
	result.setLabel(label);
	return this.cleaner.clean(result, getWorkingDirectory().toURI().toString(),
			getUri());
}
Copy the code

# 1, by a subclass implementation getLocations will pull the center of the configuration content, saved as a local file, and return the local path # 2 by NativeEnvironmentRepository, producing Environment

JGitEnvironmentRepository#getLocations -> refresh

public String refresh(String label) { Git git = null; try { git = createGitClient(); // #1 if (shouldPull(git)) { FetchResult fetchStatus = fetch(git, label); if (this.deleteUntrackedBranches && fetchStatus ! = null) { deleteUntrackedLocalBranches(fetchStatus.getTrackingRefUpdates(), git); } checkout(git, label); tryMerge(git, label); }... return git.getRepository().findRef("HEAD").getObjectId().getName(); }... }Copy the code

#1 Refresh determines if fetch is required and performs checkout, merge, and so on

Back to AbstractScmEnvironmentRepository# findOne method # 2 steps, see NativeEnvironmentRepository# findOne

public Environment findOne(String config, String profile, String label, boolean includeOrigin) { // #1 SpringApplicationBuilder builder = new SpringApplicationBuilder( PropertyPlaceholderAutoConfiguration.class); // #2 ConfigurableEnvironment environment = getEnvironment(profile); builder.environment(environment); // #3 builder.web(WebApplicationType.NONE).bannerMode(Mode.OFF); . // #4 String[] args = getArgs(config, profile, label); // #5 builder.application() .setListeners(Arrays.asList(new ConfigFileApplicationListener())); // #6 try (ConfigurableApplicationContext context = builder.run(args)) { environment.getPropertySources().remove("profiles"); // #7 return clean(new PassthruEnvironmentRepository(environment).findOne(config, profile, label, includeOrigin)); }... }Copy the code

#1 SpringApplicationBuilder uses SpringApplication#run to build an ApplicationContext #2 to build an Environment, Set the WebApplicationType of the SpringApplication, define the type of the constructed ApplicationContext, and set some necessary parameters. Note: The path to the previously saved local configuration file is added to the –spring.config.location parameter, This parameter will be the next step of ConfigFileApplicationListener use # 5 ConfigFileApplicationListener will be by spring. Config. Load the local configuration file location parameters, Add to Environment#PropertySources #6 builder.run -> ApplicationContext#run to construct an ApplicationContext, In the process of construct ConfigFileApplicationListener will work finished loading the configuration file. #7 Clean up some non-configuration center PropertySources in the Environment. For ApplicationContext#run, see the SpringBoot startup process

Spring Cloud Config client

Take a look at how other applications use the Config Server configuration. PropertySourceBootstrapConfiguration ApplicationContextInitializer is achieved, he would read the bootstrap. Properties, Obtain the configuration information of the Config Server from the configuration file such as bootstrap.yml and use PropertySourceLocator to obtain the Environment of the Config Server. Finally, insertPropertySources adds the pulled PropertySources to the application’s Environment.

ConfigServicePropertySourceLocator# locate method through RestTemplate access to Config Server Environment, And the results of PropertySource into corresponding OriginTrackedMapPropertySource.

@RefreshScope

As we know, if you want to refresh the configuration dynamically at runtime, you need to add @refreshScope on the Bean and use the HTTP interface actuator/refresh provided by spring-boot-starter-actuator to refresh the configuration. Now let’s see how they work. In Spring, beans have singleton, Prototype, and different scopes. Scope There are RefreshScope, ThreadScope, SessionScope. Spring has the @Scope annotation and Scope interface and the corresponding implementation class, while @refreshScope is actually a refresh @Scope for scopeName.

First, read the @ Scope annotations, is in ClassPathBeanDefinitionScanner# doScan method, will read through AnnotationScopeMetadataResolver ScopeMetadata information. For details about this section, see the implementation principles of @ComponentScan

Scope handling is abstractBeanFactor #doGetBean during bean construction

protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException { ... if (mbd.isSingleton()) { ... else if (mbd.isPrototype()) { ... else { String scopeName = mbd.getScope(); // #1 final Scope scope = this.scopes.get(scopeName); if (scope == null) { throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'"); } try { Object scopedInstance = scope.get(beanName, () -> { beforePrototypeCreation(beanName); try { return createBean(beanName, mbd, args); } finally { afterPrototypeCreation(beanName); }}); bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd); }... }... }Copy the code

The second argument to the #1 Scope#get method is an ObjectFactory, which does the actual construction of the Bean. The Scope#get method does caching for different scopes.

The basic implementation of the Scope interface is in GenericScope, and its subclass ThreadScope replaces ScopeCache with ThreadLocal to hold beans. Another subclass, RefreshScope, provides methods such as refreshAll.

For information about the Bean construction process, see Bean Construction Principles

Refresh With the introduction of spring-boot-starter-actuator, we can use actuator/refresh to refresh the class marked by @refreshScope. The request is processed by the refresh endpoint, which calls the link RefreshEndpoint#refresh -> ContextRefresher#refresh -> RefreshScope#refreshAll, The method before ContextRefresher#refresh will refresh the contents of the Environment and publish the EnvironmentChangeEvent event. RefreshScope# refreshAll will destroy @ RefreshScope bean (also will release RefreshScopeRefreshedEvent events), so first create bean can get the latest configuration values.

The Endpoint in Spring Boot Actuator is similar to the Controller in SpringMvc, but it can expose services through HTTP and JMX. We will talk about this later.

If you think this article is good, welcome to pay attention to my wechat public number, your attention is my motivation!