Spring Cloud Config Server With File System

Spring Boot is a popular choice while developing microservices. A microservice architecture may contain a large number of small services.

Each service can have its own application configurations placed under the application’s classpath. Maintaining these configuration properties becomes very hard when the number of services increases.

A possible solution for this can be using a common place to handle the configuration properties. We can set up a configuration server and other services can use this configuration server for required configuration properties.

First, We need to create a configuration server. This centralized configuration server contains configuration properties related to all other microservices.

Setting up configuration server

Let’s set up our configuration server now.

Create a spring boot application with spring-cloud-config-server dependency.

Add required dependency

Following is the pom.xml file contains the required dependencies for our configuration server.

<?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>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.6.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.asb.example</groupId>
	<artifactId>spring-boot-config-server</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>spring-boot-config-server</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
		<spring-cloud.version>Greenwich.SR2</spring-cloud.version>
		<maven-jar-plugin.version>3.1.1</maven-jar-plugin.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>${spring-cloud.version}</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>

Add required configuration properties

Add the following configuration properties into the application.properties file.

server.port=8888
spring.profiles.active=native
spring.cloud.config.server.native.search-locations=classpath:/config/simple-service/
  • server.port : This is the port of configuration server.
  • spring.profiles.active : Since we are using file system back end, we have to set this property value to native.
  • spring.cloud.config.server.native.search-locations : This is the configuration file path(System file path, which is placed anywhere in current system).

Let’s place configuration files inside the directory pointed by the property spring.cloud.config.server.native.search-locations. In our example, we will use /src/main/resources/config directory of the spring boot application.

Create a simple-service folder under the /src/main/resources directory.

Create two configuration .yml files with names simple-service-dev.yml and simple-service-prod.yml.

These files should have the name with the pattern applicationName-profile.yml. Our client spring boot application will also have spring application name set to simple-service, with two active profiles dev and prod.

Both files contain a single configuration property my.prop with some random value.

simple-service-dev.yml

my:
  prop: Hello Simple Service DEVELOPMENT Environment!!!

simple-service-prod.yml

my:
  prop: Hello Simple service PRODUCTION Environment!!!!!

Enable configuration server by using @EnableConfigServer

Enable configuration server by using the @EnableConfigServer annotation to spring boot application class.

@SpringBootApplication
@EnableConfigServer
public class SpringBootConfigServerApplication {

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

Test the configuration server

Our configuration server with the file system back end is ready to launch!! Start the server by running the spring boot application.

configuration server started

Now we can check if the configuration server is working as expected. We can also access our two configuration properties by hitting respective endpoints.

Profile dev configuration file is available at http://localhost:8888/simple-service/dev.

configuration server profile dev

Also, the prod profile configuration file is available at http://localhost:8888/simple-service/prod.

configuration server profile prod

Setting up configuration client

Our configuration server is ready. Let’s create a configuration client service now.

Add required dependency

Create a spring boot application with dependency spring-cloud-config-client. The complete pom.xml file is given below.

<?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>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.6.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.asb.example</groupId>
	<artifactId>simple-service</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>simple-service</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
		<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
		<spring-cloud.version>Greenwich.SR2</spring-cloud.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-config-client</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>${spring-cloud.version}</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>

Create bootstrap configuration file

create a file bootstrap.properties under the directory src/main/resources/ and also add the following properties.

spring.profiles.active=dev,prod
spring.application.name=simple-service
spring.cloud.config.url=http://localhost:8888
  • spring.profiles.active : This property can be used to set active spring boot profile. We have added dev and prod in above example.
  • spring.application.name : This is the spring application name. Also, make sure that the name should match with the configuration file name(Ex : simple-service-dev.properties)
  • spring.cloud.config.url : This property points to the config server path.

Testing config clients

Modify client spring boot application class by adding a rest endpoint to check the configuration properties.

package com.asb.example;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
public class SimpleServiceApplication {

	@Value(value = "${my.prop}")
	private String myProp;
	
	@GetMapping("/")
	public String getMyProp() {
		return "Prop value: " + myProp;
	}
	
	public static void main(String[] args) {
		SpringApplication.run(SimpleServiceApplication.class, args);
	}
}

Finally, run the config client applications with different profiles and ports using command line arguments. Let’s use port 8080 for the dev profile and 8090 for the prod profile.

Also, if you are using IDE like STS, we can run the application by Right click > Run as > Run Configurations. Here, specify the command line arguments under the Arguments tab as shown below.

command line argument sts.

Once both the servers are started, we can access them under the URL http://localhost:8080 for dev profile and the URL http://localhost:8090 for the prod profile.

config-client-dev
config-client-prod

We have accessed configuration properties from the configuration server.

Detect configuration changes with @RefreshScope

What if the configuration properties change when the application is running? Spring cloud configuration server also exposes the modified properties without requiring any changes or server restart.

Client service will not detect any changes that occurred to configuration properties, as it loads configuration properties during application startup.

To detect modified configuration properties, we have to annotate the configuration client service with the @RefrershScope annotation. Note that only custom property value changes are detected from this annotation.

Update config client service as shown below.

@SpringBootApplication
@RestController
@RefreshScope
public class SimpleServiceApplication {
	//.....
}

Also, add spring-boot-starter-actuator dependency to expose /refresh endpoint for the client server.

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

By default, this endpoint is not enabled in spring boot 2 applications. To enable it to add the following configuration property to the application.properties file.

management.endpoints.web.exposure.include=refresh

Update prod profile property to inside the simple-service-prod.yml file to some different value as shown below.

Make sure both config server and client service are running before modifying configuration value.

my:
  prop: Hello Simple service PRODUCTION Environment Updated!!!

Now access the refresh endpoint using postman. The refresh endpoint is exposed as the POST endpoint, so we can not access it from the browser.

Refresh actuator end point

Refresh the browser now. We should be able to get an updated configuration property value.

refresh scope example spring boot

Finally, we have refreshed the configuration property without restarting the server.

Conclusion

In this article, we learned how to set up the spring boot cloud configuration server. We also created a simple service, used as a configuration client to read the property values.

We also learned how to refresh the modified configuration property values without server restart.

Source code is available on Github.