Spring Cloud Gateway new generation API gateway service

Spring Cloud Gateway provides API gateway support for SpringBoot applications and has powerful intelligent routing and filter functions. This article will introduce its usage in detail.

Introduction to Gateway

Gateway is an API gateway service built on the Spring ecosystem and is based on technologies such as Spring 5, Spring Boot 2 and Project Reactor. Gateway aims to provide a simple and effective way to route APIs and provide some powerful filter functions, such as fusing, current limiting, Retry, etc.

Spring Cloud Gateway has the following features:

  • Based on Spring Framework 5, Project Reactor and Spring Boot 2.0;
  • Dynamic routing: it can match any request attribute;
  • You can specify Predicate and Filter for the route;
  • Integrated circuit breaker function of Hystrix;
  • Integrate Spring Cloud service discovery function;
  • Easy to write Predicate and Filter;
  • Request current limiting function;
  • Path rewriting is supported.

Related concepts

  • Route: route is the basic module for building gateway. It consists of ID, target URI, a series of assertions and filters. If the assertion is true, the route will be matched;
  • Predicate: refers to the Function Predicate of Java 8. The input type is ServerWebExchange in the Spring framework. This allows developers to match everything in the HTTP request, such as the request header or request parameters. Route if the request matches the assertion;
  • Filter: refers to the instance of GatewayFilter in the Spring framework. Using the filter, you can modify the request before and after it is routed.

Create project module

Here we create an API Gateway module to demonstrate the common functions of Gateway.

Introduce dependency

In POM Add related dependencies to XML:

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

Related configuration

Gateway provides two different ways to configure routing, one is through yml file, the other is through Java Bean. Let's introduce them respectively.

Method 1: use yml configuration

In application Configure in YML:

server:
  port: 9201
service-url:
  user-service: http://localhost:8201
spring:
  cloud:
    gateway:
      routes:
        - id: path_route #ID of the route
          uri: ${service-url.user-service}/user/{id} #Route address after matching
          predicates: # Assert that the path matches the route
            - Path=/user/{id}

Start Eureka server, user service and API gateway services, and call the address to test: http://localhost:9201/user/1 , we found that the request was routed to the path of user service: http://localhost:8201/user/1

Method 2: use Java Bean configuration

Add relevant configuration classes and configure a RouteLocator object:

@Configuration
public class GatewayConfig {

    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes()
                .route("path_route2", r -> r.path("/user/getByUsername")
                        .uri("http://localhost:8201/user/getByUsername"))
                .build();
    }
}

Restart the API gateway service and call the address test: http://localhost:9201/user/getByUsername?username=antonio , we found that the request was routed to the path of user service: http://localhost:8201/user/getByUsername?username=antonio

Use of Route Predicate

Spring gateway is used as part of the spring gateway routing infrastructure. Spring Cloud Gateway includes many built-in route predict factories. All these predictions match the different attributes of the HTTP request. Multiple route predictors can be combined. Let's introduce some common route predictors.

Note: the configurations mentioned in predict are all in application predict YML file and start the API gateway service with this configuration.

After Route Predicate

Requests after the specified time will match the route.

spring:
  cloud:
    gateway:
      routes:
        - id: after_route
          uri: ${service-url.user-service}
          predicates:
            - After=2019-09-24T16:30:00+08:00[Asia/Shanghai]

Before Route Predicate

Requests before the specified time will match the route.

spring:
  cloud:
    gateway:
      routes:
        - id: before_route
          uri: ${service-url.user-service}
          predicates:
            - Before=2019-09-24T16:30:00+08:00[Asia/Shanghai]

Between Route Predicate

Requests within the specified time interval will match the route.

spring:
  cloud:
    gateway:
      routes:
        - id: before_route
          uri: ${service-url.user-service}
          predicates:
            - Between=2019-09-24T16:30:00+08:00[Asia/Shanghai], 2019-09-25T16:30:00+08:00[Asia/Shanghai]

Cookie Route Predicate

Requests with the specified Cookie match the route.

spring:
  cloud:
    gateway:
      routes:
        - id: cookie_route
          uri: ${service-url.user-service}
          predicates:
            - Cookie=username,antonio

Using curl tool to send a request with Cookie username=antonio can match the route.

$ curl http://localhost:9201/user/1 --cookie "username=antonio"

Header Route Predicate

The request with the specified request header matches the route.

spring:
  cloud:
    gateway:
      routes:
      - id: header_route
        uri: ${service-url.user-service}
        predicates:
        - Header=X-Request-Id, \d+

Using curl tool to send a request with request header X-Request-Id:123 can match the route.

$ curl http://localhost:9201/user/1 -H "X-Request-Id:123" 

Host Route Predicate

Requests with the specified Host will match the route.

spring:
  cloud:
    gateway:
      routes:
        - id: host_route
          uri: ${service-url.user-service}
          predicates:
            - Host=**.antoniopeng.com

Use curl tool to send a request with a header of host: www.antonionopeng Com requests can match the route.

$ curl http://localhost:9201/user/1 -H "Host:www.antoniopeng.com" 

Method Route Predicate

The request to send the specified method will match the route.

spring:
  cloud:
    gateway:
      routes:
      - id: method_route
        uri: ${service-url.user-service}
        predicates:
        - Method=GET

The route can be matched by sending a GET request using the curl tool.

$ curl http://localhost:9201/user/1

Sending a POST request using curl tool cannot match the route.

$ curl -X POST http://localhost:9201/user/1

Path Route Predicate

The request to send the specified path will match the route.

spring:
  cloud:
    gateway:
      routes:
        - id: path_route
          uri: ${service-url.user-service}/user/{id}
          predicates:
            - Path=/user/{id}

Using curl tool to send / user/1 path request can match this route.

$ curl http://localhost:9201/user/1

The / abc/1 path request sent by curl tool cannot match the route.

$ curl http://localhost:9201/abc/1

Query Route Predicate

Requests with specified query parameters can match the route.

spring:
  cloud:
    gateway:
      routes:
      - id: query_route
        uri: ${service-url.user-service}/user/getByUsername
        predicates:
        - Query=username

Using curl tool to send a request with username=antonio query parameter can match the route.

$ curl http://localhost:9201/user/getByUsername?username=antonio

Sending a request with or without query parameters using curl tool cannot match the route.

$ curl http://localhost:9201/user/getByUsername

RemoteAddr Route Predicate

Requests originating from the specified remote address can match the route.

spring:
  cloud:
    gateway:
      routes:
      - id: remoteaddr_route
        uri: ${service-url.user-service}
        predicates:
        - RemoteAddr=192.168.1.1/24

The route can be matched by sending a request from 192.168.1.1 using curl tool.

$ curl http://localhost:9201/user/1

Weight Route Predicate

Use the weight to route the corresponding requests. The following shows that 80% of the requests will be routed to localhost:8201 and 20% to localhost:8202.

spring:
  cloud:
    gateway:
      routes:
      - id: weight_high
        uri: http://localhost:8201
        predicates:
        - Weight=group1, 8
      - id: weight_low
        uri: http://localhost:8202
        predicates:
        - Weight=group1, 2

Use of Route Filter

The routing filter can be used to modify the incoming HTTP request and the returned HTTP response. The routing filter can only be used by specifying the route. Spring Cloud Gateway has built-in multiple routing filters, which are generated by the factory class of GatewayFilter. Let's introduce the usage of common routing filters.

AddRequestParameter GatewayFilter

A filter that adds parameters to the request.

spring:
  cloud:
    gateway:
      routes:
        - id: add_request_parameter_route
          uri: http://localhost:8201
          filters:
            - AddRequestParameter=username, antonio
          predicates:
            - Method=GET

The above configuration will add the request parameter of username=antonio to the GET request, and use the following command to test through curl tool.

$ curl http://localhost:9201/user/getByUsername

Equivalent to initiating the request:

$ curl http://localhost:8201/user/getByUsername?username=antonio

StripPrefix GatewayFilter

A filter that removes a specified number of path prefixes.

spring:
  cloud:
    gateway:
      routes:
      - id: strip_prefix_route
        uri: http://localhost:8201
        predicates:
        - Path=/user-service/**
        filters:
        - StripPrefix=2

The above configuration will remove two bits from the path of the request starting with / user service /, and use the following command to test through curl tool.

$ curl http://localhost:9201/user-service/a/user/1

Equivalent to initiating the request:

$ curl http://localhost:8201/user/1

PrefixPath GatewayFilter

Contrary to the StripPrefix filter, it is a filter that adds to the original path.

spring:
  cloud:
    gateway:
      routes:
      - id: prefix_path_route
        uri: http://localhost:8201
        predicates:
        - Method=GET
        filters:
        - PrefixPath=/user

The above configuration will add / user path prefix to all GET requests, and use the following command to test through curl tool.

$ curl http://localhost:9201/1

Equivalent to initiating the request:

$ curl http://localhost:8201/user/1

Hystrix GatewayFilter

The Hystrix filter allows you to add the circuit breaker function to the gateway routing, protect your service from cascading failures, and provide service degradation processing.

To turn on the circuit breaker function, we need to switch on the POM Add the related dependencies of Hystrix in XML:

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

Then add the processing class of related service degradation:

@RestController
public class FallbackController {

    @GetMapping("/fallback")
    public Object fallback() {
        Map<String,Object> result = new HashMap<>();
        result.put("data",null);
        result.put("message","Get request fallback!");
        result.put("code",500);
        return result;
    }
}

In application filter Add relevant configurations in YML. When the route fails, it will be forwarded to the controller for service degradation processing:

spring:
  cloud:
    gateway:
      routes:
        - id: hystrix_route
          uri: http://localhost:8201
          predicates:
            - Method=GET
          filters:
            - name: Hystrix
              args:
                name: fallbackcmd
                fallbackUri: forward:/fallback

Close the user service and call the address to test: http://localhost:9201/user/1 , it is found that the processing information of service degradation has been returned.

RequestRateLimiter GatewayFilter

The RequestRateLimiter filter can be used to limit the flow. The RateLimiter implementation is used to determine whether to allow the current request to continue. If the request is too large, the HTTP 429- too many request status will be returned by default.

In POM Add related dependencies to XML:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>

Add the configuration class of current limiting policy. There are two strategies: one is to limit the flow according to the username in the request parameter, and the other is to limit the flow according to the access IP:

@Configuration
public class RedisRateLimiterConfig {
    @Bean
    KeyResolver userKeyResolver() {
        return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("username"));
    }

    @Bean
    public KeyResolver ipKeyResolver() {
        return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
    }
}

We use Redis to limit the flow, so we need to add the configuration of Redis and RequestRateLimiter. All GET requests are limited by IP:

server:
  port: 9201
spring:
  redis:
    host: localhost
    password: 123456
    port: 6379
  cloud:
    gateway:
      routes:
        - id: requestratelimiter_route
          uri: http://localhost:8201
          filters:
            - name: RequestRateLimiter
              args:
                redis-rate-limiter.replenishRate: 1 #Number of requests allowed to be processed per second
                redis-rate-limiter.burstCapacity: 2 #Maximum number of requests processed per second
                key-resolver: "#{@ipKeyResolver}" #Current limiting policy, Bean corresponding to the policy
          predicates:
            - Method=GET
logging:
  level:
    org.springframework.cloud.gateway: debug

Multiple requests for this address: http://localhost:9201/user/1 , an error with status code 429 will be returned:

Retry GatewayFilter

The filter that retries the routing request can determine whether to retry according to the HTTP status code returned by the routing request.

Modify application Yaml profile:

spring:
  cloud:
    gateway:
      routes:
      - id: retry_route
        uri: http://localhost:8201
        predicates:
        - Method=GET
        filters:
        - name: Retry
          args:
            retries: 1 #Number of retries required
            statuses: BAD_GATEWAY #Which status code needs to be returned for retry? The status code returned is 5XX for retry
            backoff:
              firstBackoff: 10ms
              maxBackoff: 50ms
              factor: 2
              basedOnPreviousValue: false

When the call returns 500, it will retry and access the test address: http://localhost:9201/user/111 , it can be found that the user service console reported an error twice, indicating a retry.

2019-10-27 14:08:53.435 ERROR 2280 --- [nio-8201-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.NullPointerException] with root cause

java.lang.NullPointerException: null
	at com.antonio.cloud.controller.UserController.getUser(UserController.java:34) ~[classes/:na]

Use with registry

Last time we talked about using Zuul as a gateway in combination with the registry. By default, Zuul will create a dynamic route with the service name as the path according to the service list registered in the registry. Gateway also realizes this function. Let's demonstrate how the gateway uses the default dynamic routing and filter in combination with the registry.

Use dynamic routing

In POM Add related dependencies to XML:

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

Add application Eureka YML profile:

server:
  port: 9201
spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true #Enable the function of dynamically creating routes from the registry
          lower-case-service-id: true #Use lowercase service name, which is uppercase by default
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8001/eureka/
logging:
  level:
    org.springframework.cloud.gateway: debug

Use application Eureka YML configuration file starts API gateway service and accesses http://localhost:9201/user-service/user/1 , which can be routed to the user service http://localhost:8201/user/1 Office.

Use filter

When using the filter in combination with the registry, we should note that the protocol of uri is lb, so as to enable the load balancing function of Gateway.

Modify application Eureka YML file, using PrefixPath filter, will add / user path for all GET request paths and route:

server:
  port: 9201
spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
        - id: prefixpath_route
          uri: lb://User service # requires lb protocol
          predicates:
            - Method=GET
          filters:
            - PrefixPath=/user
      discovery:
        locator:
          enabled: true
eureka:
  client:
    service-url: 
      defaultZone: http://localhost:8001/eureka/
logging:
  level:
    org.springframework.cloud.gateway: debug

Use application Eureka YML configuration file starts API gateway service and accesses http://localhost:9201/1 , which can be routed to the user service http://localhost:8201/user/1 Office.

For more dry goods, please move to: https://antoniopeng.com

Tags: Spring Cloud gateway

Posted by Adastra on Tue, 19 Apr 2022 06:48:58 +0930