A Journey with Microservices – Part 4

In the previous article, we implemented our service gateway and successfully test it by accessing the Mitrais homepage via http://localhost:8081/hello. Now we will implement our registry service to make service registration autonomous.

What is the problem? Why do we use it?

Imagine when we try to call a service that has a REST API. Our code needs to make requests to the known network location (IP address and port) of the service. We can have our code read the network location from the configuration file, but what if the number and network locations of services keeps changing?

In a microservice architecture, the network location of services will constantly be changing. The change will be more frequent because of autoscaling, failures, and upgrades. Consequently, we need to use a service discovery mechanism to make our lives easier. 

Introducing Netflix Eureka Service Discovery  

The main function of this service is to provide registration of running services and auto-discover services needed by other services. For example, when the Product Service and Order Service are starting up, they will register themselves to this service. When the Order Service needs to contact the Product Service, the Registry Service will provide the IP address or hostname of Product Service. 

Service discovery is like a database of all service network locations in microservice architecture. This service will constantly update all services in the microservice ecosystem and cache the information. Netflix Eureka is one of the available service registries. It provides a REST API registering and querying service. 

A new service instance will register its network location using a post request, and every 30 seconds it will automatically refresh its registration status using a PUT request. The service may be removed from the service registration list by using the HTTP DELETE request.  

Creating Registry Service 

We will use the project layout described the earlier article A Journey with microservice (part 2), because it already provides us with multi-modules and minimises repetition. We create a new module called registry-service inside non-functional module, and add this in our pom

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-config</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.retry</groupId>
        <artifactId>spring-retry</artifactId>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
        <plugin>
            <groupId>com.spotify</groupId>
            <artifactId>dockerfile-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

Don’t forget to create a bootstrap.yml inside the resources folder so that service will load its configuration from the configuration-service. 

Bootsrap.yml 

spring:
  application:
    name: registry
  cloud:
    config:
      URI:
        - http://localhost:9090
      fail-fast: true
      retry:
        initialInterval: 5000
        maxInterval: 5000
        maxAttempts: 10

Next, create a configuration file for the registry-service in the configuration-service resources->config folder using this configuration. 

# EUREKA DISCOVERY
eureka:
  client:
    healthcheck:
      enabled: true
    registerWithEureka: false # no need to register
    fetchRegistry: false # no need to discover other services
    serviceUrl:
      defaultZone: http://localhost:9091/eureka
  instance:
    instanceId: ${spring.application.name}:${spring.application.instance_id:${random.value}}
    leaseRenewalIntervalInSeconds: 5   # DO NOT DO THIS IN PRODUCTION
# TOMCAT
server:
  port: 9091
# ACTUATOR ENDPOINTS
management:
  endpoints:
    web:
      exposure:
        include: health, info, env

Let’s move on to creating a spring boot application. We use Spring Cloud’s @EnableEurekaServer to make our Spring Boot application act as a Eureka Server. 

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

/**
 *
 */
@SpringBootApplication
@EnableEurekaServer
public class ServiceRegistryMain {
   public static void main(String[] args) {
      SpringApplication.run(ServiceRegistryMain.class, args);
   }
}

Testing 

To test the service, we need to create a dummy service and register it to register-service. Let’s create this dummy-service inside the functional module, and add this to the pom file 

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-config</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.retry</groupId>
        <artifactId>spring-retry</artifactId>
    </dependency>
</dependencies>
<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
        <plugin>
            <groupId>com.mysema.maven</groupId>
            <artifactId>apt-maven-plugin</artifactId>
        </plugin>
        <plugin>
            <groupId>com.spotify</groupId>
            <artifactId>dockerfile-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

Don’t forget to create a bootstrap.yml inside the resources folder so that service will load its configuration from the configuration-service. 

Bootsrap.yml 

spring:
  application:
    name: dummy
  cloud:
    config:
      uri:
        - http://localhost:9090
      fail-fast: true
      retry:
        initialInterval: 5000
        maxInterval: 5000
        maxAttempts: 10

Next, create a configuration file for the dummy-service in the configuration-service resources->config folder and using this configuration. 

logging:
  level:
    org.springframework.data.rest: DEBUG

# EUREKA DISCOVERY
eureka:
  client:
    healthcheck:
      enabled: true
    fetchRegistry: false
    serviceUrl:
      defaultZone: http://localhost:9091/eureka
  instance:
    instanceId: ${spring.application.name}:${spring.application.instance_id:${random.value}}
    leaseRenewalIntervalInSeconds: 5   # DO NOT DO THIS IN PRODUCTION
# TOMCAT
server:
  port: 8086
# ACTUATOR ENDPOINTS
management:
  endpoints:
    web:
      exposure:
        include:
          - health
          - info
          - env

Let’s move on to create a spring boot application. We use Spring Cloud’s @EnableEurekaServer to make our Spring Boot application act as a Eureka Server. 

package com.microservice.dummy;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DummyServiceApplication {

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

Then we create Dummy REST API to simulate the dummy service.  

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DummyController {

    @GetMapping("/test")
    public String test() {
        return "Test Dummy service";
    }

}

The last thing is we need to add routes inside the gateway.yml configuration file to map the dummy-service. 

spring:
  cloud:
    gateway:
      x-forwarded-for:
        enabled: true
      discovery:
        locator:
          lower-case-service-id: true
          enabled: true
      routes:
      - id: dummy
        URI: lb://DUMMY
        predicates:
        - Path=/dummy/**
        filters:
        - RewritePath=/dummy/(?<remains>.*), /$\{remains}
# EUREKA DISCOVERY
eureka:
  client:
    healthcheck:
      enabled: true
    registerWithEureka: false
    fetchRegistry: true
    serviceUrl:
      defaultZone: http://localhost:9091/eureka
  instance:
    instanceId: ${spring.application.name}:${spring.application.instance_id:${random.value}}
    leaseRenewalIntervalInSeconds: 5   # DO NOT DO THIS IN PRODUCTION
# TOMCAT
server:
  port: 8081

Now we can start our microservice sequentially, starting from the configuration-service, then to the registry-service, gateway-service, and dummy-service. To test if the registry service works properly, you can open the browser and access the following URL format http://localhost:9091. It should show you the Spring eureka system status.

Conclusion

Microservices is an architectural style that structures an application as a collection of services that are highly maintainable and testable, loosely-coupled, and independently deployable. Using this architecture, services can be updated independently – updating one service doesn’t require changing any other services. There are some core services that need to be created to make our microservices easier to maintain and deploy, and they have been discussed in our previous articles: Microservices part 1, part 2, and part 3.

The first is the Configuration Service that helps to externalise configurations in our microservice system. By using the Configuration Service, configurations for all microservices in all environments are stored in one place – a centralised configuration store. When a microservice needs its configuration, it will call Configuration Service which will cause the config server to look up the configuration and provide it to the microservice. This ensures that the configuration is secured and has role-based access.

The second service that needs to be implemented is the Gateway Service that acts as an entry point into our microservice system. The service encapsulates the system architecture and provides an API that is tailored to each client. The Gateway Service is responsible for routing, composition, and protocol translation. All requests from clients go to the Gateway that routes requests to the appropriate microservice.

The third is the Registry Service that helps provides registration of running services and auto-discovery services needed by others. Service discovery is a database of all service network locations in our microservice architecture. A new service instance will register its network location using a post request, and refresh it’s details automatically every 30 seconds using a PUT request. This service will always update all services in the microservice ecosystem and cache the information.

Author:
I Kadek Dendy Senapartha – Analyst Programmer Mitrais

Share

Get the latest news from us to your inbox

(Weekly newsletter)

Leave a comment



from Indonesia:
from Australia:
from New Zealand:
from Singapore:
from other countries:
© Copyright 1991 - 2020 Mitrais