Spring Cloud Gateway Example

author-image  By Dhiraj,   03 June, 2019 0K

In the last article, we looked into Spring Cloud Gateway and discussed its core concepts and capabilities as a non-blocking API Gateway. In this article, we will use those concepts to develop an end to end microservice based architecture application using spring cloud. In the process, we will use spring cloud gateway as a gateway provider, Netflix Eureka as a discovery server with circuit breaker pattern using Netflix Hystrix.

In this implementation, we will have 2 different spring boot based microservices as first-service and second-service. These services will register themselves to the discovery server. Spring cloud gateway will use Netflix client API to discover the service and route the request to load balanced downstream services.

Here is an example of spring cloud Netflix with Zuul as Gateway Provider.

Discovery Server Implementation

In a microservice architecture, service discovery is one of the key tenets. Service discovery automates the process of multiple instance creations on demand and provides high availability of our microservices.

Below is the pom.xml and application.yml file required for this integration. You can visit my previous article for the complete integration of Netflix Eureka with Spring Cloud

pom.xml

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

	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-test</artifactId>
		<scope>test</scope>
	</dependency>
</dependencies>

<dependencyManagement>
	<dependencies>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-dependencies</artifactId>
			<version>${spring-cloud.version}</version>
			<type>pom</type>
			<scope>import</scope>
		</dependency>
	</dependencies>
</dependencyManagement>
<repositories>
	<repository>
		<id>spring-milestones</id>
		<name>Spring Milestones</name>
		<url>https://repo.spring.io/milestone</url>
	</repository>
</repositories>
<build>
	<plugins>
		<plugin>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-maven-plugin</artifactId>
		</plugin>
	</plugins>
</build>
application.yml
spring:
  application:
    name: discovery-service

eureka:
  client:
    eureka-server-connect-timeout-seconds: 5
    enabled: true
    fetch-registry: false
    register-with-eureka: false

server:
  port: 8761

Spring Cloud Gateway Implementation

Project Setup

First, we will generate a sample spring boot project from https://start.spring.io and import into workspace. The selected dependencies are Gateway, Hystrix and Actuator.

spring-cloud-gateway-project-strct

We will also add spring-cloud-starter-netflix-eureka-client dependency in our pom.

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

Spring Cloud Route Configuration

Route is the basic building block of the gateway. It is defined by an ID, a destination URI, a collection of predicates and a collection of filters. A route is matched if aggregate predicate is true.

Spring Cloud Gateway provides many built-in Route Predicate Factories such as Path, Host, Date/Time, Method, Header etc. We can use these built-in route with conjuction with and() or or() to define our routes. Once a request reaches to the gateway, the first thing gateway does is to match the request with each of the available route based on the predicate defined and the request is routed to matched route.

You can follow my another article for the complete spring cloud gateway guide.

Below is our route configuration. We have 2 different routes defined for our 2 microservices - first-service and second-service.

BeanConfig.java
package com.devglan.gatewayservice;

import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class BeanConfig {

    @Bean
    public RouteLocator gatewayRoutes(RouteLocatorBuilder builder) {
        return builder.routes()
                .route(r -> r.path("/api/v1/first/**")
                        .filters(f -> f.rewritePath("/api/v1/first/(?.*)", "/${remains}")
                                .addRequestHeader("X-first-Header", "first-service-header")
                                .hystrix(c -> c.setName("hystrix")
                                        .setFallbackUri("forward:/fallback/first")))
                        .uri("lb://FIRST-SERVICE/")
                        .id("first-service"))

                .route(r -> r.path("/api/v1/second/**")
                        .filters(f -> f.rewritePath("/api/v1/second/(?.*)", "/${remains}")
                                .hystrix(c -> c.setName("hystrix")
                                        .setFallbackUri("forward:/fallback/second")))
                        .uri("lb://SECOND-SERVICE/")
                        .id("second-service"))
                .build();
    }

}

In the above configuration, the first route is matched for a path that has a predicate defined to match all the request URL with /api/v1/queue/**. After these multiple filters such as rewritePath, addRequestHeader etc is applied. These are in-built filters provided by the gateway out of the box.

rewritePath filter takes a path regexp parameter and a replacement parameter.

The AddRequestHeader GatewayFilter Factory takes a name and value parameter and adds the configured header param in the request.

The Hystrix GatewayFilter Factory requires a single name parameter, which is the name of the HystrixCommand. The request will be forwarded to the controller matched by the fallbackUri parameter.

Below is an equivalent application.yaml configuration.

spring
	cloud:
		gateway:
		  routes:
		  - id: first-service
			uri: lb://FIRST-SERVICE
			predicates:
			- Path=/api/v1/first/**
			filters:
			- RewritePath=/api/v1/first/(?.*), /$\{remains}
			- AddRequestHeader=X-first-Header, first-service-header
			- name: Hystrix
				args:
				  name: hystrix
				  fallbackUri: forward:/fallback/first

Spring Cloud Gateway Application Config

Below is our application.yaml file.

application.yaml
hystrix.command.fallbackcmd.execution.isolation.thread.timeoutInMilliseconds: 2000
spring:
  application:
    name: api-gateway

server:
  port: 8088

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka
    register-with-eureka: false
  instance:
    preferIpAddress: true

management:
  endpoints:
    web:
      exposure:
        include: hystrix.stream

Hystrix Fallback Command

Below is our controller implementation whose endpoints will be called on failure of our microservices.

HystrixController.java
package com.devglan.gatewayservice;

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

@RestController
@RequestMapping("/fallback")
public class HystrixController {

    @GetMapping("/first")
    public String firstServiceFallback(){
        return "This is a fallback for first service.";
    }

    @GetMapping("/second")
    public String secondServiceFallback(){
        return "Second Server overloaded! Please try after some time.";
    }
}

Now, let us define our spring boot main class.

GatewayServiceApplication.java
package com.devglan.gatewayservice;

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

@SpringBootApplication
public class GatewayServiceApplication {

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

}

Microservices Implementation

First Service

These services are very simple implementation with only one controller defined for demo purpose.

FirstController.java
package com.devglan.gatewayservice.controller;

import org.springframework.web.bind.annotation.*;

@RestController
public class FirstController {

    @GetMapping("/test")
    public String test(@RequestHeader("X-first-Header") String headerValue){
        return headerValue;
    }

}

application.yaml
spring:
  application:
    name: first-service
server:
  port: 8086

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka

FirstServiceApplication.java
package com.devglan.gatewayservice;

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

@SpringBootApplication
public class FirstServiceApplication {

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

}

Second Service Implementation

SecondController.java

@RestController
public class SecondController {

    @GetMapping("/second")
    public String test(@RequestHeader("X-second-Header") String headerValue){
        return headerValue;
    }

}

application.yaml
spring:
  application:
    name: second-service
server:
  port: 8087

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka

Conclusion

In this article, we discussed about spring cloud gateway and created a demo application using it. The source code can be downloaded from github here.

If You Appreciate This, You Can Consider:

  • Like us at: Facebook or follow us at Twitter
  • Share this article on social media or with your teammates.
  • We are thankful for your never ending support.

About The Author

author-image

Further Reading on Spring Cloud

1. Spring Cloud Tutorial

2. Refresh Property Config Runtime

3. Spring Cloud Netflix Eureka

4. Introduction To Microservices

5. Encrypt Decrypt Cloud Config Properties