In previous article, we had implemented our basic project layout and configuration service. Now we will create two other non-functional services. Firstly, the Gateway. 

What is the problem? Why do we use it? 

Let’s go back to the map of Microservices. 

We can see that not all services are directly accessible by our front end apps. All requests from the front-end will be managed, balanced, and redirected to the target service. In fact, all our functional services can be access directly by the front-end, but issues can arise as the front-end requires access to more and more services. When the front-end needs to call a service directly, it may use protocols that are not web-friendly.  

Calling Microservices directly also have another potential drawback in that it makes difficult to refactor the Microservice. We may need to change how the service is partitioned, and this can affect the client that calls it if it communicates directly with the services. For this reason, it rarely makes sense for clients to talk directly to a Microservice. 

Introduction Gateway Service 

The Gateway service acts as an entry point into our Microservice system. This service encapsulates the system architecture and provides an API that is tailored to each client. We can also add other responsibilities to it, such as authentication, monitoring, load balancing, caching, request shaping and management, and static response handling. 

The Gateway service is responsible for routing, composition, and protocol translation. All requests from clients should go to the Gateway which will route requests to the appropriate Microservice. Sometimes the Gateway service handles a request by invoking multiple Microservices and aggregating the results. One example of using a Gateway is enabling the client to retrieve product details with single request (/productdetails?productid=xxx). The Gateway handles the client request by invoking various services (ex: product inifo, reviews, balance, recomendations) and combining the results. 

Creating Gateway Service 

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

<dependencies>
   <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-gateway</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>
   <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-actuator</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>

Dont forget to create the bootstrap.yml inside the resources folder so that the Gateway service will load its configuration from the configuration-service.

Bootsrap.yml

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

Next, create the configuration file for the gateway-service in configuration-service resources->config folder and use this configuration.

spring:
  cloud:
    gateway:
      x-forwarded-for:
        enabled: true
      discovery:
        locator:
          lower-case-service-id: true
          enabled: true
      routes:     
      - predicates:
          - Path=/hello/**
        filters:
          - StripPrefix=1
        uri: "https://www.mitrais.com/"
# 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
# ACTUATOR ENDPOINTS
management:
  endpoints:
    web:
      exposure:
        include:
          - health
          - info
          - env

As we can see in this configuration, we route the url path “/hello/” to open the mitrais home page. To test it, lets move on to creating a spring boot application.

We use Spring Cloud’s @EnableDiscoveryClient to activate the Netflix Eureka DiscoveryClient implementation in our Spring Boot application.

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

@SpringBootApplication
@EnableDiscoveryClient
public class GatewayMain {
   public static void main(String[] args) {
      SpringApplication.run(GatewayMain.class, args);
   }
}

Testing

To test the service, we run the Configuration and Gateway services in sequence. Opening  a browser and accessing the URL http://localhost:8081/hello will redirect you to the Mitrais homepage. If the URL http://localhost:8081/hello/about-us/ is used, it will redirect us to Mitrais’ About Us web page.

Thats all for our journey with Microservices for now. In the next part we will look at the Registry service and how it is used in our Microservice architecture.

Author:
I Kadek Dendy Senapartha – Analyst Programmer Mitrais