Developing Microservices Using Spring


This guide describes how to set up a Spring Boot microservice and integrate it in the microservice system landscape.

Content

Requirements

A basic understanding of building an application with Spring Boot and building Java projects with Maven is required.

Dependencies

The following dependencies are necessary to be able to integrate a Spring Boot microservice in the system landscape.

spring-boot-starter-actuator

(https://spring.io/guides/gs/actuator-service/)

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

With this dependency, various services and endpoints are available for managing and monitoring the application in a productive system. This includes the endpoint for the health check that the admin-service uses to get the current status of a service in the system.

jolokia-core

(https://jolokia.org)

<dependency>                            
    <groupId>org.jolokia</groupId>           
    <artifactId>jolokia-core</artifactId>
</dependency>

Jolokia is an HTTP/JSON bridge for remote JMX access. This dependency is needed to be able to access a services's JMX beans from the admin-service user interface.

spring-cloud-starter-config

(https://cloud.spring.io/spring-cloud-config/)

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

This dependency makes it possible for the service to get its configuration from the config server. The config server provides all relevant configurations for the microservices system globally. Currently, the config server is integrated in the service-manager (Argus). When started, a service receives so-called profiles, which tell the service which configurations it must get from the config-server. For example, if a service is started with the prod profile, it gets the application-prod.yml from the config server and gets the settings configured therein.

The goal here is to use the same configuration for all services, or for a group of services, so that it does not have to be configured for each service. To do so, you can use a central config-server that knows the configurations of the entire system (for all services) and provides these using REST. Thus, any service that knows where its config server is, can get its configuration there.

The following parameter tells a service where it can reach its config server.

bootstrap.yml
...
# tell your service where the config-server is running
spring.cloud.config.uri: http://127.0.0.1:7281/config
spring.cloud.config.username: root
spring.cloud.config.password: optimal
...

These declarations must be in a service's bootstrap.yml. When the service is started,the Servicewatcher (Argus) overwrites the declarations with the configured settings as environment variables in the servicewatcher-sw.yml.

servicewatcher-sw.yml
...
globalEnvironment:
  SPRING_CLOUD_CONFIG_URI: "http://127.0.0.1:${server.port}${spring.cloud.config.server.prefix}"
  SPRING_CLOUD_CONFIG_USERNAME: "${security.user.name}"
  SPRING_CLOUD_CONFIG_PASSWORD: "${security.user.password}"
...

spring-cloud-starter-eureka

(https://cloud.spring.io/spring-cloud-netflix/)

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

This dependency delivers the requirements a service must meet to be able to sign in to the Discovery Service. The following configuration class must be available in your service.

CloudConfiguration.java
package com.os.services.meintollerservice.config;
 
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
 
@Configuration
@Profile("cloud")
@EnableDiscoveryClient
public class CloudConfiguration
{
}
application.yml
# tell eureka where it gets status and health information 
eureka.instance.status-page-url-path: /manage/info
eureka.instance.health-check-url-path: /manage/health
# define the instanceId pattern for eureka
eureka.instance.metadata-map.instanceId: ${spring.application.name}:${spring.application.index:${server.port}}
 
# tell your service where the eureka instance is running
eureka.client.service-url.defaultZone: http://${APPLICATION_DOMAIN:${COMPUTERNAME:localhost}}:7261/eureka/

A service must sign in to the Discovery Service, so that it can be integrated into the system landscape and communicate with other services (and they with it), even when the landscape grows. It is also important how a service signs in to Discovery. For this, the following parameters must be configured in bootstrap.yml.

bootstrap.yml
server.port: 4711
 
spring.application.name: meintollerservice
spring.application.index: ${server.port}
...

The configuration parameter server.port specifies at which port the service should be started (see also Overview of Microservices). The port is also used as spring.application.index. The spring.application.name is the service name with which it signs in to Discovery. If multiple services have the same name, the Discovery Service treats them as a service group, even if the instances are running on different hosts and ports. However, if you plan to have the same service running with different configurations for different applications in a system landscape, then these should have different names. One example would be to use the gateway service once with NTLM authentication and once with basic authentication.


Configuration Files

The two most important configuration files are bootstrap.yml and application.yml. In a service, both should be in the directory src/main/resources.

The main difference between the two is that the properties of bootstrap.yml are loaded before those of application.yml. Some properties must be available earlier, and are thus configured in bootstrap.yml. An example is the spring.cloud.config properties, as well as server.port and spring.application.name.  For a service to be able to get its configuration from a config server, it must know where it can get this configuration before loading it. In other words, it must know where the config server is. In addition, the service must know its name, since the configuration files of format ${spring.application.name}-prod.yml are loaded in addition to the profile configurations. For more information, see the spring cloud website.

Examples

You can use the following configuration files as templates.

bootstrap.yml
server.port: 4711
   
spring.application.name: myawesomeservice
spring.application.index: ${server.port}
 
# tell your service where the config-server is running
spring.cloud.config.uri: http://localhost:7281
spring.cloud.config.username: root
spring.cloud.config.password: optimal
 
management.context-path: /manage
management.security.enabled: false
 
logging.exception-conversion-word: '%nopex'
 
info:
  version: '@project.version@ [@fix@]'
  app.name: '${spring.application.name}'
  app.description: '@project.description@'
  app.port: ${server.port}
  app.java-version: '${java.version}'
  build.artifact: '@project.artifactId@'
  build.name: '@project.name@'
  build.version: '@project.version@'
  build.fix: '@fix@'
  build.date: '@buildNumber@'
  build.java-version: '@java.version@'
  build.spring-boot: '@spring-boot.version@'
application.yml
# tell eureka where it gets status and health information 
eureka.instance.status-page-url-path: /manage/info
eureka.instance.health-check-url-path: /manage/health
# define the instanceId pattern for eureka
eureka.instance.metadata-map.instanceId: ${spring.application.name}:${spring.application.index:${server.port}}
 
# tell your service where the eureka instance is running
eureka.client.service-url.defaultZone: http://${APPLICATION_DOMAIN:${COMPUTERNAME:localhost}}:7261/eureka/


Profiles

Which profiles are used to start a service is defined in the template.yml file of the services. The entries in template.yml are added to the servicewatcher configuration by the SAM tool, and imported after the service manager is (re)started.

The services profiles determine which programmatic configurations get loaded and which configuration files are gotten from the config server (part of the servicewatcher). If, for example, a service with the profile cloud is started, it gets the configuration from the application-cloud.yml file of the config server. In addition, configuration classes with the annotation @Profile("cloud") are loaded. For more information, see "spring-cloud-starter-eureka").