Configuration Of A Microservice

This article is in continuation to the microservices services so if you hadn’t read the last article then I suggest you read that first (here).In the previous article, I provided detailed information on microservice and what challenges we face while working with Microservices. We will not be discussing what we discussed earlier in microservices.

What is the need?

Every application we use comes up by default with basic configuration, but to use the application with its best every application needs to be configured, updated or modified to get the best use of it. 

So consider we have a database that have configuration properties like username and password which are not a part of business logic so we keep them aside of business logic in a separate place(here file) which is externalized like we put them in application properties or YAML file.

Examples of Configuration

  • Database Connection
  • Credentials
  • Feature or Flags, etc.

Goals

  • Externalized
    It should be separate from main business logic, can be placed inside resources folder if working in Java projects.
     
  • Environment Specific
    It should be configurable in an environment specific manner, for development we have production database, for testing we have QA database, etc.
     
  • Consistency
    Suppose we have multiple different instances of a spring boot service, how other instances use one value and other using different values for the same configuration. We should have the configuration values to be the same all around the application.
     
  • Version History
    This is the advantage of using configuration in any spring boot application i.e we will get version history, so we can check what is the value of the particular configuration at particular time or version.
     
  • Real Time Management
    Let us suppose we have multiple threads changing the application in real time, this can be checked with the configuration and find out which value is what of a particular thread. 

How to do configuration?

Method 1- Using properties files 

According to oracle docs, properties are configuration values managed as key/value pairs. In each pair key and pair both are String values. The key identifies and is used to retrieve the values.

Properties files are used to store configuration data or settings. 

Properties in the application life cycle

  1. Loads default properties from well-known default locations.
  2. Loads values from the last invocation from a well-known default location.
  3. Initializes itself based on the default and remembered properties.
  4. Set or modify various values based on user interactions.
  5. Saves values to the well known locations for the next time. 

Let us take an example. In this the value of the variables are read from the application.properties file and assigned at the time of bean creation. We mostly use this approach to inject values to the beans.

Injecting values with the help of @value annotation is mostly used in real time applications. We had used the default application.properties files of spring boot, where we can define variables that we can access afterwards.

Default application.properties file

brand=Hyundai
model=Venue
color=Denim Blue
weight=1085

Simple Java Program to use the values

@Value("${brand}")
private String brand;

@Value("${color}")
private String color;

@Value("${color}")
private String color;

@Value("${weight}")
private int weight;

Note: 

  • We can also use @Value annotation on the method level as it is processed by BeanPostProcessed class, it will be invoked when spring is building the spring context by instantiating files or beans.
  • We can assign default values to the variables that will be used as fallback values when the property we want to inject is missing or not properly injected.
@Value("${brand:Hyundai}")
private String brand;

Method 2- Using ConfigurationProperties Annotation

According to spring docs, this is an annotation for externalized configuration. We can add this annotation to the class definition or bean method in Configuration class. Binding is either by calling setters on annotated class or if @ConstructorBinding is in use by using constructor parameters. 

Normally we use @value to inject property one by one, this is best for a single simple application.

The equivalent in @ConfigurationProperties

car.brand = hyundai
car.model = venue
car.color = denim blue
car.weight = 1085

The equivalent class will be,

package com.ConfigurationProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties("car") // prefix app, find car.* values
public class AppProperties {
    public static class Menu {
        private String brand;
        private String model;
        private String color;
        private int weight;
        //constructor
        //getters and setters
        //toString
    }

Note: ConfigurationProperties supports both .properties and .yml files.

When to use @Value and @ConfigurationProperties?

When we have only one value per single message so it is better to use @value whereas when we have a bunch of values we can use @ConfigurationProperties.

YAML files

YAML stands for Yet Another Markup Language but it later changes to YAML Ain't Markup Language. So whether it is a language to store data elements.

Let us create a YAML file, equivalent to application.properties file.

car:
brand = hyundai
model = venue
color = denim blue
weight = 1085

Profiles

Spring has this feature called profiles in order to use your configurations in different environments. 

Suppose we have a JAR file and application properties file with it and we have 3 different environments dev, QA and prod.

So, JAR and properties files of dev goes to dev. Similarly for QA and Production. 

We can deploy JAR properties file to different environment technically, but it is not a good approach we have some drawbacks with i.e 

  • Outside source code control.
  • Manual management across management.

So we got one step closure in solving this problem, so we use spring profiles to make configuration different for different environments.

Whenever you run any application you will see a profile is already set as default if no profile is set manually in application.properties or application.yml file.

Profile has a set of configuration values, a preset of config values in a form of group and form a profile, whenever we are not using any profile so the configuration we made are placed with the default profile and by default default profile is active.

Custom Profiles

We can create a custom profile with a custom group of configuration properties and make a spring boot program use your custom profile.

Naming Convention is application-<profile name>.extension and add spring.profiles.active = <profile name> in application.properties or application.yml file.

We can use N number of profiles active at one time and all profiles are overridden by the last one.

Default profile is always active, we made pointing to another profile overrides values from default to different profiles.

Note: We can also select beans by profile.

Let us understand this by an example, we have a RepositoryBean that connects to Production Database and LocalDataSourceBean that connects to Local Database.

We can achieve it by,

Production Bean

@Profile("production")
@Repository
public class DBBean {
    //some connection
}

Local Bean

@Profile("dev")
@Repository
public class DBBean {
    //some connection
}

Using the Environment Object for Configuration

So till now with @value we are only injecting values to our code, but for environment specific changes we can do it with environment objects present in spring.

Environment is a bean that we can inject/Autowire in our application and then can access config values using active profile.

Let us understand with an example,

@Autowire
Private Environment env;
@GetMapping("envDetails")
public String envDetails() {
    return env.toString();
}

Now, with this env object we can look up for profiles and properties. But this is highly not recommended to check profiles or properties.

As an alternative we'll be using Spring Cloud Config Server.

Configuration as a Microservice

Suppose we have 4-5 microservices with some configuration that configuration should be the same for every microservice, so we need to create a microservice which will act as a configuration service and provide the same configuration to all other microservice. 

This way we can get consistency.

Options

  • Apache Zookeeper
  • ETCD- distributed key value store.
  • Hashicorp Consul
  • Spring Cloud Configuration Server.

Summary

So by this we come to the end of this series of Microservice. In this we discussed configuring a Microservice, why we need it, how we can achieve it and many more things in detail. By this we are now able to do all five steps as discussed in the last article (here).