Introduction
In modern software development, managing configurations across multiple applications and environments is crucial for reliability and scalability. Spring Cloud Config, a feature of the Spring framework, offers a centralized solution for handling configurations in Spring applications. By centralizing configurations and providing a server-client architecture, Spring Cloud Config simplifies configuration management and ensures applications operate with up-to-date settings.
We can store the configuration in Git version control or in the file system as well. Here we are going to see two spring boot applications: one will act as a spring cloud config server and the other will be the client application.
This guide explores the architecture, functionalities, and benefits of Spring Cloud Config, empowering developers to streamline configuration workflows and enhance application reliability.
Spring Cloud Config Server Architecture
Once done with adding the properties to application.yml file, create the configuration file in the path mentioned for "search-locations".
Spring Cloud Starter Config: Required Dependencies
The server project is relying on “spring-cloud-config-server” dependency,
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
The client application is relying on “spring-cloud-config-client” dependency,
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
<version>2.2.1.RELEASE</version>
<scope>runtime</scope>
</dependency>
Note: You can also use spring-cloud-starter-config dependency instead of spring-cloud-config-client.
Spring Cloud Config Server Implementation
To make the spring application into Cloud Config Server first we need to mention the @EnableConfigServer annotation.
@EnableConfigServer
@SpringBootApplication
public class SpringConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(SpringConfigServerApplication.class, args);
}
}
Next, we need to add the below properties in the application.yml file
Storing Configuration in the Local File System
spring:
application:
name: config-server
profiles:
active: native
cloud:
config:
server:
native:
search-locations: file:///C:/Documents/config
The native property tells the server to search for the configuration in the local file system instead of Git. And search-locations is the path of the local file system where you are storing the configuration file.
Storing Configuration in Git
spring:
application:
name: config-server
cloud:
config:
server:
git:
uri: <git url>
Storing configuration in Multiple Locations
spring:
application:
name: config-server
profiles:
active: native, git
cloud:
config:
server:
native:
search-locations: file:///C:/Documents/config, file:///C:/Documents/config
git:
uri: <git url>
repos:
testapp:
pattern: test-app
uri: <git url>
You can mention multiple search locations for the file system separated by a comma and for multiple Git locations use repos and pattern tags to differentiate the URL so in the above example if the URL has a pattern “test-app” like “localhost:8080/test-app/development” the config server fetch the value from the URI mentioned for test-app else by default it will fetch from “git.uri” location.
In this demo, I will go with storing the configuration in the local file system. Once done with adding the properties to application.yml file, create the configuration file in the path mentioned for "search-locations".
Starting Config Server
Step 1 : I have created a file named “redis-client-development.yml” in C:Documentsconfig. The naming convention of the file name should be that the first part that is "redis-client" should match the client application name, and the last part that is "development" shows the profiling for the client application. You can have different files with different profiling like production, and QA.
Example file names for different profiles - redis-client-production.yml,redis-client-qa.yml
The file contains the configuration below, which I want to externalize.
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/test
username: test
password: test
Step 2: Start the application
Once you start the application successfully, you hit the endpoint
"http://localhost:9898/redis-client/development" then the response will be:
{
"name":"redis-client",
"profiles":[
"development"
],
"label":null,
"version":null,
"state":null,
"propertySources":[
{
"name":"file:///C:/Documents/config/redis-client-development.yml",
"source":{
"spring.datasource.driver-class-name":"com.mysql.jdbc.Driver",
"spring.datasource.url":"jdbc:mysql://localhost:3306/test",
"spring.datasource.username":"test",
"spring.datasource.password":"test"
}
}
]
}
The development in the URL will be considered as profiling so we have to mention it after "/".
Spring Cloud Config Client Implementation
The client application is going to be a simple one. We need to add the required dependency for the config client.
Then we have to add the below properties to our bootstrap.yml file.
spring:
application:
name: redis-client
profiles:
active: development
cloud:
config:
uri: http://localhost:9898
Here cloud.config.uri is the URL of the config server. So the Spring boot will understand that it has to fetch configurations from that URL.
To show that the client application fetches the values from the config server I have created a java file in the client application that populates all its required configurations from the config server.
package com.project.redisdemo.configuration;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
@Configuration
public class DataSourceConfig {
@Value("${spring.datasource.driver-class-name}")
private String driverClassName;
@Value("${spring.datasource.url}")
private String url;
@Value("${spring.datasource.username}")
private String username;
@Value("${spring.datasource.password}")
private String password;
@Bean
public DataSource getDataSource() {
DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create();
System.out.println("********************************driverClassName - "+driverClassName);
dataSourceBuilder.driverClassName(driverClassName);
dataSourceBuilder.url(url);
dataSourceBuilder.username(username);
dataSourceBuilder.password(password);
return dataSourceBuilder.build();
}
}
This class has @configuration annotation so that the file will get loaded at server startup and also I have added a print statement to show that the values have been fetched from the config server. Now we can start the client application.
Client Application Logs
2021-08-29 20:16:57.850 INFO 10268 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2021-08-29 20:16:57.850 INFO 10268 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 2789 ms
********************************driverClassName - com.mysql.jdbc.Driver
Loading class `com.mysql.jdbc.Driver'. This is deprecated. The new driver class is `com.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.
2021-08-29 20:16:58.112 INFO 10268 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Starting...
2021-08-29 20:16:58.119 WARN 10268 --- [ main] com.zaxxer.hikari.util.DriverDataSource : Registered driver with driverClassName=com.mysql.jdbc.Driver was not found, trying direct instantiation.
2021-08-29 20:16:58.634 INFO 10268 --- [ main] com.zaxxer.hikari.HikariDataSource : HikariPool-1 - Start completed.
In the above logs, you can see the MySQL driver class name has been fetched from the config server.
Conclusion
Now, we are able to create a config server that fetches the config properties from our local file system. And now we can also build the centralized configuration for microservices.
- - - - - -