Spring cloud – Configuration via config server

Application usally are more than just code in one file. They need to connect to a Database, MQ, talk to other application etc. These configuration like db url/credentials, Other service calls, or simple key/value that is supplied via some properties file.  Following are the various configuration options that we use:

  • Package configuration files with application : This is quite common that we have say a properties files within our classpath from which our code reads. The problem with this approach is that if we want to change something , it would require a rebuild, then deploy and then start. We could have different env specific files like *-dev.properties, -prod.properties etc. but the problem still remails, for any change, we have to build -> deploy -> restart.
  • Configuration files in a common file system : Here we keep the configuration file in some well known file system, say a shared path or the home folder of the user in the vm/box. This works fine as long as you have a file system. In cloud deployments its not easy to get a shared file system or when you have many services running in cluster mode, to go and update file in all of the VMs home folder. Its a time consuming job where you could miss something.
  • Use environment variables : This is another approach, but setting up enviornment variables is different in different platforms. There is also a problem of managing large number of environmet variables and any duplication. Also this has to be done on every VMs and you cloud run out of env variables space.
  • Use a cloud-vendor specific solution : Different vendors like Heroku, Cloud foundry provides different solution to this, but you wind up coupling your application with the specific cloud provider which should be avoided.

Other challages of configuration:

  • Microservices: Most of the cloud based applications would be functionally decoupled and deployed as microservices. Microservices are designed to dependencies on other microservices which means we have a large set of configuration. For example Netflix has over 600+ microservices runing , so to manage the dependencies via conventional methods requires too much efforts.
  • Version Control : Its nice to have some tracesibility. Conventional way there is just the latest file. What was the env variables before or who changed some of the configuration is something thats should be traced.

Spring Cloud Config provides centralized, externalized , secured and easy to reach source of application configuration. Its just a certralized server to serv up configuration. The configuration itself can be backed by some source control. Clients connect to this config server via http and retrive their configuration settings. Config server itself could be run in multiple instances. The config server can serve up from a file system or a version control system like git.

spring-cloud-config0

Lets start buiding the config server first. We will build a simple spring boot project with the following pom.xml:


<?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>1.4.2.RELEASE</version>
<relativePath />
</parent>

<groupId>com.mynotes.spring.cloud</groupId>
<artifactId>config-server-demo</artifactId>
<version>1.0.0-SNAPSHOT</version>
<name>config-server-demo</name>
<description>Demo project for Spring Cloud cloud config</description>

<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>Camden.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
</dependencies>
</project>

You could directly use the spring-cloud-dependencies as your parent but if you need to have different parent (companies requiremtns etc) you can add via dependency management. As you can see we need only one dependency.

Next you need to define where config server should look for the config files.  One of the way is to use a source control like git. It could be just your local file system too. We will be using git. application.yml:

server:
port: 8888

spring:
cloud:
config:
server:
git :
uri: https://github.com/dhananjay12/spring-cloud-config
searchPaths: basic-demo

uri is your git repository path and searchPaths is the folder name it should look into. I already created a repo with the folder :

spring-cloud-config-repo

 

Writing Application.java to start up:


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

@SpringBootApplication
@EnableConfigServer
public class Application {

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

}

Thats it, we are done with the config server.

Now lets create the client application product-config-client. pom.xml


<?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>1.4.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.mynotes.spring.cloud</groupId>
<artifactId>product-config-client</artifactId>
<version>1.0.0-SNAPSHOT</version>
<name>product-config-client</name>
<description>Demo project for Spring Cloud cloud config</description>
<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>Camden.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>

As you can see we need congif client dependencies for this application to serve as client.

Simple rest controller ProductRestController.java:


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;

@RestController
@RefreshScope
public class ProductRestController {

@Value("${display.offers.message}")
private String message;

@RequestMapping("/offer-message")
public String offerMessage() {
return this.message;
}

}



Notice the @RefreshScope at the top. Whatever beans have this annoatation, spring will recreate the same without the service restart and the old bean is discarded. This is usually triggered by soe event. Because the client to this bean if any gets a proxy object, they dont know if the actual bean has changed. They remain unaffected.

Starting point application.java:



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

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

}

Now we need to add 2 properties which we need to configure as soon as the server starts up. This needs to be in a bootstrap.yml/properties file since its in the startup process.

spring:
application:
name: product-config-client
cloud:
config:
uri: http://localhost:8888

Note that whatever name give here should be the name of the file config server searches in the git repo. I already created product-config-client.yml in the git folder.

spring-cloud-config-repo1

Starting config server first which will run on port 8888. You can check what properties it took for client via http://localhost:8888/product-config-client/master

spring-cloud-config-server

Then starting client server on port 8080. and checking the rest endpoint.

spring-cloud-config3

Lets go and change the config file in github to a new value. Now visit the config server url. You can see the changes there immediately, but the product client doenst have the new value. You have to trigger it.  Lets trigger the change via curl command. This is equivalent to an empty post request.


curl -d{} http://localhost:8080/refresh

spring-cloud-config2

There are other ways to refresh like active MQ etc.

Check the client logs, you will see some refresh related logs. Check the rest endpoint again.

spring-cloud-config1

If profiles are set, spring will use that profile file , for example if prfile is set to 'europe', spring will give preference to product-config-client-europe.yml. and so on.

What is the config server is down?
First of all Spring cloud config server should typically run on multiple instances, so downtime wont create an issue. Even if its down, clients application can control how to handle missing configuration server. There is a setting called spring.cloud.config.failFast=true (default is false). The default is the client will not fail. If you dont want that behaviour, set it as true i.e it wont run. You can have a fallback local setting. The configuration server setings will simply override the local.

 

Advertisements
%d bloggers like this: