Sitemap

Spring Cloud Gateway— An Introduction

In this article, we will try to understand the fundamentals of Spring Cloud Gateway which represents one of the most important patterns in the Microservices Architecture — API Gateway. We will create sample implementations based on Spring Boot & Spring Cloud Gateway. This is the fifth part of our Spring Boot Microservices series.

--

Press enter or click to view image in full size
Photo by Zetong Li on Unsplash

API Gateway

Spring Cloud Gateway provides a library to build an API Gateway. This is the preferred gateway implementation provided by Spring Cloud. It's built with Spring 5, Spring Boot 2, and Project Reactor.

To understand the offerings of Spring Cloud Gateway we must understand the API Gateway pattern in detail. Let's assume, we are implementing the microservices architecture for our e-commerce system. One of the microservices in the system is Product Catalog Service, which is responsible to manage product lifecycle through — create, update, delete, and get operations. Let’s go through some common scenarios, we might come across —

Scenario 1 — Multiple Versions of Service

Our Product Catalog Service is updated with the new changes, but the updated service is not compatible with the mobile client.

Though we cannot achieve the service update completely, we can optimize it by running multiple versions in parallel. Our mobile client will continue using version-V1 while other clients will move to the version-V2.

Scenario 2 — Restricted Access

Our Product Catalog Service provides APIs to both read and write, the catalog data.

Our security team suggests to limit the access to write APIs — add, update, and delete, from the vendor portal only — vendor.test-gateway-store.com.

The rest of the clients, like the e-commerce portal, can only access get product API from Product Catalog Service.

Scenario 3 — Monitoring API Performance

A lot of customers are complaining about delays while getting the product details. We must monitor the performance of the “get product details” API to assess the severity and plan out the next steps.

Scenario 4 — Updating Response Header

We received the performance statistics of Product Catalog Service. Get product details API is getting hit too many times. This is not needed as the product details are not updated every minute.

We can avoid the unnecessary hits by introducing the “Cache-Control” header attribute in the response of “get product details API”.

The above-discussed scenarios typically fall into the category of cross-cutting concerns and must be dealt with separately. This increases the maintainability and agility of the overall system. The API Gateway pattern provides a cleaner approach to resolve such issues.

How API Gateway Works?

The API gateway takes all the API calls, from the clients and routes them to appropriate microservice(s) with request routing, composition, and protocol translation.

Press enter or click to view image in full size
API Gateway — An Illustration

API Gateway was introduced to provide the coarse grained APIs (implementing API Composition) which can communicate to the fine-grained APIs, aggregate the responses and return back to the end user. As a single entry point was introduced with this pattern, it became the preferred choice to enable the cross-cutting concerns.

Exactly what the API gateway does will vary from one provider to another. Some common functions include routing, rate limiting, billing, monitoring, authentication, API composition, policies, alerts, and security.

Spring Cloud Gateway — Core Functions

Spring Cloud Gateway supports many cross-functional features we discussed above. To support the wide range of such features, the framework is based on three core components — Routes, Predicates, and Filters.

Routes

This is the primary component consisting of ID, Destination Uri, Predicates, and Filters.

Predicates

This is more like a condition to match the route. This is done based on the HTTP request, headers, parameters, path, cookie, and other request criteria.

Filters

They are the plugins to update the request or response. They function similarly to the servlet filters with the pre and post variations. You can use it for multiple purposes including “adding a request parameter”, “adding a response header”, “monitoring”, and many other utilities.

We will see the examples for each of these aspects through the sample implementations in the next section.

Sample Implementation

In this section, we will implement our API Gateway and build the solutions to the problem scenarios, we discussed earlier in this article.

Scenario 1 — Multiple versions of a service.

As discussed earlier, we will have two versions of our Product Catalog Service — V1 & V2. Let's create the Product Catalog Service based on Spring Boot as explained in our very first series — Developing First Service. Assuming you are already aware of creating a Spring Boot based service, we will work on updating the Product Catalog Service.

Let's introduce a new method — getVersion in this service, which does a simple job to display the service version.

@RestController
public class ProductCatalogService {
...
@GetMapping("/product/version")
public String getVersionInfo(){
return "Version - V1";
}
}

Copy the whole project to create a new version of our Product Catalog Service. Implement the getVersion method similar to the one above. The only difference is the version name — V2.

@RestController
public class ProductCatalogService {
...
@GetMapping("/product/version")
public String getVersionInfo(){
return "Version - V2";
}
}

Update the respective application properties to avoid port conflicts. We will be running Version 1 on port 8081 and Version 2 on port 8082.

server.port=8081 #8081 for V1 and #8082 for V2

Run both the service versions with the maven command mvn spring-boot:run , in the respective directories. You should be able to view the specific versions by visiting http://localhost:8081/product/version or http://localhost:8082/product/version URLs.

It's time to create our API Gateway server. Let's go to our favorite store to create the Spring Boot application — The Spring Initializer. Add the Spring Cloud Gateway dependency.

Press enter or click to view image in full size

Generate, download, and unpack the archive to your local system. Open the project with your favorite code editor. We will be creating src/main/resources/application.yml file for the gateway server configuration.

server:
port: 9090
spring:
cloud:
gateway:
routes:
- id: product_service_v1
uri: 'http://localhost:8081'
predicates:
- Path=/product/**
- Query=src,mobile
- id: product_service_v2
uri: 'http://localhost:8082'
predicates:
- Path=/product/**

We are defining two routes here — product_service_v2 and product_service_v1 . The first version can only be accessed by Mobile devices. We are using two predicates for product_service_v1 to achieve this —

  • Path (/product/**) — This will match the path pattern containing ‘/product’ with any suffix.
  • Query (src, mobile) — This will match the query parameter in the path if src parameter is available and has a value mobile

If both the conditions meet, the request will be forwarded to — http://localhost:8081 as specified against uri attribute. If only the path matches, the request will be forwarded to http://localhost:8082 .

Let's start our gateway server by running mvn spring-boot:run and visit the following URLs

  • http://localhost:9090/product/version?src=mobile — You will see the message — “Version V1”
  • http://localhost:9090/product/version —You will see the message — “Version V2”

Congratulations. You just completed the first sample routing for our API Gateway. We used predicates to route the requests to different versions of a service.

Scenario 2- Restricted Access

As discussed earlier, we need to restrict the access of our write operations including create, update, and delete APIs of Product Catalog Service. We will do this by updating our route definitions.

Spring cloud gateway supports route definition in two ways — configuration files and code.

But this time we will do it in a different way. Spring cloud gateway supports route definition in two ways — configuration files and code. We already used the first option, now we will use the second. Update your GatewayServerApplication.java , the auto-generated java file in our gateway server, with the highlighted code.

@SpringBootApplication
public class GatewayServerApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayServerApplication.class, args);
}
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("path_route", r -> r.path("/product").and().method("POST", "PUT", "DELETE").and().host("vendor.test-gateway-store.com")
.uri("http://localhost:8081"))
.route("path_route", r -> r.path("/product/**").and().method("GET")
.uri("http://localhost:8082"))
.build();
}
}

With the customRouteLocator method, we are again defining two routes. The first route definition says — if a request is received with the path “/product” and the method type is either POST, PUT or DELETE and the host is vendor.test-gateway-store.com then allow the request to be forwarded to http://localhost:8081

The second route takes care of all the get requests with the pattern /product/** , and forwards them to http://localhost:8082

If you run your post requests from your local machine, it will fail. If you want them to pass update the host value in the first route as “localhost*”. The get requests will pass for all the different sources.

Great! We solved another major issue with the minimal configuration. Two more to go!

Scenario 3 — Monitoring Apis

This is relatively simpler. To enable gateway metrics, add spring-boot-starter-actuator as a project dependency in our gateway server application. Gateway Metrics Filter is a global filter and does not need any route configuration. The global filters are special filters that are conditionally applied to all the routes. Let's update the pom.xml to include the actuator dependency —

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

Enable the specific actuator views, in src/main/resources/application.properties . This will enable us to view the metrics through the browser.

management.endpoints.web.exposure.include=gateway,metrics

Start the gateway server and access the metrics at http://localhost:9090/actuator/metrics/gateway.requests . You can view a response similar to the one below.

{
"name": "gateway.requests",
"description": null,
"baseUnit": "seconds",
"measurements": [
{
"statistic": "COUNT",
"value": 4
},
{
"statistic": "TOTAL_TIME",
"value": 1.754685765
},
{
"statistic": "MAX",
"value": 0.086543555
}
],
"availableTags": [
{
"tag": "routeUri",
"values": [
"http://localhost:8082",
"http://localhost:8081"
]
},...
]
}

The above response provides valuable insights including the count of requests, total time taken, and maximum time taken for the API response. The above figures reflect the numbers aggregated across all the requests. You can check the specific figures based on routeId, routeUri, outcome, status, httpStatusCode, and httpMethod, which are available as filter tags. So if you want to monitor the API on http://localhost:8082 , we can access it through

http://localhost:9090/actuator/metrics/gateway.requests?tag=routeUri:http://localhost:8082

The above metrics can be easily integrated with Prometheus to create a Grafana dashboard.

Scenario 4 — Updating Response Header

So our monitoring results indicate that the get product details API throughput is on the compromising side. We will use the basic caching mechanism to improve its performance. We will take the help of cache-control response- header to limit the hits on the server-side. Product details or lists are not updated so frequently.

For our test purpose, we will add max-age attribute to it with the value of 5 mins. This means the client of this API (get product details) will keep the cache of API results for 5 mins. If the user tries to get the product details within this period, the cached result will be returned.

Let's update our GatewayServerApplication.java

@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder)
{
return builder.routes()
.route("path_route", r -> r.path("/product").and().method("POST", "PUT", "DELETE").and().host("localhost*")
.uri("http://localhost:8081"))
.route("path_route", r -> r.path("/product/**").and().method("GET")
.filters(f -> f.addResponseHeader("Cache-Control", "max-age=300"))
.uri("http://localhost:8082"))
.build();
}

We added the required response header with the help of a pre-defined filter — Add Response Header filter.

Restart the gateway server and try to get the product list. Inspect its response headers and you will find the cache-control header we introduced just now. If you try to add a product now and refresh, all in 5 mins, your list will not be updated on the browser side.

Bravo! we finished all the scenarios in less than 10 mins.

Learning never stops

We are able to implement our basic API Gateway server based on Spring Cloud Gateway. The complete source code of the examples is available at Github. We tried to get a practical insight into the API Gateway pattern but we just scratched the surface, in terms of features.

We explored a few predicates like path, method, and host. But the Spring Cloud Gateway provides many more built-in route predicate factories including After/Before/Between DateTime, Cookie, Header, Host, Method, Path, Query, Remote Address, and Weight.

We explored the Add Response filter to implement the cache strategy. Similar to the built-in predicates, there are many other built-in filters too, including Add Request, Add Response, Circuit Breaker, Hystrix, Fallback, Map-Request, Prefix Path, Preserve Host, Request Rate Limiter, etc. We can add a custom filter too if the already available set does not suffice the needs.

The library provides options to cater to advanced routing needs with Cors Configuration. We already discussed monitoring API performance but the actuator API provides a rich set of monitoring options. Do check out the Spring Cloud Gateway documentation to further upgrade your skills with this technology.

--

--