Skip to main content

Spring Cloud Gateway

Introduction

Spring Cloud Gateway is a powerful, modern API gateway built on Spring Framework 5, Spring Boot 2, and Project Reactor. It's designed to provide a simple yet effective way to route requests to your microservices, apply cross-cutting concerns, and secure your applications in a microservices architecture.

In a microservices environment, an API gateway serves as the single entry point for clients, handling requests by routing them to appropriate services, applying transformations, and implementing cross-cutting concerns like authentication, monitoring, and rate limiting. Spring Cloud Gateway excels in this role with its non-blocking, reactive design.

Why Use Spring Cloud Gateway?

  • Reactive Design: Built on Spring WebFlux, it offers non-blocking I/O for handling high concurrency with fewer resources
  • Dynamic Routing: Route traffic to different services based on various predicates
  • Circuit Breaking: Integration with resilience tools like Resilience4j
  • Filtering Capabilities: Pre and post-filtering for cross-cutting concerns
  • Spring Ecosystem Integration: Seamless integration with Spring Cloud Discovery, Config, and Security

Getting Started

Setting Up Your Project

Let's start by creating a basic Spring Cloud Gateway application:

  1. Create a new Spring Boot project with Spring Cloud Gateway dependency.

You can use Spring Initializer (https://start.spring.io/) with the following dependencies:

  • Spring Cloud Gateway
  • Spring Boot DevTools (optional)

Alternatively, add the following to your pom.xml:

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

Make sure you have the Spring Cloud dependencies management:

xml
<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>

<properties>
<spring-cloud.version>2023.0.0</spring-cloud.version>
</properties>

Basic Configuration

Create a simple application that routes requests to example.org:

java
@SpringBootApplication
public class GatewayApplication {

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

@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("example_route", r -> r
.path("/example/**")
.uri("https://example.org"))
.build();
}
}

This configuration creates a route that forwards any request coming to /example/** to https://example.org.

Alternatively, you can configure routes in the application.yml file:

yaml
spring:
cloud:
gateway:
routes:
- id: example_route
uri: https://example.org
predicates:
- Path=/example/**

Core Concepts

Route

A route is the primary API element in Spring Cloud Gateway. It consists of:

  • ID: Unique identifier for the route
  • Destination URI: Where the request should be sent
  • Predicates: Conditions that must be met for the route to be matched
  • Filters: Operations to modify the request or response

Predicates

Predicates determine if a route matches a given request. Spring Cloud Gateway includes many built-in predicates:

PredicateDescriptionExample
PathMatches on request pathPath=/orders/**
MethodMatches HTTP methodMethod=GET,POST
HeaderMatches header valueHeader=X-Request-Id, \d+
HostMatches host headerHost=**.example.org
CookieMatches cookie valueCookie=sessionId, \d+
QueryMatches query parameterQuery=id, \d+
After/Before/BetweenTime-based matchingAfter=2023-01-01T00:00:00+01:00[Europe/Paris]

Example with multiple predicates:

yaml
spring:
cloud:
gateway:
routes:
- id: user_service_route
uri: lb://user-service
predicates:
- Path=/users/**
- Method=GET,POST
- Header=X-Api-Version, v1

This route will only match requests that:

  • Have a path starting with /users/
  • Use either GET or POST method
  • Include header X-Api-Version: v1

Filters

Filters modify the incoming request or outgoing response. They can be global (applied to all routes) or specific to a route.

Here are some common built-in gateway filters:

Request Modification Filters

yaml
spring:
cloud:
gateway:
routes:
- id: add_request_header_route
uri: https://example.org
predicates:
- Path=/headers
filters:
- AddRequestHeader=X-Request-Foo, Bar
- PrefixPath=/api
- StripPrefix=1

This configuration:

  1. Adds a header X-Request-Foo: Bar to the request
  2. Adds prefix /api to the path
  3. Removes the first path segment

Response Modification Filters

yaml
filters:
- AddResponseHeader=X-Response-Foo, Bar
- RemoveResponseHeader=Sensitive-Header
- SetStatus=201

Implementing Custom Filters

You can create custom filters by implementing either GatewayFilter or GlobalFilter:

java
@Component
public class CustomGlobalFilter implements GlobalFilter {

private static final Logger log = LoggerFactory.getLogger(CustomGlobalFilter.class);

@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
log.info("Processing request: {}", exchange.getRequest().getPath());

// Add custom header
ServerHttpRequest request = exchange.getRequest().mutate()
.header("X-Custom-Header", "custom-value")
.build();

return chain.filter(exchange.mutate().request(request).build())
.then(Mono.fromRunnable(() -> {
log.info("Response status: {}", exchange.getResponse().getStatusCode());
}));
}
}

Practical Examples

Load Balancing with Service Discovery

Spring Cloud Gateway integrates seamlessly with service discovery tools like Eureka:

xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
yaml
spring:
application:
name: api-gateway
cloud:
gateway:
discovery:
locator:
enabled: true # Enable service discovery
lower-case-service-id: true
routes:
- id: user-service
uri: lb://USER-SERVICE # 'lb://' prefix for load balancing
predicates:
- Path=/users/**

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

With this configuration, requests to /users/** will be load-balanced across all instances of USER-SERVICE registered in Eureka.

Rate Limiting

You can implement rate limiting using the RequestRateLimiter filter with Redis:

xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
yaml
spring:
cloud:
gateway:
routes:
- id: rate-limited-route
uri: https://example.org
predicates:
- Path=/api/**
filters:
- name: RequestRateLimiter
args:
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20
key-resolver: "#{@userKeyResolver}"

Define the key resolver in your configuration:

java
@Bean
public KeyResolver userKeyResolver() {
return exchange -> Mono.just(exchange.getRequest()
.getRemoteAddress()
.getHostString());
}

This limits requests based on the client's IP address, allowing 10 requests per second with a burst capacity of 20.

Circuit Breaking with Resilience4j

Add circuit breaking to protect your services from cascading failures:

xml
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-circuitbreaker-reactor-resilience4j</artifactId>
</dependency>
yaml
spring:
cloud:
gateway:
routes:
- id: circuit-breaker-route
uri: lb://order-service
predicates:
- Path=/orders/**
filters:
- name: CircuitBreaker
args:
name: orderCircuitBreaker
fallbackUri: forward:/fallback/orders

Implement a fallback controller:

java
@RestController
@RequestMapping("/fallback")
public class FallbackController {

@GetMapping("/orders")
public Mono<Map<String, String>> orderFallback() {
Map<String, String> response = new HashMap<>();
response.put("message", "Order service is currently unavailable. Please try again later.");
response.put("timestamp", LocalDateTime.now().toString());
return Mono.just(response);
}
}

Authentication and Authorization

Integrate with Spring Security OAuth2:

xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-oauth2-client</artifactId>
</dependency>
java
@Configuration
public class SecurityConfig {

@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
http
.authorizeExchange()
.pathMatchers("/public/**").permitAll()
.pathMatchers("/api/admin/**").hasRole("ADMIN")
.anyExchange().authenticated()
.and()
.oauth2Login()
.and()
.oauth2ResourceServer()
.jwt();
return http.build();
}
}

Performance Considerations

  1. Use Non-blocking APIs: Spring Cloud Gateway is built on reactive principles, so use non-blocking calls whenever possible.

  2. Tune Thread Pool Size: If integrating with blocking APIs, configure the thread pool size appropriately:

yaml
spring:
cloud:
gateway:
httpclient:
pool:
max-connections: 200
acquire-timeout: 5000
max-idle-time: 15000
  1. Enable Response Caching: Implement caching for appropriate endpoints:
yaml
spring:
cloud:
gateway:
routes:
- id: cacheable_route
uri: https://example.org
predicates:
- Path=/cacheable/**
filters:
- name: Retry
args:
retries: 3
statuses: BAD_GATEWAY
- name: CachingRequestBodyGatewayFilter
args:
max-size: 10000

Monitoring and Observability

Spring Cloud Gateway provides metrics out of the box with Spring Boot Actuator:

xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
yaml
management:
endpoints:
web:
exposure:
include: gateway,health,info,metrics
endpoint:
gateway:
enabled: true
health:
show-details: always

This exposes:

  • /actuator/gateway/routes: Lists all configured routes
  • /actuator/gateway/routes/{id}: Information about a specific route
  • /actuator/gateway/globalfilters: Lists all global filters
  • /actuator/gateway/routefilters: Lists all route filters

Summary

Spring Cloud Gateway provides a modern, reactive API gateway solution for microservices architectures with:

  • Dynamic routing based on various predicates
  • Powerful filtering capabilities for cross-cutting concerns
  • Seamless integration with Spring Cloud ecosystem
  • High performance through its non-blocking design
  • Extensive monitoring capabilities

With its declarative configuration and flexibility, Spring Cloud Gateway enables developers to focus on business logic while providing robust edge functionality like security, resilience, and observability.

Additional Resources

Exercises

  1. Create a gateway that routes requests to two different backend services based on path.
  2. Implement rate limiting for a specific endpoint to allow only 5 requests per minute per client.
  3. Add a circuit breaker with a custom fallback response for a service route.
  4. Implement a custom filter that adds correlation IDs to all incoming requests.
  5. Configure the gateway to integrate with a service discovery system like Eureka.


If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)