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. |