• Skip to main content
  • Skip to primary sidebar
  • Home
  • Java
    • Spring
      • Spring Cloud
    • Design Patterns
    • Java Miscellaneous Tips

Coding Strain

All about code

Spring Cloud: How to Deal with Microservice Configuration (Part I)

January 22, 2023 by mario.casari@gmail.com Leave a Comment

Introduction

Configuring a software system in a monolithic approach does not pose particular problems. To make the configuration properties available to the system, we can store them in a file inside an application folder, in some place in the filesystem, or as OS environment variables. Microservice Configuration is a more complex subject. We have to deal with a number, which can be huge, of totally independent services, each with its own configuration. We could even face a scenario in which several instances of the same service need different configuration values.

In such a situation, a way to centralize and simplify configuration management would be of great importance. Spring Cloud has its own module to solve these problems, named Spring Cloud Config. This module provides an implementation of a server that exposes an API to retrieve the configuration information, usually stored in some remote repository like Git, and at the same time, it gives us the means to implement the client side meant to consume the services of that API.

In the first part of this article, we will discuss the basic features of this Spring Cloud module, storing the configuration in the configuration server classpath, and in the next part, we will show how to use other, more effective, repository options, like Git, and also how to refresh the configuration without restarting the services. Then, in later posts, we will show how the centralized configuration can be coupled with service discovery features to set a solid base for the whole microservice system.

Microservice Configuration: Spring Cloud Config Server Side

The first component we need in a distributed configuration scenario is a server meant to provide the configuration information for the services. To implement such a server component by Spring Cloud Config we have to use the right Spring Boot “starter” dependency, like in the following configuration fragment:

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

Then we have to annotate the Spring Boot main class with the @EnableConfigServer annotation:

@SpringBootApplication
@EnableConfigServer
public class AppMain {
	public static void main(String[] args) {
		new SpringApplicationBuilder(Config.class).run(args);
	}
}

The Spring Cloud Config server, according to the auto-configuration features of Spring Boot, would run on the default 8080 port as all Spring Boot applications. If we want to customize it, we can do it by the application.properties or application.yaml file:

server:  
  port: ${PORT:8888}
    
spring:
  application:
    name: config-server

If we run the application with the above configuration it will use the 8888 port as default. We can override the default by launching the application with a different port, by the PORT placeholder:

java -jar -DPORT=8889 sample-server-1.0-SNAPSHOT.jar 

In any case, If we launch the application with the spring.config.name=configserver argument instead, the default port will be 8888. This is due to a configserver.yml default file embedded in the spring-cloud-config-server library. As a matter of fact, it would be very convenient to launch the server config application on the 8888 port, either by explicitly configuring it by the server.port parameter, like in the example above, or passing spring.config.name=configserver in the startup Java command, because 8888 happens to be the default port used by the client side.

Important note: the spring.config.name=configserver option only works if passed in the startup command and seems to be ignored for some reason if set in the configuration file. We can see below an example of how to start the config server with a Java command:

java -jar -Dspring.config.name=configserver spring-cloud-config-native-server-1.0-SNAPSHOT.jar

By default, the Spring Cloud Config server uses Git as a remote repository to store the configuration data. In this article, to simplify the discussion we will focus on a more basic approach based on files stored on the application classpath. We will describe this option in the next section. It must be stressed that in a real scenario, this would be far from ideal and Git would be surely a better choice.

Enforcing Basic Authentication on the Server Side

We can provide the server with a basic security layer, in the form of an authentication mechanism based on user and password. To do that we must first add the following security starter to the POM:

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-security</artifactId>
		</dependency>

And then add the following piece of configuration in the application.yml file:

  security:
    user:
      name: myusername
      password: mypassword

With the above, the client side should be configured accordingly to be able to connect to the server, as we will see in the section related to the client side. We will discuss more advanced securing mechanisms in later articles.

Spring Cloud Config Backend Storing Options

The Spring Cloud Config server can store the configuration data in several ways:

  • By a remote Git system. This is the default.
  • Other Version Control Systems (VCS) like SVN.
  • VAULT: it is a tool by HashiCorp specialized in storing passwords, certificates, or other entities as secrets.
  • By storing it in some place in the filesystem or in the classpath.

In this article, we will describe the filesystem/classpath option. Spring Cloud Config has a profile named native that covers this scenario. In order to run the config server with a filesystem/classpath backend storage we have to start it with the spring.profiles.active=native option.

In the native scenario, the config server will search by default in the following places:

  • classpath:/
  • classpath:/config
  • file:./
  • file:./ config

So, we can simply store the configuration files inside the application jar file. If we want to use an external filesystem directory instead or customize the above classpath options, we can set the spring.cloud.config.server.native.searchLocations property accordingly.

Config Server API

The Config Server can expose the configuration properties of a specific application by an HTTP API with the following endpoints:

  • /{ application}/{ profile}[/{ label}]: this returns the configuration data as JSON, with the specific application, profile, and an optional label parameter.
  • /{ label}/{ application}-{ profile}.yml: this returns the configuration data in YAML format, with the specific application, profile, and an optional label parameter.
  • /{ label}/{ application}-{ profile}. properties: this returns the configuration data as raw text, with the specific application, profile, and an optional label parameter.

The application part represents the name of the application, configured by the spring.application.name property and the profile part represents the active profile. A profile is a feature to segregate a set of configurations related to specific environments, such as development, test, and production. The label part is optional and is used when using Git as a backend repository to identify a specific branch.

Microservice Configuration: Spring Cloud Config Client Side

If we want our services to obtain their own configuration from the server we must provide them with a dependency named spring-cloud-starter-config:

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

Clearly, the configuration must be obtained as the first step during its startup. To deal with this requirement, Spring Cloud introduces a bootstrap context. The bootstrap context can be seen as the parent of the application context. It serves the purpose of loading configuration retrieved data from some external source and making it available to the application context.

In earlier versions of Spring Cloud, we could provide the configuration properties for the bootstrap context by a bootstrap.yml file. This is deprecated in the new versions. Now, we have to simply provide a config.import property=optional:configserver: property in the standard application.yml:

  config:
    import: "optional:configserver:"

With the “optional:configserver:” value the config client service will use the default http://localhost:8888 address to contact the config server. If we exclude the optional part an error will be raised during startup, if the server is unreachable.

If we want to set a specific address and port, we can add the address part to the value, like this:

  config:
    import: "optional:configserver:http://myhost:myport"

Configuring Security on the Client Side

If we have secured the server with basic authentication we must provide the necessary configuration to the client. Adding the following piece of configuration to the application.yml will be enough:

  security:
    user:
      name: myusername
      password: mypassword

Putting the Pieces Together in a Simple Demo

Using the notions described above we can realize a simple demo with a configuration server and a single client service, as in the picture below:

A simple configuration with a Configuration Server in native mode and a single Client Service
Config Server and Client Service

Server Side Implementation

To implement the server side we create a Spring Boot application with the required Spring Cloud release train and Spring Cloud Config starter:

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>2021.0.5</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>

Then we write the application.yml file, setting the port to the conventional 8888 value, the active profile as native, and finally the application name:

server:  
  port: ${PORT:8888}
  
spring:
  profiles:
     active: native
  application:
    name: config-server

Since we have set the spring.profiles.active equal to the native value, this config server application storage will be based on filesystem/classpath. In our example, we choose to store the configuration file of the client service in the classpath, in a config subdirectory of the /resources folder. We name the client service application file name as client-service.yml and we fill it with the following content:

server:  
  port: ${PORT:8081}
myproperty: value  
myproperties:
   properties:
      - value1
      - value2

The myproperty and myproperties parts will be used to test this minimal demo: we will expose them by some REST service on the client and if all works as expected the above values will be returned.

Client Side Implementation

We configure the client application with the same release train of the server. As dependencies with have a spring-cloud-starter-config starter, and also a spring-boot-starter-web, because we want our application to expose some HTTP REST services:

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>2021.0.5</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>
	<dependencies>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-starter-config</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
	</dependencies>

The application.yml properties will be consumed by a specific component class by the @Value and @ConfigurationProperties annotations:

@Component
@ConfigurationProperties(prefix = "myproperties")
public class DemoClient {
	private List<String> properties = new ArrayList<String>();
	public List<String> getProperties() {
		return properties;
	}
	@Value("${myproperty}")
	private String myproperty;
	public String getMyproperty() {
		return myproperty;
	}
}

Then a controller class will implement two REST services, /getProperties and /getProperty, returning the above class properties:

@RestController
public class ConfigClientController {
	private static final Logger LOG = LoggerFactory.getLogger(ConfigClientController.class);
	
	@Autowired
	private DemoClient demoClient;
	
	@GetMapping("/getProperties")
	public List<String> getProperties() {
		LOG.info("Properties: " + demoClient.getProperties().toString());
		return demoClient.getProperties();
	}
	
	@GetMapping("/getProperty")
	public String getProperty() {
		LOG.info("Property: " + demoClient.getMyproperty().toString());
		return demoClient.getMyproperty();
	}
}

Compiling and running the Config Server and the Client Service

After compiling the two applications by Maven, we can take the resulting jars and run first the server part and then the client from the command line:

java -jar spring-cloud-config-native-server-1.0-SNAPSHOT.jar
...
java -jar spring-cloud-config-native-client-1.0-SNAPSHOT.jar

We can test the correct behavior by executing a call with the following address in the browser:

http://localhost:8081/getProperties

If all works as expected we will have the following values printed on the screen:

[
"value1",
"value2"
]

The Maven projects related to the demo described above are available on GitHub at the following addresses:

  • Client Service
  • Config Server

Conclusion

In this article, we have covered the basic notions required to configure a microservice system based on remote configuration. We have used here the native approach, using the classpath as a storage repository. In the next part we will show how to use a remote Git repository, and also how to refresh the configuration at runtime without restarting the services.

Filed Under: Spring Cloud

Reader Interactions

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Primary Sidebar

Recent Posts

  • Spring Cloud: How to Deal with Microservice Configuration (Part I)
  • Java Map: Preserve Insertion Order
  • Java Double: Parse String with Comma
  • Spring Boot for Cloud – Actuator
  • Spring Boot for Cloud – REST API Development

Recent Comments

    Copyright © 2023 · Magazine Pro on Genesis Framework

    We use cookies on our website to give you the most relevant experience by remembering your preferences and repeat visits. By clicking “Accept All”, you consent to the use of ALL the cookies. However, you may visit "Cookie Settings" to provide a controlled consent.
    Cookie SettingsAccept All
    Manage consent

    Privacy Overview

    This website uses cookies to improve your experience while you navigate through the website. Out of these, the cookies that are categorized as necessary are stored on your browser as they are essential for the working of basic functionalities of the website. We also use third-party cookies that help us analyze and understand how you use this website. These cookies will be stored in your browser only with your consent. You also have the option to opt-out of these cookies. But opting out of some of these cookies may affect your browsing experience.
    Necessary
    Always Enabled
    Necessary cookies are absolutely essential for the website to function properly. These cookies ensure basic functionalities and security features of the website, anonymously.
    CookieDurationDescription
    cookielawinfo-checkbox-analytics11 monthsThis cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Analytics".
    cookielawinfo-checkbox-functional11 monthsThe cookie is set by GDPR cookie consent to record the user consent for the cookies in the category "Functional".
    cookielawinfo-checkbox-necessary11 monthsThis cookie is set by GDPR Cookie Consent plugin. The cookies is used to store the user consent for the cookies in the category "Necessary".
    cookielawinfo-checkbox-others11 monthsThis cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Other.
    cookielawinfo-checkbox-performance11 monthsThis cookie is set by GDPR Cookie Consent plugin. The cookie is used to store the user consent for the cookies in the category "Performance".
    viewed_cookie_policy11 monthsThe cookie is set by the GDPR Cookie Consent plugin and is used to store whether or not user has consented to the use of cookies. It does not store any personal data.
    Functional
    Functional cookies help to perform certain functionalities like sharing the content of the website on social media platforms, collect feedbacks, and other third-party features.
    Performance
    Performance cookies are used to understand and analyze the key performance indexes of the website which helps in delivering a better user experience for the visitors.
    Analytics
    Analytical cookies are used to understand how visitors interact with the website. These cookies help provide information on metrics the number of visitors, bounce rate, traffic source, etc.
    Advertisement
    Advertisement cookies are used to provide visitors with relevant ads and marketing campaigns. These cookies track visitors across websites and collect information to provide customized ads.
    Others
    Other uncategorized cookies are those that are being analyzed and have not been classified into a category as yet.
    SAVE & ACCEPT