Enterprise Java

Intro to Spring Cloud Config Server

1. Overview

In this tutorial, we will review the basics of Spring Cloud Config Server. We will setup a Config Server and then build a client application that consumes the configuration on startup and then refreshes the configuration without restarting. The application we are building is the same “Hello World” application discussed in the Centralized Configuration Getting Started Guide, but we go into more depth about the concepts of Spring Cloud Config Server in this article.

The full source code for the tutorial is on Github.

2. What is Spring Cloud Config Server?

As the documentation succinctly states, “Spring Cloud Config provides server and client-side support for externalized configuration in a distributed system.” The default implementation of the server storage backend uses git, so it supports labelled versions of configuration environments with ease and is accessible to many tools for managing the content.

Spring Cloud Config fits very well into Spring applications because its concepts of both client and server map precisely to the Spring Environment and PropertySource abstractions. However, Spring Cloud Config can be used with any application running in any language.

3. Create a Multi Module Project

The application we are creating will have two modules: one for the Configuration Service and the other for the Configuration client. Because of this, we need to create a parent pom.

3.1 Parent

In our IDE, let’s create a new project. I’m using Spring Tool Suite, but that’s just a personal preference.

In our pom.xml, let’s specify our two modules:

<?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.michaelcgood</groupId>
	<artifactId>com.michaelcgood</artifactId>
	<version>0.0.1</version>
	<packaging>pom</packaging>

	<name>michaelcgood-spring-cloud-config-server</name>
	<description>Intro to Spring Cloud Config Server</description>


    <modules>
        <module>mcg-configuration-client</module>
        <module>mcg-configuration-service</module>
    </modules>

</project>

3.2 Configuration Service

In our IDE, let’s create a new Maven module for our configuration service and insert this in our pom:

<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.michaelcgood</groupId>
  <artifactId>mcg-configuration-service</artifactId>
  <version>0.0.1</version>
  <packaging>jar</packaging>
  <name>mcg-configuration-service</name>
 
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.9.RELEASE</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-config-server</artifactId>
        </dependency>

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

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Edgware.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

3.3 Configuration Client

Now we just need to make a module for our configuration client. So, let’s make another Maven module and insert this into our pom:

<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.michaelcgood</groupId>
  <artifactId>mcg-configuration-client</artifactId>
  <version>0.0.1</version>
  <packaging>jar</packaging>
  	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.9.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<java.version>1.8</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-config</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>Edgware.RELEASE</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
</project>

Our project structure looks like this now:

4. Config Server

Now we will create a Config Service to act as an intermediary between our client and a git repository.

4.1 Enable Config Server

We use Spring Cloud’s @EnableConfigServer to create a config server that can be communicated with. So, this is just a normal Spring Boot application with one annotation added to enable the Config Server.:

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

4.2 application.properties

To ensure that there is no conflict between ports for our Config Service and client, we specify a different port for the Config Service:

server.port=8888

spring.cloud.config.server.git.uri=${HOME}/Desktop/mcg-config

The second line spring.cloud.config.server.git.uri=${HOME}/Desktop/mcg-config points to a git repository, which we will create next.

4.3 Git

On a *nix system, we can do everything on the command line.
We make a folder on our desktop:

mkdir mcg-config

We create a file named a-bootiful-client.properties using vim:

vim a-bootiful-client.properties

We add the message, “Hello World” but this could be whatever we would like. After we write (:w) we quit (:q) vim.

Now let’s create a new repo:

git init

Now we add the file that contains our message:

git add a-bootiful-client.properties

Let’s commit:

git commit

 5. Configuration Client

Now let’s create a new Spring Boot application that uses the Config Server to load its own configuration and that refreshes its configuration to reflect changes to the Config Server on-demand, without restarting the JVM.

Spring will see the configuration property files just like it would any property file loaded from application.properties, application.yml or any other PropertySource.

5.1 Reflecting Changes

The client may access any value in the Config Server using the standard Spring ways, such as @ConfigurationProperties or @Value(“${…​}”).

With this in mind, we create a REST controller that returns the resolved message property’s value:

@SpringBootApplication
public class ConfigClientApplication {

    public static void main(String[] args) {
        SpringApplication.run(ConfigClientApplication.class, args);
    }
}

@RefreshScope
@RestController
class MessageRestController {

    @Value("${message:Hello default}")
    private String message;

    @RequestMapping("/message")
    String getMessage() {
        return this.message;
    }
}

The default configuration only allows the values to be read on the client’s startup and not again. So, using @RefreshScope we force the bean to refresh its configuration, which means it will pull updated values from the Config Server, and then trigger a refresh event.

5.2 bootstrap.properties

The properties to configure the Config Client must be read in before the rest of the application’s configuration is read from the Config Server, during the bootstrap phase.

We specify the client’s spring.application.name and the location of the Config Server spring.cloud.config.uri:

spring.application.name=a-bootiful-client
spring.cloud.config.uri=http://localhost:8888
management.security.enabled=false

Notice:
We disabled security with our setting management.security.enabled=false to make testing and tinkering easy for us.

6. Demo

First we need to change directory to our configuration service and start it:

mcg-configuration-service mike$  mvn spring-boot:run

And then do the same for our client:

mcg-configuration-client mike$  mvn spring-boot:run

We can see in our terminal for configuration service when the a-bootiful-client.properties is added:

INFO 5921 --- [nio-8888-exec-1] o.s.c.c.s.e.NativeEnvironmentRepository  : Adding property source: file:/var/folders/dk/48l9cm2x3vnfl5ymh6dtxpwc0000gn/T/config-repo-7195892194658362240/a-bootiful-client.properties

Let’s open our browser and visit http://localhost:8080/message. We see “Hello World”.

Now let’s change the message in a-bootiful-client.properties again and this time put, “Hi! :-)”.

After saving and doing a commit, we visit http://localhost:8888/a-bootiful-client/default to confirm our change.

Now we invoke the Spring Boot Actuator refersh endpoint to refresh our client:

curl -X POST http://localhost:8080/refresh

We visit http://localhost:8080/message and see our message “Hi! :-)” is displayed.

For more information on Spring Boot Actuator, see the tutorial Building Spring Boot RESTful Service + Spring Boot Actuator.

7. Conclusion

We just completed centralizing configuration of our services in Spring. We accomplished this by standing up a Spring Cloud Config Server and creating a client to consume the configuration on startup and then refresh the configuration without restarting.

Many other things can be done with Spring Cloud Config Server that we did not touch on, such as:

  • Have the Config Server register with the Discovery Service for Spring Cloud Netflix, Eureka Service Discovery or Spring Cloud Consul
  • Serve configuration in YAML or Properties format
  • Serve plain text configuration files
  • Embed the config server in an application

The full source code can be found on Github.

Published on Java Code Geeks with permission by Michael Good, partner at our JCG program. See the original article here: Intro to Spring Cloud Config Server

Opinions expressed by Java Code Geeks contributors are their own.

Michael Good

Michael is a software engineer located in the Washington DC area that is interested in Java, cyber security, and open source technologies. Follow his personal blog to read more from Michael.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button