Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to simply analyze the refresh mechanism of SpringCloud configuration

2025-02-28 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Servers >

Share

Shulou(Shulou.com)05/31 Report--

Today, I will talk to you about how to carry out a simple analysis of the SpringCloud configuration refresh mechanism, which may not be well understood by many people. in order to make you understand better, the editor has summarized the following contents for you. I hope you can get something according to this article.

SpringCloud Nacos

Mainly divided into the design ideas of SpringCloud Nacos

Briefly analyze the process that happened after the refresh event was triggered and some experience of stepping on the pit.

Org.springframework.cloud.bootstrap.config.PropertySourceLocator

This is an initiator load configuration class provided by SpringCloud, which implements locate and can be injected into the context to discover the configuration.

/ * *

* @ param environment The current Environment.

* @ return A PropertySource, or null if there is none.

* @ throws IllegalStateException if there is a fail-fast condition.

, /

PropertySource locate (Environment environment)

Com.alibaba.cloud.nacos.client.NacosPropertySourceLocator

This class is a configuration discovery class implemented by nacos

Org.springframework.core.env.PropertySource

Change the class to the class abstracted by springcloud to express the attribute source.

Com.alibaba.cloud.nacos.client.NacosPropertySource / nacos implements this class and assigns other properties

/ * *

* Nacos Group.

, /

Private final String group

/ * *

* Nacos dataID.

, /

Private final String dataId

/ * *

* timestamp the property get.

, /

Private final Date timestamp

/ * *

* Whether to support dynamic refresh for this Property Source.

, /

Private final boolean isRefreshable

Give an overview of com.alibaba.cloud.nacos.client.NacosPropertySourceLocator#locate

Source code parsing

@ Override

Public PropertySource locate (Environment env) {

NacosConfigProperties.setEnvironment (env)

/ / get the service class of nacos configuration, http protocol, and access the api API of nacos to obtain configuration

ConfigService configService = nacosConfigManager.getConfigService ()

If (null = = configService) {

Log.warn ("no instance of config service found, can't load config from nacos")

Return null

}

Long timeout = nacosConfigProperties.getTimeout ()

/ / build a builder

NacosPropertySourceBuilder = new NacosPropertySourceBuilder (configService

Timeout)

String name = nacosConfigProperties.getName ()

String dataIdPrefix = nacosConfigProperties.getPrefix ()

If (StringUtils.isEmpty (dataIdPrefix)) {

DataIdPrefix = name

}

If (StringUtils.isEmpty (dataIdPrefix)) {

DataIdPrefix = env.getProperty ("spring.application.name")

}

/ / build a composite data source

CompositePropertySource composite = new CompositePropertySource (

NACOS_PROPERTY_SOURCE_NAME)

/ / load shared configuration

LoadSharedConfiguration (composite)

/ / load extension configuration

LoadExtConfiguration (composite)

/ / load the application configuration, and the priority of the application configuration is the highest, so we will do it at the back here because the place where the configuration is added is addFirst, so the priority is the first and the last.

LoadApplicationConfiguration (composite, dataIdPrefix, nacosConfigProperties, env)

Return composite

}

Every time nacos checks for a configuration update, it triggers a context configuration refresh and calls the locate method.

Org.springframework.cloud.endpoint.event.RefreshEvent

This event is an event built into spring cloud and is used to refresh the configuration

Com.alibaba.cloud.nacos.refresh.NacosRefreshHistory

This class is used to store the refresh history of nacos, to save the MD5 value of the configuration pulled each time, and to compare whether the configuration needs to be refreshed.

Com.alibaba.cloud.nacos.refresh.NacosContextRefresher

This class is used by Nacos to manage some internal listeners, mainly to start a callback when the configuration is refreshed, and to issue configuration refresh events in the spring cloud context

Com.alibaba.cloud.nacos.NacosPropertySourceRepository

This class is used by nacos to hold the pulled data

Process:

Refresher checks configuration updates and saves to NacosPropertySourceRepository

Initiate a refresh event

Locate execution, read NacosPropertySourceRepository directly

Com.alibaba.nacos.client.config.NacosConfigService

This class is the main refresh configuration service class for nacos

Com.alibaba.nacos.client.config.impl.ClientWorker

This class is the main client in the service class, and the protocol is HTTP

When clientWorker starts, it initializes two thread pools, one for regular check configuration and one for auxiliary check.

Executor = Executors.newScheduledThreadPool (1, new ThreadFactory () {

@ Override

Public Thread newThread (Runnable r) {

Thread t = new Thread (r)

T.setName ("com.alibaba.nacos.client.Worker." + agent.getName ())

T.setDaemon (true)

Return t

}

});

ExecutorService = Executors.newScheduledThreadPool (Runtime.getRuntime () .availableProcessors (), new ThreadFactory () {

@ Override

Public Thread newThread (Runnable r) {

Thread t = new Thread (r)

T.setName ("com.alibaba.nacos.client.Worker.longPolling." + agent.getName ())

T.setDaemon (true)

Return t

}

});

Executor.scheduleWithFixedDelay (new Runnable () {

@ Override

Public void run () {

Try {

CheckConfigInfo ()

} catch (Throwable e) {

LOGGER.error ("[" + agent.getName () + "] [sub-check] rotate check error", e)

}

}

}, 1L, 10L, TimeUnit.MILLISECONDS)

Com.alibaba.nacos.client.config.impl.ClientWorker.LongPollingRunnable

This class is used for long polling tasks

Com.alibaba.nacos.client.config.impl.CacheData#checkListenerMd5 begins to refresh the configuration after comparing MD5

Com.alibaba.cloud.nacos.parser

The package provides converters for many file types

When loading data, a converter instance is found based on the file extension.

/ / com.alibaba.cloud.nacos.client.NacosPropertySourceBuilder#loadNacosData

Private Map loadNacosData (String dataId, String group

String fileExtension) {

String data = null

Try {

Data = configService.getConfig (dataId, group, timeout)

If (StringUtils.isEmpty (data)) {

Log.warn (

"Ignore the empty nacos configuration and get it based on dataId [{}] & group [{}]"

DataId, group)

Return EMPTY_MAP

}

If (log.isDebugEnabled ()) {

Log.debug (String.format (

"Loading nacos data, dataId:'% slots, group:'% slots, data:% s", dataId

Group, data))

}

Map dataMap = NacosDataParserHandler.getInstance ()

.parseNacosData (data, fileExtension)

Return dataMap = = null? EMPTY_MAP: dataMap

}

Catch (NacosException e) {

Log.error ("get data from Nacos error,dataId: {},", dataId, e)

}

Catch (Exception e) {

Log.error ("parse data from Nacos error,dataId: {}, data: {},", dataId, data, e)

}

Return EMPTY_MAP

}

The data will be changed into key value and then converted to PropertySource

How to configure a startup configuration class

Since the configuration context is managed by SpringCloud, this injection is different from the previous SpringBoot.

Org.springframework.cloud.bootstrap.BootstrapConfiguration=\

Com.alibaba.cloud.nacos.NacosConfigBootstrapConfiguration

How to share a bean on SpringCloud and SpringBoot (for example)

@ Bean

Public NacosConfigProperties nacosConfigProperties (ApplicationContext context) {

If (context.getParent ()! = null

& & BeanFactoryUtils.beanNamesForTypeIncludingAncestors (

Context.getParent (), NacosConfigProperties.class) .length > 0) {

Return BeanFactoryUtils.beanOfTypeIncludingAncestors (context.getParent ()

NacosConfigProperties.class)

}

Return new NacosConfigProperties ()

}

About the process org.springframework.cloud.endpoint.event.RefreshEventListener// outer method of the refresh mechanism

Public synchronized Set refresh () {

Set keys = refreshEnvironment ()

This.scope.refreshAll ()

Return keys

}

/ /

Public synchronized Set refreshEnvironment () {

Map before = extract (

This.context.getEnvironment () .getPropertySources ()

AddConfigFilesToEnvironment ()

Set keys = changes (before

Extract (this.context.getEnvironment () .getPropertySources ()) .keySet ()

This.context.publishEvent (new EnvironmentChangeEvent (this.context, keys))

Return keys

}

This class handles RefreshEvent snooping

Navigate directly to org.springframework.cloud.context.refresh.ContextRefresher#refreshEnvironment, which is the main way to refresh the configuration, what to do:

Merge and get the configuration key value before refresh

Org.springframework.cloud.context.refresh.ContextRefresher#addConfigFilesToEnvironment simulates a new SpringApplication, triggers most of the SpringBoot startup process, so it also triggers to read the configuration, so it triggers the Locator mentioned above, and then gets a new Spring application, obtains a new aggregate configuration source, compares it with the old Spring application configuration source, puts the changed configuration to the old one, and then closes the new Spring application.

Compare the old and new configurations, take out the configuration, and trigger an event org.springframework.cloud.context.environment.EnvironmentChangeEvent

After jumping out of the method stack, execute org.springframework.cloud.context.scope.refresh.RefreshScope#refreshAll

Simple Analysis of EnvironmentChangeEvent

Org.springframework.cloud.context.properties.ConfigurationPropertiesRebinder#rebind ()

The code is as follows:

@ 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)

/ / get the source object

If (AopUtils.isAopProxy (bean)) {

Bean = ProxyUtils.getTargetObject (bean)

}

If (bean! = null) {

/ / re-trigger the periodic method of destruction and initialization

This.applicationContext.getAutowireCapableBeanFactory ()

.destroyBean (bean)

/ / because the initialization life cycle is triggered, it can be triggered.

/ / org.springframework.boot.context.properties.ConfigurationPropertiesBindingPostProcessor#postProcessBeforeInitialization

This.applicationContext.getAutowireCapableBeanFactory ()

.initializeBean (bean, name)

Return true

}

}

Catch (RuntimeException e) {

This.errors.put (name, e)

Throw e

}

Catch (Exception e) {

This.errors.put (name, e)

Throw new IllegalStateException ("Cannot rebind to" + name, e)

}

}

Return false

}

After receiving the event in this method, some bean properties are rebound. Which Bean?

Org.springframework.cloud.context.properties.ConfigurationPropertiesBeans#postProcessBeforeInitialization one of the post-processors of bean later in life that the method executes in the Spring refresh context, it checks the annotation @ ConfigurationProperties, these bean are the rebound bean described in the first step above

@ Override

Public Object postProcessBeforeInitialization (Object bean, String beanName)

Throws BeansException {

If (isRefreshScoped (beanName)) {

Return bean

}

ConfigurationProperties annotation = AnnotationUtils

.findAnnotation (bean.getClass (), ConfigurationProperties.class)

If (annotation! = null) {

This.beans.put (beanName, bean)

}

Else if (this.metaData! = null) {

Annotation = this.metaData.findFactoryAnnotation (beanName

ConfigurationProperties.class)

If (annotation! = null) {

This.beans.put (beanName, bean)

}

}

Return bean

}

Simple analysis of org.springframework.cloud.context.scope.refresh.RefreshScope#refreshAll@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 ())

}

Org.springframework.cloud.context.scope.GenericScope#destroy ()

Destroy the collection of BeanLifecycleWrapper instances

What is BeanLifecycleWrapper?

Private static class BeanLifecycleWrapper {

/ / the name of bean

Private final String name

/ / obtain bean

Private final ObjectFactory objectFactory

/ / A real instance

Private Object bean

/ / destroy function

Private Runnable callback

}

How is BeanLifecycleWrapper constructed?

@ Override

Public Object get (String name, ObjectFactory objectFactory) {

BeanLifecycleWrapper value = this.cache.put (name

New BeanLifecycleWrapper (name, objectFactory))

This.locks.putIfAbsent (name, new ReentrantReadWriteLock ())

Try {

Return value.getBean ()

}

Catch (RuntimeException e) {

This.errors.put (name, e)

Throw e

}

}

The above code can be traced back to a branch code that Spring created bean, org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean 347 lines of code.

String scopeName = mbd.getScope ()

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)

}

What happens after it's destroyed? In fact, it turns the bean bound by BeanLifecycleWrapper into null, so how to refresh the configuration? The @ RefreshScope tagged object is initialized as a proxy object at the beginning, and then when performing the get operation of its @ Value attribute, it will enter the proxy method, and the proxy method will get Target, where org.springframework.cloud.context.scope.GenericScope#get will be triggered.

Public Object getBean () {

If (this.bean = = null) {

Synchronized (this.name) {

If (this.bean = = null) {

/ / because bean is empty, it triggers a reinitialization of bean, and goes through the lifecycle process, so the configuration comes back.

This.bean = this.objectFactory.getObject ()

}

}

}

Return this.bean

}

Step on the pit

The above analysis is simple enough, so what are the pitfalls in using this configuration automatic refresh mechanism?

For objects that use @ RefreshScople, if you delete a row of attributes from the configuration center, the corresponding properties of the corresponding bean will become null, but objects that use @ ConfigaruationProperties will not. Why? Because the former is a re-life process of the whole bean, but the latter only executes the init method.

Whether you use @ RefreshScople or @ ConfigaruationProperties, you should not perform excessive logic in the destory and init methods, which affect the availability of the service and block too many requests at high concurrency. The latter will affect the latency of configuration refresh.

After reading the above, do you have any further understanding of the simple analysis of the SpringCloud configuration refresh mechanism? If you want to know more knowledge or related content, please follow the industry information channel, thank you for your support.

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Servers

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report