1 introduction

Welcome to visit www.pkslow.com for more wonderful articles!

Configurability is a feature that a mature software system should provide, and configuration management is important for large systems, especially microservice systems with multiple applications. Fortunately, Spring provides good configuration management, such as Springboot’s configuration is very powerful. In the case of Spring Cloud, there is the powerful Spring Cloud Config, which is useful for distributed system configuration management by providing a configuration management outside of the application, such as a file or Git repository.

2 Quick Experience

The Spring Cloud Config server is a Springboot application that is simple to start and deploy.

The overall architecture is shown in the figure below:

2.1 The server is Springboot

Add dependencies to Springboot as follows:

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-config-server</artifactId>
  <version>2.2.0. RELEASE</version>
</dependency>
Copy the code

Only one is needed, and it already includes both the Web and the ACTUATOR.

Add Java main class:

package com.pkslow.config;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;

@SpringBootApplication
@EnableConfigServer
public class ConfigServer {
    public static void main(String[] args) { SpringApplication.run(ConfigServer.class,args); }}Copy the code

There is only one more annotation @enableconFigServer than the normal Springboot application.

2.2 Configuring the Repository

We will manage the configuration through version control, usually using the Git repository. For a simple demonstration, use the local repository as follows:

# Create directory
mkdir git-repo

Initialize a git directory
git init

# create new file
touch application.properties

# Add changes
git add .

# Submit changes
git commit -m "init"
Copy the code

Configure the application. Properties of the project, and note that it is in the Config Server project, not in the git-repo directory:

server.port=8888
spring.application.name=config-server
spring.cloud.config.server.git.uri=/Users/pkslow/IdeaProjects/pkslow-modules/config-server/git-repo
Copy the code

Then you can start Config Server.

But the configuration file in the Git repository doesn’t have any content, so we add the following content and commit (must commit, otherwise can’t get).

pkslow.webSite=www.pkslow.com
pkslow.age=18
pkslow.email=[email protected]
Copy the code

2.3 Configuring Path Matching

So how do we get these configurations? It can be read from the following URL:

/{application}/{profile}[/{label}]
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties
Copy the code
  • Label refers to the code branch, such as master, feature-1, etc.

  • Application is the name of the application that the client will use to read later.

  • Profiles are used to specify environments such as PROD, dev, and UAT.

So, we can use the following URL to get the configuration information we just added:

http://localhost:8888/application/default
http://localhost:8888/application/default/master
http://localhost:8888/master/application.properties
http://localhost:8888/application-default.properties
Copy the code

Access is as follows:

$ curl http://localhost:8888/application/default/master
{"name":"application"."profiles": ["default"]."label":"master"."version":"8796f39b35095f6e9b7176457eb03dd6d62b1783"."state":null,"propertySources": [{"name":"/Users/pkslow/IdeaProjects/pkslow-modules/config-server/git-repo/application.properties"."source": {"pkslow.webSite":"www.pkslow.com"."pkslow.age":"18"."pkslow.email":"[email protected]"}}}]Copy the code

/{label}/{application}-{profile}. Properties /{application}-{profile}.

$ curl http://localhost:8888/application-default.properties
pkslow.age: 18
pkslow.email: [email protected]
pkslow.webSite: www.pkslow.com
Copy the code

If we first create a branch relea-20200809 and change the age to the actual age 9, then we would do the following:

$ curl http://localhost:8888/application/default/release-20200809
{"name":"application"."profiles": ["default"]."label":"release-20200809"."version":"7e27e6972ed31ee1a51e9277a2f5c0a628cec67a"."state":null,"propertySources": [{"name":"/Users/pkslow/IdeaProjects/pkslow-modules/config-server/git-repo/application.properties"."source": {"pkslow.webSite":"www.pkslow.com"."pkslow.age":"9"."pkslow.email":"[email protected]"}}}]Copy the code

You can see the corresponding pkslow. Age has to nine, but access/application/default/master or 18, between branches will not influence each other.

2.4 Remote Warehouse

Local repositories are just for the sake of illustration. In a real project, a remote repository is usually used. Create a new repository on GitHub as follows:

A private repository was created to check whether the authentication was correct.

Reconfigure the warehouse address as follows:

spring.cloud.config.server.git.uri=https://github.com/pkslow/pkslow-config
spring.cloud.config.server.git.username=[email protected]
spring.cloud.config.server.git.password=* * *
spring.cloud.config.server.git.default-label=master
spring.cloud.config.server.git.search-paths=demo
Copy the code

Create a demo directory to hold the configuration, so search-Paths is configured for demo. After the configuration is complete and the server is restarted, the remote repository configuration can be read properly.

2.5 More code configuration repositories

In some cases, our configuration may not only be in a repository, but in the respective client code base. For example, we have the following three services:

  • (1) Service discovery: Discovery, code base pkslow-discovery-service
  • (2) API gateway: gateway, code base pkslow-gateway-service
  • (3) Order service: order, code base pkslow-order-service

Each configuration file is in its own code base, which requires multiple code bases to be configured. We also configure a default configuration library pkslow-default, if not, the default code base configuration will be selected. The configuration is as follows:

server:
  port: 8888
spring:
  application:
    name: config-server
  cloud:
    config:
      server:
        git:
          uri: /Users/pkslow/multiple-repos/pkslow-default
          repos:
            pkslow-discovery-service:
              pattern: pkslow-discovery-*
              cloneOnStart: true
              uri: /Users/pkslow/multiple-repos/pkslow-discovery-service
              search-paths: config
            pkslow-gateway-service:
              pattern: pkslow-gateway-*/dev
              cloneOnStart: true
              uri: /Users/pkslow/multiple-repos/pkslow-gateway-service
              search-paths: config
            pkslow-order-service:
              pattern: pkslow-order-*
              cloneOnStart: true
              uri: /Users/pkslow/IdeaProjects/pkslow-order-service
              search-paths: config
Copy the code

You can define a directory for search-paths. The default directory is not the root directory.

The pattern is configured with {application}/{profile} and supports the regular symbol *. Note that only one result is matched. If both results are met, only the first matching repository is taken.

Let’s look at the configuration results after startup:

# Default profile and label, match correctly
$ curl http://localhost:8888/pkslow-order-service/default/master
{"name":"pkslow-order-service"."profiles": ["default"]."label":"master"."version":"9d86e5d11974f0a0e7c20cd53d8f062755193e70"."state":null,"propertySources": [{"name":"/Users/pkslow/IdeaProjects/pkslow-modules/config-server/multiple-repos/pkslow-order-service/config/application.properti es"."source": {"pkslow.webSite":"www.pkslow.com"."pkslow.app.name":"order-service"}}}]# correct match, but no Label, configuration library has no corresponding code branch, 404
$ curl http://localhost:8888/pkslow-order-service/default/release
{"timestamp":"The 2020-08-13 T06:58:38. 722 + 0000"."status": 404,"error":"Not Found"."message":"No such label: release"."path":"/pkslow-order-service/default/release"}

# profile = dev
$ curl http://localhost:8888/pkslow-order-service/dev/master
{"name":"pkslow-order-service"."profiles": ["dev"]."label":"master"."version":"9d86e5d11974f0a0e7c20cd53d8f062755193e70"."state":null,"propertySources": [{"name":"/Users/pkslow/IdeaProjects/pkslow-modules/config-server/multiple-repos/pkslow-order-service/config/application.properti es"."source": {"pkslow.webSite":"www.pkslow.com"."pkslow.app.name":"order-service"}}}]Profile =dev for gateway, read the default configuration
$ curl http://localhost:8888/pkslow-gateway-service/default/master
{"name":"pkslow-gateway-service"."profiles": ["default"]."label":"master"."version":"8358f2b4701fac21a0c7776bc46cec6d9442c549"."state":null,"propertySources": [{"name":"/Users/pkslow/IdeaProjects/pkslow-modules/config-server/multiple-repos/pkslow-base/application.properties"."source": {"pkslow.birthDate":"2020-08-10"}}}]# match profile=dev for gateway
$ curl http://localhost:8888/pkslow-gateway-service/dev/master
{"name":"pkslow-gateway-service"."profiles": ["dev"]."label":"master"."version":"1a4e26849b237dc2592ca0d391daaa1a879747d2"."state":null,"propertySources": [{"name":"/Users/pkslow/IdeaProjects/pkslow-modules/config-server/multiple-repos/pkslow-gateway-service/config/application.proper ties"."source": {"pkslow.webSite":"www.pkslow.com"."pkslow.app.name":"gateway-service"}}}]Service name does not exist, cannot match, read default configuration
$ curl http://localhost:8888/unknown-service/dev/master
{"name":"unknown-service"."profiles": ["dev"]."label":"master"."version":"8358f2b4701fac21a0c7776bc46cec6d9442c549"."state":null,"propertySources": [{"name":"/Users/pkslow/IdeaProjects/pkslow-modules/config-server/multiple-repos/pkslow-base/application.properties"."source": {"pkslow.birthDate":"2020-08-10"}}}]Copy the code

3 Client Configuration

From the previous example we have seen how the server can get the configuration from the code base, but it is always important that the client can get the corresponding configuration and make it work. So let’s do a demonstration.

3.1 Project Preparation

Build a simple Springboot Web project with Spring Cloud Config support and add the following dependencies:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-config</artifactId>
</dependency>
Copy the code

Add configuration file: bootstrap.properties (although we will read the configuration from the server, some of the configuration must be added from the client, such as the server address), as follows:

server.port=8080
spring.application.name=pkslow-gateway-service
spring.cloud.config.uri=http://localhost:8888
Copy the code

Here we configure the port of the client, the address of the server, and the name of the client application. This name is very important, which will be explained later.

Add the Controller to expose the API to display the read configuration:

@RestController
public class PkslowController {
    @Value("${pkslow.age}")
    private Integer age;

    @Value("${pkslow.email}")
    private String email;

    @Value("${pkslow.webSite}")
    private String webSite;

    @GetMapping("/pkslow")
    public Map<String, String> getConfig(a) {
        Map<String, String> map = new HashMap<>();
        map.put("age", age.toString());
        map.put("email", email);
        map.put("webSite", webSite);
        returnmap; }}Copy the code

Then start the client, and the access result is as follows:

$ curl http://localhost:8080/pkslow
{"webSite":"default.pkslow.com"."age":"9"."email":"[email protected]"}
Copy the code

The configuration content is not on the client, which means that the configuration information can be obtained from the server.

3.2 How Do I Match clients

The client has obtained the configuration information, is it correct? How does the client match? In fact, the previous content has been mentioned, mainly through three information to match:

  • Label refers to the code branch.

  • Application is the name of the application.

  • A profile is used to specify the environment.

In the previous example, the client name is pkslow-gateway-service. Label is not specified, it defaults to master; Profile is not specified. Default is default.

Pkslow -gateway-*/dev = = pkslow-gateway-*/dev = = pkslow-gateway-*/dev = = pkslow-gateway-*/dev = = pkslow-gateway-*/dev = = pkslow-gateway-*/dev

$ cat pkslow-base/application.properties 
pkslow.webSite=default.pkslow.com
pkslow.age=9
[email protected]
Copy the code

Modify the client configuration and set profile to dev as follows:

server.port=8080
spring.application.name=pkslow-gateway-service
spring.profiles.active=dev
spring.cloud.config.uri=http://localhost:8888
Copy the code

Obtain the client configuration again as follows:

$ curl http://localhost:8080/pkslow
{"webSite":"gateway-master.pkslow.com"."age":"9"."email":"[email protected]"}
Copy the code

The pkslow-gateway-service repository configuration has been read:

$ cat pkslow-gateway-service/config/application.properties 
pkslow.webSite=gateway-master.pkslow.com
pkslow.age=9
[email protected]
Copy the code

Create a branch release in the pkslow-gateway-service repository and add the configuration. Then modify the client configuration as follows:

server.port=8080
spring.application.name=pkslow-gateway-service
spring.profiles.active=dev
spring.cloud.config.label=release
spring.cloud.config.uri=http://localhost:8888
Copy the code

Re-access after restart, the configuration of the new branch is read correctly:

$ curl http://localhost:8080/pkslow
{"webSite":"gateway-release.pkslow.com"."age":"9"."email":"[email protected]"}
Copy the code

3.3 Client Configuration Takes Effect

After we modify the configuration, the results are as follows:

$ git commit -a -m "update"
[release 0e489fe] update
 1 file changed, 2 insertions(+), 2 deletions(-)

$ curl http://localhost:8888/pkslow-gateway-service/dev/release
{"name":"pkslow-gateway-service"."profiles": ["dev"]."label":"release"."version":"0e489fec5de73b1a6d11befa3f65e44836979e23"."state":null,"propertySources": [{"name":"/Users/pkslow/IdeaProjects/pkslow-modules/config-server/multiple-repos/pkslow-gateway-service/config/application.proper ties"."source": {"pkslow.webSite":"gateway-release.pkslow.com"."pkslow.age":"10"."pkslow.email":"[email protected]"}}]}

$ curl http://localhost:8080/pkslow
{"webSite":"gateway-release.pkslow.com"."age":"9"."email":"[email protected]"}
Copy the code

It turns out that the server has taken effect, but the client has not. This is because in this mode the client only reads the configuration at startup for it to take effect. If we want this to work for the client, we need to use the /refresh endpoint provided by Springboot ACTUATOR.

Add the ACTUATOR dependency first:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
Copy the code

To make /refresh accessible, add the following configuration:

management.endpoints.web.exposure.include=refresh
Copy the code

Add a @refreshScope annotation to the Controller as follows:

@RefreshScope
@RestController
public class PkslowController {
//xxx
}
Copy the code

Restart the application. Operation and effect are as follows:

# Modify the configuration and submit
$ git commit -a -m "update age to 18"
[release fc863bd] update age to 18
 1 file changed, 1 insertion(+), 1 deletion(-)

Server configuration takes effect
$ curl http://localhost:8888/pkslow-gateway-service/dev/release
{"name":"pkslow-gateway-service"."profiles": ["dev"]."label":"release"."version":"fc863bd8849fa1dc5eaf2ce0a97afb485f81c2f0"."state":null,"propertySources": [{"name":"/Users/larry/IdeaProjects/pkslow-modules/config-server/multiple-repos/pkslow-gateway-service/config/application.propert ies"."source": {"pkslow.webSite":"gateway-release.pkslow.com"."pkslow.age":"18"."pkslow.email":"[email protected]"}}}]# Client not in effect
$ curl http://localhost:8080/pkslow
{"webSite":"gateway-release.pkslow.com"."age":"10"."email":"[email protected]"}

Send a POST request to the client /refresh
$ curl -X POST http://localhost:8080/actuator/refresh
["config.client.version"."pkslow.age"]

# The client has taken effect
$ curl http://localhost:8080/pkslow
{"webSite":"gateway-release.pkslow.com"."age":"18"."email":"[email protected]"}
Copy the code

3.4 Automatic Configuration Update

It is not friendly to manually send a POST request to the client to update the configuration every time the code configuration is submitted. GitHub has a Webhook feature that sends a request to a specified URL after an event is triggered, so automatic updates can be implemented.

Automatic updates through the Webhook feature are one-to-one and cannot be implemented directly if there are many clients (which is usually the case). There are two options:

(1) Implement a port to accept the requests from Git, and then distribute them to various servers. This method is more troublesome, not very recommended.

(2) Refresh multiple clients by introducing Spring Cloud Bus. However, MQ, such as Kafka or RabbitMQ, needs to be introduced.

3.5 Differences in Service Discovery

In a microservice architecture, if you configure both the server and client to register with a service discovery (such as Eureka), the client does not need to configure the server address and will get the identification from the service discovery center. This is similar to Springboot Admin with Eureka.

Code is nothing special, is the server and client at the same time register to Eureka.

4 summarizes

This article uses examples to show you how to use Spring Cloud Config step by step, mainly to understand the interaction process and matching rules, the rest is code details, refer to the official documentation.

Some articles on configuration:

How does Java read the configuration file Properties (normal file system -classpath-jar-URL) from these four locations?

The @ConfigurationProperties annotation keeps the configuration neat and simple

I just want to record the use of @value in one post, and I don’t want to find anything else

Springboot integrates Jasypt, making it the most elegant and convenient way to configure information security

Use Spring Cloud Config to manage your configurations uniformly, and stop putting configuration files around

Spring Cloud Config integrates Spring Cloud Kubernetes and manages configuration on K8S


Welcome to follow the wechat public account < pumpkin shuo >, will continue to update for you…

Read more, share more; Write more. Organize more.