How to Setup SPRING CLOUD CONFIG?

1. Introduction

Microservice architecture or simply Micro services has gained a lot of traction in recent times. They are better suited for agile methodology and ensure better continuous delivery. Big organizations like EBay, Amazon, and Netflix are embracing it.

Traditional monolithic applications operate as a single unit. A minor change in application, needs entire application to be deployed. There is no option to scale up/down certain modules in application independently, the entire application needs to scale up/down.


In Microservice, an application is deployed as a suite of smaller and independent applications each one managing a separate business operation/entity. These services can be independently developed (even in different languages), scaled up/down and deployed. They typically communicate with each other using REST API.



However there are also downsides to this approach:

Spring Cloud Config attempts to resolve the configuration drawback mentioned in #3.

2. Spring Cloud Config

Spring Cloud Config employs Server-Client approach for storing the configuration externally at 1 place. Any changes in configuration doesn’t need to deploy the associated services, in-fact these changes are reflected automatically.


2.1 HLD and Execution Flow


spring-cloud-config

2.2 Spring Cloud Config Components


Spring cloud Config consists of following components:


2.2.1 Git Repository

The file containing configuration (typically .properties or .yml) is hosted in git repository. The default implementation of the Spring cloud config storage uses GIT repository.
In this repository maintain properties for each environment (dev, local, prod) separately using profile specific config files such as application. Properties, application-local.properties, application-dev.properties.


2.2.2 Config Server

This is a standalone application wherein the path to git repository is configured. It also provides a REST API endpoints to read the configuration in the underlying git repository.


2.2.3 Config Clients

These are the individual Microservice, which binds to Spring Cloud Config Server.
All property changes in git repo are reflected without redeploying the Microservice.
Spring beans in this should be annotated with @RefreshScope, so that Whenever any parameter in configuration file hosted in git changes, on triggering /refresh  endpoint, the application context is reloaded with new config without application restart.

3. Project

In this project, we will create a 2 projects

3.1 Git Repository


Before starting with projects, let’s first create a git repository which will host central configuration.


1. Go to a folder and create an empty git repository in it.


spring-cloud-config

2. Create properties file in it.


Here I have create 2 files

application-dev.properties- which will be used when the active profile for client = dev

application-local.properties- which will be used when the active profile for client = local

The contents of application-local.properties is


spring-cloud-config

3. Add the files to staging area and commit the changes


spring-cloud-config

3.2 Config Server Project


This is a simple spring boot project with following structure


spring-cloud-config

3.2.1 Maven Dependencies

Here we are using latest version of spring-boot-starter as well as Spring Cloud Finchley Release Train.


<?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.hemant</groupId> <artifactId>cloud-config-server</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>cloud-config-server</name> <description>Spring Cloud Server</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.3.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <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> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>

3.2.2 Main Application Class :


We have added the EnableConfigServer annotation before the class so that project will act like a spring config server


packagecom.hemant.configserver; importorg.springframework.boot.SpringApplication; importorg.springframework.boot.autoconfigure.SpringBootApplication; importorg.springframework.cloud.config.server.EnableConfigServer; @SpringBootApplication @EnableConfigServer publicclassCloudConfigServerApplication { publicstaticvoidmain(String[] args) { SpringApplication.run(CloudConfigServerApplication.class, args); } }

3.2.2 Application properties


Here we have chosen server port as 8888.
spring.cloud.config.server.git.uri will bind the git repository.
Here we are using local git repo but we can also use the remote repository by changing to its URL.



server.port:8888 spring.application.name:config-server spring.cloud.config.server.git.uri=${HOME}/GIT_CONFIG

3.2.4 Start the config server


1. Go to the project’s root and issue
>>> mvncleaninstall
2. If build is success, launch the jar generated in the target folder
>>> java-jartarget/cloud-config-server-0.0.1-SNAPSHOT.jar
3. We can use REST Endpoints of the Config server for retrieving the configuration
>>> /{applicationName}/{profile}


GET http: //localhost:8888/config-server/local { "name": "config-server", "profiles": [ "local"], "label": null, "version": "55e871a4f85a6d70f7bf2931ba416e41a9c1cf3f", "state": null, "propertySources": [ { "name": "/Users/hemant/GIT_CONFIG/application-local.properties", "source": { "connect.host": "localhost", "connect.port": "90909", "connect.username": "root", "connect.password": "root", "welcome.message": "Hi User! Attempt 1" } } ] } GET http: //localhost:8888/config-server/dev { "name": "config-server", "profiles": [ "dev"], "label": null, "version": "55e871a4f85a6d70f7bf2931ba416e41a9c1cf3f", "state": null, "propertySources": [ { "name": "/Users/hemant/GIT_CONFIG/application-dev.properties", "source": "source": { "connect.host": "dev-server", "connect.port": "1234", "connect.username": "dev", "connect.password": "dev", "welcome.message": "Hi User! Attempt 1" } } ] }

Here the setup of config server is complete.


3.3 Config Client Application


Startbuilt a latest Spring Boot application that uses the Config Server to load its own configuration and that refreshes its configuration to reflect modifies in configuration on-demand continue start.

It is a spring-boot application, with following structure:


spring-cloud-config

3.3.1 Maven dependencies


Here we are using latest version of spring-boot-starter as well as Spring Cloud Finchley Release Train.


<?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.hemant</groupId> <artifactId>config-client</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>config-client</name> <description>Spring Cloud Config Client</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.1.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <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> <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> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>

Explanation :

1. Spring-boot-starter-web - for enabling MVC

2. Spring-cloud-starter-config - This dependency is used to connect to Config Server.

3. Spring-boot-starter-actuator - This dependency exposes operational endpoints of application. We’ll need /refresh endpoint to refresh the application context, when changes are made to git repository.


3.3.2 Main Application Class :


packagecom.hemant.configclient; importorg.springframework.boot.SpringApplication; importorg.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication publicclassConfigClientApplication { publicstaticvoidmain(String[] args) { SpringApplication.run(ConfigClientApplication.class, args); } }

3.3.3 Stateful Beans


In order to demonstrate the spring cloud configuration more clearly, we are creating a stateful spring bean here.


packagecom.hemant.configclient; importorg.springframework.boot.SpringApplication; importorg.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication publicclassConfigClientApplication { publicstaticvoidmain(String[] args) { SpringApplication.run(ConfigClientApplication.class, args); } }

The configuration class to create its bean is :


packagecom.hemant.configclient.config; import org.slf4j.Logger; import org.slf4j.LoggerFactory; importorg.springframework.beans.factory.annotation.Value; import org.springframework.cloud.context.config.annotation.RefreshScope; importorg.springframework.context.annotation.Bean; importorg.springframework.context.annotation.Configuration; /** * Config class used to create Connect object bean * @author hemant * */ @Configuration @RefreshScope publicclassConnectionConfig { privatestaticfinal Logger LOG = LoggerFactory.getLogger(ConnectionConfig.class); @Value("${connect.host}") private String host; @Value("${connect.username}") private String username; @Value("${connect.password}") private String password; @Value("${connect.port}") privateint port; /** * This method creates a stateful bean by using * configuration from config-server. * * Also its annotated with @RefreshScope, so that this will be * refreshed/reinitialized, at runtime * @return */ @Bean @RefreshScope public Connection mongo() { LOG.info("START : Connection Bean Creation"); Connection conn = new Connection(); conn.setHost(host); conn.setUsername(username); conn.setPassword(password); conn.setPort(port); LOG.info("END : Connection Bean Creation :{}", conn); return conn; } }

Here the bean is annotated with @RefreshScope, so that bean will be re-initialized at run-time.


3.3.4 Controller


We are also exposing the REST endpoints, so demonstrate how beans and properties are changed.


packagecom.hemant.configclient.web; importjava.util.HashMap; importjava.util.Map; importorg.springframework.beans.factory.annotation.Autowired; importorg.springframework.beans.factory.annotation.Value; import org.springframework.cloud.context.config.annotation.RefreshScope; importorg.springframework.web.bind.annotation.RequestMapping; importorg.springframework.web.bind.annotation.RequestMethod; importorg.springframework.web.bind.annotation.RestController; importcom.hemant.configclient.config.Connection; /** * Controller for exposing how the state (beans + properties) * have changed on the fly. * * This controller bean is annotated with @RefreshScope so that, * its reinitialized on the fly. * * @author hemant * */ @RestController @RefreshScope publicclassController { @Autowired private Connection connection; @Value("${welcome.message}") private String welcomeMessage; @RequestMapping(value = "", method = RequestMethod.GET) public Map < String, String > get() { Map < String, String > map = newHashMap < > (); map.put("connectionProperties", connection.toString()); map.put("welcomeMessage", welcomeMessage); return map; } }

3.3.5 Application Properties


spring.application.name:config-client server.port:8080 spring.cloud.config.uri=http://localhost:8888 management.endpoints.web.exposure.include=refresh

Here application.properties doesn’t have any specific configuration parameters, this is because it gets its properties from the config-server.


The significance of handful properties configured here is :



3.3.6 Starting the config-client :


1. Go to the project’s root and issue
>>>mvncleaninstall
2. If build is success, launch the jar generated in the target folder
>>>java-jar-Dspring.profiles.active=localtarget/config-client-0.0.1-SNAPSHOT.jar
Here we have selected the active profile as “local”, so that it will read properties from “application-local.properties” from git repo. Changing the same to “dev”, means it will read properties from application-dev.properties from git repo.


4. Demonstration

To demonstrate the application :


1. Let’s first hit the client-config endpoint, to get the latest client data


GET http://localhost:8080 { "connectionProperties": "Connection [host=localhost, port=90909, username=root, password=root]", "welcomeMessage": "Hi User! Attempt 1" } These properties are same which were defined in application-local.properties in git repository.

2. Make changes in Git Repo


We have made changes to application-local.properties, so that it looks like


spring-cloud-config

Commit the changes


spring-cloud-config

3. Confirm if the properties are changed in client:


GET http://localhost:8080 { "connectionProperties": "Connection [host=localhost, port=90909, username=root, password=root]", "welcomeMessage": "Hi User! Attempt 1" } These properties are same which were defined in application-local.properties in git repository.

You can find, that old properties are still reflected inspite of changing the file in git repository and committing the changes. This is where the /refresh endpoint of actuator comes in.


4. Trigger Refresh Endpoint on Client:


We can use CURL to trigger the same or any other REST client.


spring-cloud-config

The response is list of configuration properties which are changed.


5. Now Again hit client endpoint


GET http://localhost:8080 { "connectionProperties": "Connection [host=localhost, port=27017, username=local, password=root]", "welcomeMessage": "Hi LOCAL! Attempt 2" }

Thus here we can see

1. The properties were refreshed.

2. The beans with @RefreshScope was also reinitiated.



5. Conclusion

Thus we have accomplished:

  • img
  • img
  • img
  • img
  • img
  • img
  • img
  • img
  • img
  • img
  • img
  • img
  • img
  • img
  • img
  • img
  • img