Spring Cloud Alibaba: Nacos configuration center

Keywords: Java Spring Cloud Microservices

Spring Cloud Alibaba: Nacos configuration center

Dynamic configuration service can manage the application configuration and service configuration of all environments in a centralized, external and dynamic way. Dynamic configuration eliminates the need to redeploy applications and services when configuration changes, making configuration management more efficient and agile. Configuration centralized management makes it easier to implement stateless services and make it easier for services to expand flexibly on demand. Nacos provides a simple and easy-to-use UI to help manage the configuration of all services and applications. Nacos also provides a series of out of the box configuration management features, including configuration version tracking, Canary release, one click rollback configuration and client configuration update status tracking, to help more safely manage configuration changes and reduce the risks caused by configuration changes in the production environment.

Bloggers have previously introduced the Config component of the configuration center provided by Spring Cloud:

However, the Config component is not responsible for storing and managing configuration files (regardless of the cache of the configuration files first). The configuration files are stored on a third-party platform (such as Github), and the platform needs to have the function of Webhook. When the configuration file is modified, the platform calls back the interface of the Config Server through the function of Webhook to notify the Config Server that the configuration file has been updated, After that, the Config Server also needs to use MQ to transfer the modified configuration file to the corresponding Config Client, so the Config Client also needs to be bound with MQ. It seems that the Config component is not very flexible. Next, the blogger will introduce the use of Nacos as the configuration center. Unlike Config, Nacos is responsible for storing and managing configuration files, so it does not need the intervention of a third-party platform, and realizes dynamic configuration services by combining push and pull, It also provides a simple and easy-to-use UI to help manage the configuration of all services and applications.

The previous blog has introduced the use of Nacos as the service registration and discovery center. For the installation and operation of Nacos service, please refer to the following blog:

Create service

Create the alibablog Maven project as the parent module, and then create the config child module.

AlibabaBlog module

pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.kaven</groupId>
    <artifactId>AlibabaBlog</artifactId>
    <packaging>pom</packaging>
    <version>1.0-SNAPSHOT</version>

    <description>Spring Cloud Alibaba</description>
    <modules>
        <module>nacos</module>
        <module>consumer</module>
    </modules>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.2.RELEASE</version>
    </parent>

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <spring-cloud-version>Hoxton.SR9</spring-cloud-version>
        <spring-cloud-alibaba-version>2.2.6.RELEASE</spring-cloud-alibaba-version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud-version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring-cloud-alibaba-version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
</project>

config module

pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>AlibabaBlog</artifactId>
        <groupId>com.kaven</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>config</artifactId>

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
    </dependencies>
</project>

bootstrap.yml (not application.yml here):

spring:
  application:
    name: config
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
      config:
        server-addr: 127.0.0.1:8848
        file-extension: yaml

ConfigController interface class:

package com.kaven.alibaba.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Author: ITKaven
 * @Date: 2021/11/09 12:57
 * @Blog: https://kaven.blog.csdn.net
 * @Leetcode: https://leetcode-cn.com/u/kavenit
 * @Notes:
 */

@RestController
@RefreshScope
public class ConfigController {

    @Value("${kaven}")
    private String kaven;

    @GetMapping("/config")
    public String getConfig() {
        return kaven;
    }
}

@The RefreshScope annotation needs to be added to make the config service aware that there are configuration file parameters that need to be updated dynamically.

ConfigApplication startup class:

package com.kaven.alibaba;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

/**
 * @Author: ITKaven
 * @Date: 2021/11/09 11:43
 * @Blog: https://kaven.blog.csdn.net
 * @Leetcode: https://leetcode-cn.com/u/kavenit
 * @Notes:
 */

@SpringBootApplication
@EnableDiscoveryClient
public class ConfigApplication {
    public static void main(String[] args) {
        SpringApplication.run(ConfigApplication.class);
    }
}

@The EnableDiscoveryClient annotation needs to be added.

Why bootstrap.yml instead of application.yml:

Spring Cloud is built on Spring Boot. There are two contexts in Spring Boot, one is bootstrap and the other is application. Bootstrap is the parent context of the application, that is, bootstrap loading takes precedence over application. Bootstrap is mainly used to load configuration information from additional resources and decrypt attributes in local and external configuration files. The two contexts share an environment that is the source of external properties for any spring application. The properties in bootstrap will be loaded first, and they cannot be overwritten by the same local configuration by default.

Add a configuration file config.yaml on Nacos. Why it is named like this will be explained later.

The contents of config.yaml configuration file are as follows:

server:
  port: 9000

kaven: "hello kaven"


Start the config service. Obviously, the service successfully obtained the config.yaml configuration file (otherwise, the default port is 8080).

The config service is also successfully registered on Nacos.

Interface to request config service http://localhost:9000/config , also displays the contents of the config.yaml configuration file.

Modify the value of kaven parameter in config.yaml configuration file in Nacos.

In fact, the config service has sensed the update of the config.yaml configuration file.

Request the config service interface again, and the updated value will be displayed.

Data ID

In Nacos, the complete format of Data ID is as follows:

${prefix}-${spring.profiles.active}.${file-extension}

Prefix defaults to the value of spring.application.name, which can also be configured through the configuration item spring.cloud.nacos.config.prefix. spring.profiles.active is the profile corresponding to the current environment (when it is empty, the corresponding connector - will not exist, so the splicing format of Data ID becomes:

${prefix}.${file-extension}

File exception is the suffix of the configuration file, which can be configured through the configuration item spring.cloud.nacos.config.file extension.

The config service does not set the profile, and the prefix is config (the default value is spring.application.name), and the file extension is yaml. Therefore, the config.yaml configuration file will be obtained by the config service.

However, it can be seen from the background of the config service that the config service will obtain the config and config.yaml configuration files. Obviously, config is the value of prefix and does not add the following file suffix:

.${file-extension}


Here, bloggers will verify whether the config service will obtain the config configuration file, delete the kaven parameter in the config.yaml configuration file, create the config configuration file, and add the kaven parameter to the config configuration file. If the config service does not obtain the config configuration file, an error will be reported.


Restart the config service and the service does not report an error.

The interface to request the config service is also the interface to display the contents in the config configuration file.

Therefore, the service will obtain these configuration files (if any, the priority rules of the configuration files are in this order, from high to low. You can test them yourself):

${prefix}-${spring.profiles.active}.${file-extension}
${prefix}.${file-extension}
${prefix}

Shared profile

If some configurations in some services are the same, such as Redis or MQ cluster configuration, if there is no shared configuration file, these configurations must be repeated in the configuration file of these services. When the number of services is very large, it is inconvenient to manage. For example, if the Redis cluster configuration changes, it is necessary to modify the configuration file of each service that depends on the Redis cluster, This requires a lot of labor cost, so it is necessary to share the configuration file.

It is also very convenient to implement shared configuration files in Nacos. Modify the bootstrap.yml configuration file of config service:

spring:
  application:
    name: config
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
      config:
        server-addr: 127.0.0.1:8848
        file-extension: yaml
        prefix: config
        shared-configs[0]:
          data-id: redis.yaml
          refresh: true
        extension-configs[0]:
          data-id: mq.yaml
          refresh: true
  profiles:
    active: test

Shared configs and extension configs can share configuration files.

        shared-configs[0]:
          data-id: redis.yaml
          refresh: true
        extension-configs[0]:
          data-id: mq.yaml
          refresh: true

Moreover, both shared configs and extension configs are List type data, so [0] subscript is added after them (starting from 0, the next is [1]).


Config class (dataId and refresh need to be set, and group can use the default value):

	public static class Config {

		/**
		 * Extended configuration data ID
		 */
		private String dataId;

		/**
		 * Extended configuration group, default is DEFAULT_GROUP
		 */
		private String group = "DEFAULT_GROUP";

		/**
		 * Whether dynamic refresh is supported. It is not supported by default
		 */
		private boolean refresh = false;

        ...
	}

Create these profiles in Nacos:

The contents of the configuration file are as follows:

config.yaml: "config.yaml"
config: "config"
config-test.yaml: "config-test.yaml"
redis.yaml: "redis.yaml"
mq.yaml: "mq.yaml"

Modify the config service interface as follows:

package com.kaven.alibaba.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @Author: ITKaven
 * @Date: 2021/11/09 12:57
 * @Blog: https://kaven.blog.csdn.net
 * @Leetcode: https://leetcode-cn.com/u/kavenit
 * @Notes:
 */

@RestController
@RefreshScope
public class ConfigController {

    @Value("${config.yaml}")
    private String configYaml;

    @Value("${config}")
    private String config;

    @Value("${config-test.yaml}")
    private String configTestYaml;

    @Value("${redis.yaml}")
    private String redisYaml;

    @Value("${mq.yaml}")
    private String mqYaml;


    @GetMapping("/config")
    public String getConfig() {
        return configYaml.concat(" ")
                .concat(config).concat(" ")
                .concat(configTestYaml).concat(" ")
                .concat(redisYaml).concat(" ")
                .concat(mqYaml);
    }
}

Interface for requesting config service (the service port is not configured in the configuration file, so it is the default port 8080):

Obviously, these configuration files succeeded.

Priority of these profiles (from high to low):

config-test.yaml
config.yaml
config
mq.yaml
redis.yaml

Therefore, the priority rule of the configuration file is as follows (priority from high to low):

${prefix}-${spring.profiles.active}.${file-extension}
${prefix}.${file-extension}
${prefix}
${extension-configs}
${shared-configs}

It is also reflected in the background of config service:

Located property source: [BootstrapPropertySource {name='bootstrapProperties-config-test.yaml,DEFAULT_GROUP'}, BootstrapPropertySource {name='bootstrapProperties-config.yaml,DEFAULT_GROUP'}, BootstrapPropertySource {name='bootstrapProperties-config,DEFAULT_GROUP'}, BootstrapPropertySource {name='bootstrapProperties-mq.yaml,DEFAULT_GROUP'}, BootstrapPropertySource {name='bootstrapProperties-redis.yaml,DEFAULT_GROUP'}]

The principle of Nacos configuration center will be introduced by bloggers later. This will be the end of Nacos configuration center. Later, other features and high availability of Nacos will be introduced.

If the blogger has something wrong or you have different opinions, you are welcome to comment and supplement.

Posted by paulytrick on Wed, 10 Nov 2021 08:17:29 -0800