Sunday, February 25, 2024

Spring Boot Microservice - Externalized Configuration With Spring Cloud Config

In this tutorial, you'll see how to externalize configurations of the Spring boot microservices using Spring cloud config server.

Spring cloud config server

With the Config Server, you have a central place to manage external properties for applications across all environments. This centralized configuration makes it easy to manage configuration when you have multiple microservices.

As your microservice move through various deployment stages from dev to qa to production, you can manage the configuration between those environments easily with externalized configuration and be certain that applications have everything they need to run when they are deployed.

How does Spring cloud config server work

You can create a config server application with the necessary information about the repository where configuration files are kept. You can also use discovery server to register the config server application.

In the microservices you can provide the location of the config server using spring.config.import property. This is now the default way to bind to Config Server.

Spring Cloud Config

Spring Cloud config example

In this microservice externalized configuration using cloud config example, we'll use the example shown in this post- Spring Boot Microservice - Service Registration and Discovery With Eureka as our base example and make changes in it to externalize the configuration.

In the example we'll have two services CustomerService and AccountService to cater to customer and account related functionalities respectively. When customer information is retrieved it should also get the accounts associated with the specific customer. For getting the associated accounts for a customer we'll have to make a call from CustomerService to AccountService.

1. Config server application

We'll create config server as a separate application which also get registered with Eureka.

If you are using Spring Tool Suite, create a new Spring starter project and give the name as config-server. Give other details like Group and Artifact Id. In the dependencies select Config Server and Eureka Discovery Client as dependencies. Created pom.xml should be something like this.

<?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>3.1.2</version>
    <relativePath/> <!-- lookup parent from repository -->
  </parent>
  <groupId>com.netjstech</groupId>
  <artifactId>configserver</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>config-server</name>
  <description>Configuration Server</description>
  <properties>
    <java.version>17</java.version>
    <spring-cloud.version>2022.0.4</spring-cloud.version>
  </properties>
  <dependencies>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-config-server</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>

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

</project>

2. Using @EnableConfigServer annotation

In the generated ConfigServerApplication class add the @EnableConfigServer annotation to mark this application as a config server.

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

@EnableDiscoveryClient
@EnableConfigServer
@SpringBootApplication
public class ConfigServerApplication {

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

@EnableDiscoveryClient is used to mark that this application is a discovery client and should be registered by the discovery server.

Note that @EnableDiscoveryClient is no longer required. You can put a DiscoveryClient implementation on the classpath to cause the Spring Boot application to register with the service discovery server.

3. Configuration for the config server

Config server needs to know the location of the repository where the configuration properties are saved. You can use GIT, BitBucket, DB, AWS S3 and many other options as your repository. In this example GIT is used as the repository of configuration files.

Give the location of the repository with this property- spring.cloud.config.server.git.uri

For example, if I have created a new repository named config-server-repository in github then I need to give location of this repository.

In your config-server application under src/main/resources create a file named application.yml and give the configuration similar to as given below.

server:
  port: 8084
  
spring:
  application:
    name: configserver
  cloud:
    config:
      server:
        git:
          uri: https://github.com/netjs/config-server-repository
          default-label: main
          username: GITHUB_USER
          password: GITHUB_PASSWORD
          clone-on-start: true
      
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka 

server.port specifies the port config server listens on.

spring.application.name specifies the name of the application. The application gets registered with the discovery server with this name.

default-label: main means config server will try to get configuration stored in the main branch in the repository.

clone-on-start: true means repository should be cloned on startup

With this step done, config-server application is ready.

Changes in customer-service configuration to use config server

1. Adding dependencies

Your microservices should get configuration data through config-server now, so first thing is to set micro-services as config-client. For that add spring-cloud-starter-config dependency in the pom.xml of customer-service

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-config</artifactId>
</dependency>

2. Changes in application.yml

In the application.yml file comment the existing configuration and add the following.

spring:
  application:
    name: customer-service
  profiles:
    active: dev
    
  config:
    import: configserver:http://localhost:8084

In the config clients you can provide the location of the config server using spring.config.import property.

3. Adding configuration to repository

One important thing in the configuration is active profile which is dev. By default config-server looks for the file with the name in the format APPLICATION_NAME-PROFILE.yml (or .properties). For example, as per the configuration given above config-server will try to load customer-service-dev.yml file from the configured GIT repository. That way based on the current profile (dev, qa, prod), config server can load the correct file (APPLICATION_NAME-dev.yml, APPLICATION_NAME-qa.yml, APPLICATION_NAME-prod.yml).

You can create a directory where you want to store these configuration files and then do git init to initialize it as GIT repository. Connect it with your remote repository created in GitHub.

In the directory, create a file customer-service-dev.yml and add the previous configuration of the customer-service (which should be commented right now, if you are following this tutorial).

server:
  port: 8081
 
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/customer
    username: DB_USERNAME
    password: DB_PASSWORD
  application:
    name: customer-service
  jpa:
    properties:
      hibernate:
        sqldialect: org.hibernate.dialect.MySQLDialect
        showsql: true

commit the changes to the repository.

4. Verifying the configuration

Start the config-server, eureka-discovery-service and customer-service applications.

The Config Service serves property sources from /{application}/{profile}/{label}. Since we don't have any label so configuration should be accessible from http://localhost:8084/customer-service/dev. If everything is set right then you should be able to see configuration you saved in customer-service-dev.yml by using this URL.

Externalized Configuration With Spring Cloud Config

You can also check the logs to verify that your application is fetching configuration from config-server.

[2m2023-10-06T11:03:57.520+05:30[0;39m [32m INFO[0;39m [35m26224[0;39m [2m---[0;39m [2m[  restartedMain][0;39m [36mc.n.c.CustomerServiceApplication        [0;39m [2m:[0;39m The following 1 profile is active: "dev"
[2m2023-10-06T11:03:57.570+05:30[0;39m [32m INFO[0;39m [35m26224[0;39m [2m---[0;39m [2m[  restartedMain][0;39m [36mo.s.c.c.c.ConfigServerConfigDataLoader  [0;39m [2m:[0;39m Fetching config from server at : http://localhost:8084
[2m2023-10-06T11:03:57.570+05:30[0;39m [32m INFO[0;39m [35m26224[0;39m [2m---[0;39m [2m[  restartedMain][0;39m [36mo.s.c.c.c.ConfigServerConfigDataLoader  [0;39m [2m:[0;39m Located environment: name=customer-service, profiles=[default], label=null, version=153081cc68dff557826cbdc3498db09bf9aee448, state=null
[2m2023-10-06T11:03:57.570+05:30[0;39m [32m INFO[0;39m [35m26224[0;39m [2m---[0;39m [2m[  restartedMain][0;39m [36mo.s.c.c.c.ConfigServerConfigDataLoader  [0;39m [2m:[0;39m Fetching config from server at : http://localhost:8084
[2m2023-10-06T11:03:57.570+05:30[0;39m [32m INFO[0;39m [35m26224[0;39m [2m---[0;39m [2m[  restartedMain][0;39m [36mo.s.c.c.c.ConfigServerConfigDataLoader  [0;39m [2m:[0;39m Located environment: name=customer-service, profiles=[dev], label=null, version=153081cc68dff557826cbdc3498db09bf9aee448, state=null
[2m2023-10-06T11:03:57.570+05:30[0;39m [32m INFO[0;39m [35m26224[0;39m [2m---[0;39m [2m[  restartedMain][0;39m [36mo.s.c.c.c.ConfigServerConfigDataLoader  [0;39m [2m:[0;39m Fetching config from server at : http://localhost:8084

Spring Boot Config Data resolves configuration in a two step process. First it loads all configuration using the default profile. This allows Spring Boot to gather all configuration which may activate any additional profiles. After it has gathered all activated profiles it will load any additional configuration for the active profiles. Due to this you may see multiple requests being made to the Spring Cloud Config Server to fetch configuration.

Changes in account-service configuration to use config server

1. Add dependency

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-config</artifactId>
</dependency>

2. Changes in application.yml

server:
  port: 8082

spring:
  application:
    name: account-service
  profiles:
    active: dev 
  config:
    import: optional:configserver:http://localhost:8084 

If you use optional: prefix, config client won't fail if it is unable to connect to Config Server.

3. Adding configuration to repository

Create account-service-dev.yml file in the local directory and add the previous configuration of account-service there.

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/account
    username: DB_USERNAME
    password: DB_PASSWORD
  jpa:
    properties:
      hibernate:
        sqldialect: org.hibernate.dialect.MySQLDialect
        showsql: true

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka 

commit the changes to the repository.

4. Verifying the configuration

Start account-service and try to access http://localhost:8084/account-service/dev.

Once all the service are started you can access http://localhost:8081/customer/1 if everything is fine then you should get customer information along with the accounts.

That's all for this topic Spring Boot Microservice - Externalized Configuration With Spring Cloud Config. If you have any doubt or any suggestions to make please drop a comment. Thanks!

>>>Return to Spring Tutorial Page


Related Topics

  1. Spring Boot Microservices Example
  2. Spring Boot Microservice Example Using FeignClient
  3. Spring Boot Microservice - Load-Balancing With Spring Cloud LoadBalancer
  4. Spring Boot Microservice - Eureka + LoadBalancer + Feign
  5. Spring Boot + Spring Security JWT Authentication Example

You may also like-

  1. Spring MVC File Download Example
  2. Spring MVC Dot (.) Truncation Problem With @PathVariable Annotation
  3. Spring Java Configuration Example Using @Configuration
  4. How to Append to a File in Java
  5. Passing Query Parameters in Angular Routing
  6. Angular Disable Button Example
  7. Java String Interview Questions And Answers
  8. ArrayList in Java With Examples

No comments:

Post a Comment