Enable Javascript

Please enable Javascript to view website properly

Toll Free 1800 889 7020

Looking for an Expert Development Team? Take 2 weeks Free Trial! Try Now

How to Setup SPRING CLOUD CONFIG?

1. Introduction

Microservice architecture or simply Microservices has gained a lot of traction in recent times. They are best 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 the application needs an entire application to deployed. There is no option to scale up/down certain modules in the 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 develop (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:

  • Management/Monitoring of distributed services becomes painful as their number grows.
  • Debugging a bug that spans multiple services is difficult.
  • Each service maintains its configuration (typically in a properties/yml file). It’s also common that certain config parameters are use in multiple services. When a config parameter has to change, we need to track all the services using it, change the value in all of them and build and deploy each dedicated Java Developers.

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

2. Spring Cloud Config

Spring Cloud Config employs a Server-Client approach for storing the configuration externally in 1 place. Any configuration changes don’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 a git repository. The default implementation of the Spring cloud config storage uses the GIT repository.

This repository maintains 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 the git repository is configured. It also provides REST API endpoints to read the configuration in the underlying git repository.

2.2.3 Config Clients

These are the individual Microservice, which bind 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 the 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

  • Git backed config server
  • Config client microservice which binds to Config server. All configuration changes will be reflected without redeploying them.

3.1 Git Repository

Before starting with projects, let’s first create a git repository that will host the 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 the 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 started.

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

spring-cloud-config

3.3.1 Maven dependencies

Here we are using the 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 the operational endpoints of the application. We’ll need /refresh the endpoint to refresh the application context when changes are made to the 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 don’t have any specific configuration parameters, this is because it gets its properties from the config-server.

The significance of a handful of properties configured here is:

  • spring.application.name - name of the application
  • server.port - the application port
  • spring.cloud.config.uri - the URL of config server, we created above
  • management.endpoints.web.exposure.include - We had added spring-boot-starter-actuator in our pom, which exposes operational endpoints of our application. They include /health, /info, /metrics, /refresh. Of these we only want /refresh endpoint to refresh the application context, when changes are made to git repository.

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.

Conclusion

Thus we have accomplished:

  • Centralized configuration of our services in spring. This configuration is hosted in a git repository.
  • Created Cloud Config Server which secures the get store
  • Created Cloud Client services to devour the configuration on startup and then refresh the configuration without restarting.
  • Depending on the client application’s active profile, changes are only reflect from the corresponding file.
Software Development Team
Need Software Development Team?
captcha
🙌

Thank you!
We will contact soon.

Oops! Something went wrong.

Recent Blogs

Categories

NSS Note
Trusted by Global Clients