Problem statement
Before the advent of Microservices, we used to create a properties files in which we maintained our configurable parameters/properties. If we have a need to change any parameter values/adding/deleting a parameter, we generally needed to restart the application container. This is the same case in all environments like development, staging, and production.
Microservices architecture will be having a number of Microservices implemented, they may be talking to each other or independent services. Each microservice can connect to different sources to pull/push the data and serve the requests. We will be having the information of the respective source available in the configuration file. If there is any change in a property value like URL change, then we need to rebuild and deploy the Microservice, which is time-consuming in the age of Microservices.
Microservices strongly advocates the principle of “Build just once and deploy anywhere”. But for trivial configuration changes, we end up rebuilding the codebase and deploy again. I see a strong need for automating this and if an independent application can manage all the configurations then it will be an interesting and useful solution.
Configuration server exactly fits the bill here.
What is the configuration server?
Configuration server follows the client-server approach for storing and retrieving all configurations across multiple applications in all environments.
The configuration source can be Git or SVN or we can store in the local file or we can customize in the database and can be modified at the application runtime.
With Microservices, we create a central configuration server where all our configurable parameters of Microservices are written and version controlled. The benefit of a central config server is that if we change a property of a microservice, it will reflect that change on the fly without redeploying the respective client Microservice.
In this blog, I will focus on an example of config server with Git backend, use it in a simple Microservice application (ConfigClient)
Implement a config server:
1. Create ConfigServer using spring boot and update your pom.xml with the required dependencies
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
<?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.example</groupId> <artifactId>configuration-service</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.3.RELEASE</version> </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>Finchley.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> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/libs-milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> </repositories> </project> |
2. Create main class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
package com.example.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.config.server.EnableConfigServer; @EnableConfigServer @SpringBootApplication public class ConfigServiceApplication { public static void main(String[] args) { SpringApplication.run(ConfigServiceApplication.class, args); } } |
3. Create a file called ../src/main/resources/application.properties with the below content in it.
1 2 3 4 |
# Server port to be started on it server.port=8888 # The configuration file path (Git repository path) spring.cloud.config.server.git.uri=${USERPROFILE}/Desktop/config |
4. Create a project config, add a file called a-bootiful-client.properties to the project and commit it, with below content to GIT
1 2 |
# Test property message=Hello folk |
5. Confirm the config server data:
Start the ConfigServer application and access below URL to confirm the properties you configured
http://localhost:8888/a-bootiful-client/default
Implement a config client:
1. Create Config Client using spring boot and update your pom.xml with the required dependencies
pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
<?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.example</groupId> <artifactId>configuration-client</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.3.RELEASE</version> </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>Finchley.BUILD-SNAPSHOT</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> <repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/libs-milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository> <repository> <id>spring-snapshots</id> <name>Spring Snapshots</name> <url>https://repo.spring.io/libs-snapshot</url> <snapshots> <enabled>true</enabled> </snapshots> </repository> </repositories> </project> |
2. Create a file called ../src/main/resources/application.properties for client with the below content in it.
application.properties
1 2 3 4 5 6 |
# The configuration file name, which is committed to GIT spring.application.name=a-bootiful-client # ConfigServer URI: spring.cloud.config.uri=http://localhost:8888 # This enables the actuator/refresh API management.endpoints.web.exposure.include=* |
When we start the client application, the configuration values are read on the application startup, and not again. We can pull the updated values from the Config Server with a forced bean refresh. Annotate the Controller (MessageRestController) with the Spring Cloud Config @RefreshScope and then by triggering a refresh event (http://localhost:8080/actuator/refresh)
3. Create Client main class (ConfigClientApplication.java)
1 2 3 4 5 6 7 8 9 10 11 12 |
package com.example.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class ConfigClientApplication { public static void main(String[] args) { SpringApplication.run(ConfigClientApplication.class, args); } } |
4. Create a Controller (MessageRestController.java)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
package com.example.demo; import org.springframework.beans.factory.annotation.Value; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RefreshScope @RestController public class MessageRestController { @Value("${message:Hello default}") private String message; @RequestMapping("/message") String getMessage() { return this.message; } } |
Test the Config Server – Config Client applications:
1. Start the Config Server
2. Start the Config Client
3. Access the client message in the browser: http://localhost:8080/message
4. There you can see “Hello folk”, which we added in a-bootiful-client.properties file.
Test the updates in the properties file:
1. Change the message content in a-bootiful-client.properties file to “Hello Folks, how are you ?”
2. Commit the file
3. Refresh the config data with a POST request to
http://localhost:8080/actuator/refresh
4. Access the client message in the browser: http://localhost:8080/message
Summary
Now, you are able to create a configuration server with GIT backend and use it in your Microservices. You can use GIT/SVN/database as a source to your configurable data.
With this, you can avoid re-build & re-deploying your applications on each change.