Spring Cloud Config Server

Spring Cloud Config Server

This article teaches you a very important software engineering practice in today’s’ industry. That is how to set up a Spring Cloud Config Server. Let’s imagine your web application has many servers and you want to change a property value of it. Now the problem is coming on to the table. What would you do? Definitely you will have to rebuild your application on all servers. It would cause downtime in your application and it is a complex task. To avoid those kinds of problems you can keep your application properties apart from the main application. Please refer to the steps are given below to create the config server to maintain your application properties files. To accomplish this you have to create 3 applications.

  1. The client application (This is your main application)
  2. The config server
  3. The config repository (It keeps your application properties files)

Creating the Client Application

Firstly. you have to create the client application. This is the structure of a client application as below.

This is the pom file.

<?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 https://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.2.0.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.myapp</groupId>
	<artifactId>config-client</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>config-client</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
		<spring-cloud.version>Hoxton.M3</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-starter-config</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</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>

	<repositories>
		<repository>
			<id>spring-milestones</id>
			<name>Spring Milestones</name>
			<url>https://repo.spring.io/milestone</url>
		</repository>
	</repositories>

</project>

Application properties as below, You can keep many application resources files according to your environments. In this case, I’m using local, development, staging and production environments.

spring.application.name=myapp
spring.cloud.config.uri=http://localhost:8888
server.port=8080

Creating the configuration class to get the application properties from the config repository.

package com.myapp.configclient.configurations;


import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Configuration;

import javax.validation.constraints.NotNull;

@RefreshScope
@Configuration
@ConfigurationProperties(value = "myapp.rabbitmq")
public class MyAppConfigurations {

    @NotNull(message = "myapp.rabbitmq.hosts cannot be null")
    private String hosts;

    @NotNull(message = "myapp.rabbitmq.port cannot be null")
    private String port;

    @NotNull(message = "myapp.rabbitmq.username cannot be null")
    private String username;

    public String getHosts() {
        return hosts;
    }

    public void setHosts(String hosts) {
        this.hosts = hosts;
    }

    public String getPort() {
        return port;
    }

    public void setPort(String port) {
        this.port = port;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }
}

Defining the active profile in the main class

package com.myapp.configclient;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ConfigClientApplication {

	public static void main(String[] args) {
		System.setProperty("spring.profiles.active", "dev");
		SpringApplication.run(ConfigClientApplication.class, args);
	}

}

Creating the controller class.

package com.myapp.configclient.controller;

import com.myapp.configclient.configurations.MyAppConfigurations;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestConfigServer {

    @Autowired
    MyAppConfigurations myAppConfigurations;


    @RequestMapping("hello/loadconfigs")
    public String loadConfigsFromServer() {

        return "host is :" + myAppConfigurations.getHosts() + " port is :" + myAppConfigurations.getPort() + " user is :" + myAppConfigurations.getUsername();
    }


}

Creating the Config Server Application

Then you have to create a config server application. Please walk through the steps are given below to create the config server application. The main business of it is to maintaining application properties away from the main application.

The structure of a config server application as below.

This is the pom file.

<?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 https://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.2.0.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.myapp</groupId>
	<artifactId>config-server</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>config-server</name>
	<description>Demo project for Spring Boot</description>

	<properties>
		<java.version>1.8</java.version>
		<spring-cloud.version>Hoxton.M3</spring-cloud.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>
			<exclusions>
				<exclusion>
					<groupId>org.junit.vintage</groupId>
					<artifactId>junit-vintage-engine</artifactId>
				</exclusion>
			</exclusions>
		</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>

	<repositories>
		<repository>
			<id>spring-milestones</id>
			<name>Spring Milestones</name>
			<url>https://repo.spring.io/milestone</url>
		</repository>
	</repositories>

</project>

Add these properties to application.resources. Don’t use 8080 as the port since the client application is running on that port.

#URL of myapp-config-repository git repository
spring.cloud.config.server.git.uri=https://github.com/rajithabhanuka/myapp-config-repository.git

#GIT branch ( I'm using master branch)
spring.cloud.config.server.git.default-label=master

#if you are using private repository give your username of git
#spring.cloud.config.server.git.username=test

#if you are using private repository give your password of git
#spring.cloud.config.server.git.password=test

#Port of the config server
server.port=8888

Add below annotation to the main class

package com.myapp.configserver;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.config.server.EnableConfigServer;

@EnableConfigServer
@SpringBootApplication
public class ConfigServerApplication {

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

}

Creating the Config Repository.

This is not a project. It is just a location but in this case, I pushed it on to GIT hub. The structure of my repository as below.

There are 4 properties files in this case to store different properties relevant to loc, dev, stg and prd properties. Below are the dev.properties.

myapp.rabbitmq.hosts=192.168.1.2
myapp.rabbitmq.port=5672
myapp.rabbitmq.username=dev_user

Finally, you can start both config server and client application. After that, you can type http://localhost:8080/hello/loadconfigs on your web browser. The result will be as below.

GIT locations of the applications –
Spring Client – https://github.com/rajithabhanuka/myapp-config-client.git
Spring Cloud Server – https://github.com/rajithabhanuka/myapp-config-server.git
Config Repository – https://github.com/rajithabhanuka/myapp-config-repository.git