Principle Analysis of Nacos Configuration Center

Dynamic configuration management is one of the three major functions of Nacos. Through dynamic configuration services, we can manage the configuration information of all applications or services in a centralized and dynamic manner in all environments.

The dynamic configuration center can make the corresponding configuration information take effect without redeploying applications and services when the configuration is updated, which greatly increases the operation and maintenance capability of the system.

Dynamic configuration

Next, I will join you in the future to learn about the dynamic configuration capabilities of Nacos, and see how Nacos manages the configuration in a simple, elegant and efficient way and realizes the dynamic change of the configuration.

We use a simple example to understand the function of dynamic configuration of Nacos.

Environmental preparation

First, we need to prepare a Nacos server. Now there are two ways to get the Nacos server:

  • 1. Compile from source code
  • 2. Download the Release package

There are two ways to get the executable program of Nacos. Next, I will use the first method to compile an executable program from the source code. Some people may ask why not download the Release package directly, but also compile it yourself? First of all, the Release package is also obtained by compiling the source code. Secondly, we can understand some processes and may encounter some problems through our own compilation. These are very important experiences. Well, let's compile the source code directly.

First fork a nacos code to your own github repository, and then clone the code locally.

Then execute the following commands in the root directory of the project (assuming we have configured the java and maven environments):

mvn -Prelease-nacos clean install -U  

After successful execution, you will see the result as shown below:

build-nacos-server.png

Then in the distribution directory of the project, we can find the executable program, including two compressed packages, which are the Release packages published on the nacos github official website.

build-distribution.png

Next, we copy the compiled two compressed packages, and then decompress them and use them directly, which is equivalent to downloading the Release package. After decompression, the file structure is the same as nacos-server-0.8.0. We can directly execute startup.sh to start a stand-alone Nacos server.

Start the server

Execute the following command to start a Nacos server:

sh startup.sh -m standalone

After startup you will see the result as shown below:

startup-nacos.png

After the startup is successful, we can access the Nacos console, as shown in the following figure:

nacos-console-login.png

The console does simple permission control, and the default account and password are nacos.

After logging in, it looks like this:

nacos-console-index.png

New configuration

Next we create a simple configuration item on the console, as shown in the following figure:

add-config.png

start the client

When the server and configuration items are ready, the client can be created. As shown in the following figure, create a new Nacos ConfigService to receive data:

config-service.png

After execution, the following information will be printed:

config-result-1.png

Here I use a System.in.read() method to monitor the input information, mainly to prevent the main thread from exiting and not seeing subsequent results.

Modify configuration information

Next, we change our configuration information on the Nacos console to the following figure:

update-config.png

After modifying the configuration and clicking the "Publish" button, the client will receive the latest data, as shown in the following figure:

config-result-2.png

So far, a simple dynamic configuration management function has been described. Deleting a configuration is similar to updating a configuration, and will not be repeated here.

Applicable scene

After understanding the effect of dynamic configuration management, we know the general principle. The Nacos server saves the configuration information. After the client connects to the server, according to the dataID, the group can obtain the specific configuration information. When the configuration of the server occurs Clients are notified when changes are made. When the client gets the latest configuration information after the change, it can do its own processing, which is very useful. All scenarios that need to use the configuration can be managed through Nacos.

It can be said that Nacos has many applicable scenarios, including but not limited to the following situations:

  • Database connection information
  • Throttling rules and downgrade switches
  • Dynamic scheduling of traffic

Students who have read my Sentinel series of articles may know that there is an article dedicated to the construction of cluster current limiting environment, which is to create dynamic rules through Nacos.

push or pull

Now we understand the configuration management function of Nacos, but there is one problem we need to understand, that is how the Nacos client obtains the latest data of the Nacos server in real time.

In fact, the data interaction between the client and the server is nothing more than two cases:

  • Server pushes data to client
  • Client pulls data from server

Is it push or pull? From the fact that Nacos client receives the latest data through Listener, it feels like the data pushed by the server, but it cannot be taken for granted. To know the answer, the fastest and most accurate way is to start from Find it in the source code.

Create ConfigService

As we can know from our demo, the first is to create a ConfigService. The ConfigService is created through the ConfigFactory class, as shown in the following figure:

create-config-service.png

It can be seen that the ConfigService is actually created by calling the constructor of NacosConfigService through reflection, and there is a constructor with a Properties parameter.

It should be noted that there is no singleton or caching technology here, which means that an instance of ConfigService will be recreated every time it is called.

Instantiate ConfigService

Now let's take a look at the construction method of NacosConfigService and see how ConfigService is instantiated, as shown in the following figure:

init-config-service.png

When instantiating, it mainly initializes two objects, they are:

  • HttpAgent
  • ClientWorker

HttpAgent

Among them, agent is realized by decoration mode, ServerHttpAgent is the actual working class, MetricsHttpAgent also calls the method of ServerHttpAgent internally, and some statistical operations are added, so we only need to care about the function of ServerHttpAgent.

The agent actually functions in the ClientWorker. Let's take a look at the ClientWorker class.

ClientWorker

The following is the construction method of ClientWorker, as shown in the following figure:

client-worker.png

It can be seen that in addition to maintaining HttpAgent within itself, ClientWorker also creates two thread pools:

The first thread pool is an executor that only has one thread to perform timed tasks. The executor executes the checkConfigInfo() method every 10ms. From the method name, it can be known that the configuration information is checked every 10ms.

The second thread pool is an ordinary thread pool. From the name of ThreadFactory, we can see that this thread pool is used for long polling.

Now let's see what the executor does every 10ms, as shown in the following figure:

check-config-info.png

It can be seen that the checkConfigInfo method takes out a batch of tasks and submits them to the executorService thread pool for execution. The executed task is LongPollingRunnable, and each task has a taskId.

Now let's take a look at what LongPollingRunnable does. It is mainly divided into two parts. The first part is to check the local configuration information, and the second part is to obtain the configuration information of the server and update it locally.

1. Local inspection

First take out the CacheData related to the taskId, and then check the CacheData, including the local configuration check and the md5 check of the listener. The local check is mainly for fault tolerance. When the server hangs up, the Nacos client can download the local file Obtain relevant configuration information from the system, as shown in the following figure:

check-local-config.png

By tracing the checkLocalConfig method, you can see that Nacos saves the configuration information in the

~/nacos/config/fixed-{address}_8848_nacos/snapshot/DEFAULT_GROUP/{dataId}

In this file, let's take a look at the content saved in this file, as shown in the following figure:

config-snapshot.png

2. Server check

Then get the list of dataIds whose values ​​have changed from the server through the checkUpdateDataIds() method,

Through the getServerConfig method, obtain the latest configuration information from the server according to dataId, and then save the latest configuration information in CacheData.

Finally, call the checkListenerMd5 method of CacheData. You can see that this method has also been called in the first part. We need to focus on it.

get-server-info.png

It can be seen that at the end of the task, that is, the task is resubmitted through the executorService in finally.

Add Listener

Well, now we can add a Listener to ConfigService, and finally call the addTenantListeners method of ClientWorker, as shown in the following figure:

add-listeners.png

This method is divided into two parts, firstly obtain a CacheData object according to dataId, group and current scene, and then add the listener object to be added to CacheData.

That is to say, the listener is finally held by the CacheData here, so the callback method receiveConfigInfo of the listener should be triggered in the CacheData.

We found that CacheData is a class with a very high frequency. In the task of LongPollingRunnable, almost all methods revolve around the CacheData class. Now when adding a Listener, the Listener is actually delegated to CacheData, so we should focus on it. Under the CacheData class.

CacheData

First let's take a look at the member variables in CacheData, as shown in the following figure:

cache-data.png

You can see that in addition to dataId, group, content, taskId attributes related to configuration, there are two more important attributes: listeners, md5.

listeners are all listeners associated with the CacheData, but not the original Listener object saved, but the wrapped ManagerListenerWrap object, which holds a lastCallMd5 property in addition to the Listener object.

Another attribute md5 is the md5 value calculated according to the content of the current object.

trigger callback

Now that we have a general understanding of ConfigService, the last important question remains unanswered, which is when the Listener of the ConfigService triggers the callback method receiveConfigInfo.

Now let's go back and think about it. In the timing task in ClientWorker, a long polling task is started: LongPollingRunnable, which executes the cacheData.checkListenerMd5() method multiple times, so let's take a look at this What exactly does the method do, as shown in the following figure:

check-listener-md5.png

It should be clearer here. This method will check whether the current md5 of CacheData is consistent with the value of md5 saved in all Listeners held by CacheData. If they are inconsistent, execute a safe listener notification method: safeNotifyListener, what to notify Woolen cloth? We can take a bold guess, it should be to notify the user of the Listener that the configuration information that the Listener is concerned about has changed. Now let's take a look at the safeNotifyListener method as shown in the image below:

safe-notify-listener.png

You can see that in the safeNotifyListener method, focus on the three lines of code in the red box: get the latest configuration information, call the Listener's callback method, and pass in the latest configuration information as parameters, so that the Listener users can receive it. The changed configuration information, and finally update the md5 value of ListenerWrap. As we guessed, the Listener's callback method is fired in this method.

When did Md5 change

When did the md5 value of CacheData change? We can recall that in the task performed by LongPollingRunnable above, when obtaining the configuration information that has changed on the server side, the latest content data is written into CacheData. We can look at the method as follows:

set-content.png

It can be seen that in the long polling task, when the server configuration information changes, the client obtains the latest data, saves it in CacheData, and updates the md5 value of the CacheData, so when the next execution When the checkListenerMd5 method is used, it will be found that the md5 value held by the current listener is different from the md5 value of CacheData, which means that the configuration information of the server has changed. At this time, the latest data needs to be notified to the listener. have.

So far, the complete process of the configuration center has been analyzed. It can be found that Nacos does not send the latest configuration information of the server to the client by pushing, but the client maintains a long polling task and pulls it regularly. Changed configuration information, and then push the latest data to the Listener holder.

Pull advantage

What is the advantage of the client pulling data from the server compared to the server pushing data to the client? Why is Nacos not designed to actively push data, but requires the client to pull it? If the push method is used, the server needs to maintain a long connection with the client, which consumes a lot of resources, and also needs to consider the validity of the connection, such as the need to maintain the connection between the two through heartbeat. In the pull method, the client only needs to obtain data from the server through a stateless http request.

Summarize

After the Nacos server creates the relevant configuration items, the client can monitor it.

The client checks the data of the configuration items it listens to through a scheduled task. Once the data on the server changes, the client will get the latest data, save the latest data in a CacheData object, and then Recalculate the value of the md5 attribute of CacheData, and then trigger the receiveConfigInfo callback for the Listener bound to the CacheData.

Considering the problem of server failure, the client will save the latest data in the local snapshot file after obtaining the latest data, and will preferentially obtain the value of configuration information from the file in the future.

contact me

contact me

{{o.name}}
{{m.name}}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324115315&siteId=291194637
Recommended