Service gateway

Role of gateway

Performance: API high availability, load balancing, fault tolerance mechanism

Security: authority, identity authentication, desensitization, traffic cleaning, back-end signature (to ensure the trusted call of the whole link), blacklist (restriction of illegal call)

Log: log recording. Once distributed is involved, full link tracking is essential

Caching: data caching

Monitoring: record request response data, API time-consuming analysis and performance monitoring

Current limiting: flow control, peak shifting flow control, and multiple current limiting rules can be defined

Grayscale: Online grayscale deployment can reduce risk

Routing: dynamic routing rules

Core concept

  • Routing: routing is the most basic part of the gateway. Routing information consists of ID, target URI, a set of assertions (routing rules) and a set of filters. If the assertion is true, the requested URI and configuration match

  • Assertion: assertion function in Java 8. The input type of the assertion function in the Spring cloud Gateway is ServerWebExchange in the Spring 5.0 framework. The assertion function in Spring cloud Gateway allows developers to define and match any information from Http Request, such as request header and parameters.

  • Filter: a standard Spring Web Filter. There are two types of filters in Spring cloud Gateway: Gateway Filter and Global Filter. The filter will process the request and the corresponding.

Build an introductory case

Add dependency

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

configuration file

spring:
  application:
    name: gateway
  # Routing rules
  cloud:
    gateway:
      routes:
        - id: user-service            # Route ID, globally unique
          uri: http://localhost:8080 / # destination uri, which is routed to the microservice address
          predicates:                 # Assertion (judgment condition)
            - Path=/api/user/**       # For the request matching the corresponding URL, append the matching request to the target URI
server:
  port: 9999
logging:
  level:
    root: info

Routing rules

Path

spring:
  application:
    name: gateway
  # Routing rules
  cloud:
    gateway:
      routes:
        - id: user-service            # Route ID, globally unique
          uri: http://localhost:8080 / # destination uri, which is routed to the microservice address
          predicates:                 # Assertion (judgment condition)
             - Path=/api/user/**       # For the request matching the corresponding URL, append the matching request to the target URI

Query

spring:
  application:
    name: gateway
  # Routing rules
  cloud:
    gateway:
      routes:
        - id: user-service            # Route ID, globally unique
          uri: http://localhost:8080 / # destination uri, which is routed to the microservice address
          predicates:                 # Assertion (judgment condition)
             #- Query=token           # Match the request with token in the request parameter
             - Query=token,abc.        # The matching request parameter contains token and the parameter value meets the request of regular expression abc

Query=token, for example, http://localhost:9999/api/user/2?token=123

Query=token,abc. For example, http://localhost:9999/api/user/2?token=abc1

Method

spring:
  application:
    name: gateway
  # Routing rules
  cloud:
    gateway:
      routes:
        - id: user-service            # Route ID, globally unique
          uri: http://localhost:8080 / # destination uri, which is routed to the microservice address
          predicates:                 # Assertion (judgment condition)
             - Method=GET        # Match any GET request

Datetime

spring:
  application:
    name: gateway
  # Routing rules
  cloud:
    gateway:
      routes:
        - id: user-service            # Route ID, globally unique
          uri: http://localhost:8080 / # destination uri, which is routed to the microservice address
          predicates:                 # Assertion (judgment condition)
            # Matching requests after 20:20:20, 2022-02-02 Shanghai time
            - After=2022-02-02T20:20:20.000+08:00[Asia/Shanghai]

RemoteAddr

spring:
  application:
    name: gateway
  # Routing rules
  cloud:
    gateway:
      routes:
        - id: user-service             # Route ID, globally unique
          uri: http://localhost:8080 / # destination uri, which is routed to the microservice address
          predicates:                  # Assertion (judgment condition)
            - RemoteAddr=12.12.7.250/0 # The matching remote address is the request of RemoteAddr, and 0 represents the subnet mask

RemoteAddr=12.12.7.250/0, for example http://12.12.7.250:9999/api/user/2?token=abc1

Header

spring:
  application:
    name: gateway
  # Routing rules
  cloud:
    gateway:
      routes:
        - id: user-service            # Route ID, globally unique
          uri: http://localhost:8080 / # destination uri, which is routed to the microservice address
          predicates:                 # Assertion (judgment condition)
            # The matching request header contains the request_id and its value meets the request of regular expression \ d +
            - Header=request_id,\d+

Dynamic routing (routing rules for service discovery)

Dynamic routing is service-oriented routing. spring cloud gateway supports integration with eureka. It automatically registers the service address and forwards the request according to the serviceId. The advantage of this is that it can not only access all services through a single point, but also do not need to modify the routing configuration of the Gateway when adding or removing services.

Add dependency

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

configuration file

spring:
  application:
    name: gateway
  # Routing rules
  cloud:
    gateway:
      routes:
        - id: user-service            # Route ID, globally unique
          uri: lb://User # LB: / / obtains the service request address from the registry according to the service name
          predicates:                 # Assertion (judgment condition)
            - Path=/api/user/**       # For the request matching the corresponding URL, append the matching request to the target URI

filter

spring cloud gateway is divided into GatewayFilter and GlobalFilter according to the scope of action. The differences between the two are as follows

Gateway filter: gateway filter, which needs to pass spring cloud. gateway. routes. Filters are configured under specific routes and only work on the current route or through spring cloud. gataway. Default filters are configured globally and act on all routes.

GlobalFilter: Global filter. It does not need to be configured in the configuration file. It works on all routes. Finally, it is packaged into a filter recognizable by GatewayFilterChain through GatewayFilterAdapter. It is the core filter that converts the URI of the request service and route into the request address of the real service. It does not need to be loaded during system initialization and acts on each route.

Gateway filter
Path filter

RewritePath GatewayFilter Factory

The RewritePath GatewayFilter factory adopts the path regexp parameter and replacement parameter. This uses Java regular expressions to flexibly rewrite the request path.

spring:
  application:
    name: gateway
  # Routing rules
  cloud:
    gateway:
      routes:
        - id: user-service            # Route ID, globally unique
          uri: lb://User # LB: / / obtains the service request address from the registry according to the service name
          predicates:                 # Assertion (judgment condition)
            # For the request matching the corresponding URL, append the matching request to the target URI
            - Path=/api/user/**,/gateway/**
          filters:
            # Rewrite / gateway/api/user/1 to / api/user/1
            # Note: use $\ in YAML format instead of $.
            - RewritePath=/gateway/?(?<segment>.*),/$\{segment}

Note: in YAML format, use KaTeX parse error: Undefined control sequence: \ to at position 1:\ ̲ come ̲ Replace.

PrefixPathGatewayFilterFacotry

spring:
  application:
    name: gateway
  # Routing rules
  cloud:
    gateway:
      routes:
        - id: user-service            # Route ID, globally unique
          uri: lb://User # LB: / / obtains the service request address from the registry according to the service name
          predicates:                 # Assertion (judgment condition)
            - Path=/**       # For the request matching the corresponding URL, append the matching request to the target URI
          filters:
            # Rewrite / 1 to / api/user/1
            - PrefixPath=/api/user

StripPrefixGatewayFilterFactory

The StripPrefix gateway filter factory adopts a parameter StripPrefix, which indicates the number of paths stripped from the request before sending the re request to the downstream

spring:
  application:
    name: gateway
  # Routing rules
  cloud:
    gateway:
      routes:
        - id: user-service            # Route ID, globally unique
          uri: lb://User # LB: / / obtains the service request address from the registry according to the service name
          predicates:                 # Assertion (judgment condition)
            - Path=/**       # For the request matching the corresponding URL, append the matching request to the target URI
          filters:
            # Rewrite / a/b/api/user/2 to / api/user/2
            - StripPrefix=2

SetPathGatewayFilterFactory

The SetPath gateway filter factory adopts the path template parameter. It provides a simple method to operate the request path by allowing templated path segments. It uses the uri template in the Spring Framework and allows multiple matching segments.

spring:
  application:
    name: gateway
  # Routing rules
  cloud:
    gateway:
      routes:
        - id: user-service            # Route ID, globally unique
          uri: lb://User # LB: / / obtains the service request address from the registry according to the service name
          predicates:                 # Assertion (judgment condition)
            - Path=/a/b/{segment}       # For the request matching the corresponding URL, append the matching request to the target URI
          filters:
            # Rewrite / a/b/2 to / api/user/2
            - SetPath=/api/user/{segment}
Parameter filter

AddRequestParameter the gateway filter factory will add the specified parameter to the matching downstream request.

spring:
  application:
    name: gateway
  # Routing rules
  cloud:
    gateway:
      routes:
        - id: user-service            # Route ID, globally unique
          uri: lb://User # LB: / / obtains the service request address from the registry according to the service name
          predicates:                 # Assertion (judgment condition)
            - Path=/api/user/**       # For the request matching the corresponding URL, append the matching request to the target URI
          filters:
            # Add flag parameter in downstream request
            - AddRequestParameter=flag,1
Status filter

Status filter

The SetStatus gateway filter factory adopts a single status parameter, which must be a valid Spring HttpStatus. It can be an integer 404 or an enumeration not_ The string representation of found.

spring:
  application:
    name: gateway
  # Routing rules
  cloud:
    gateway:
      routes:
        - id: user-service            # Route ID, globally unique
          uri: lb://User # LB: / / obtains the service request address from the registry according to the service name
          predicates:                 # Assertion (judgment condition)
            - Path=/api/user/**       # For the request matching the corresponding URL, append the matching request to the target URI
          filters:
            # In any case, the HTTP status of the response will be set to 404
            - SetStatus=404
Custom gateway filter

Custom gateway filters need to implement the following two interfaces: GatewayFilter and Ordered

Create filter
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.core.Ordered;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
 * Custom gateway filter
 */
public class CustomerGatewayFilter implements GatewayFilter , Ordered {
    private Logger log= LoggerFactory.getLogger(CustomerGatewayFilter.class);

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("Custom gateway filter executed--");
        // Continue to execute downward. If it is not satisfied, chain. Will not be called filter
        return chain.filter(exchange);
    }

    /**
     * Filter execution order: the smaller the value, the higher the priority
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }
}
Register gateway filter
/**
 * Gateway routing configuration class
 */
@Configuration
public class GatewayRoutesConfiguration {

    @Bean
    public RouteLocator routeLocator(RouteLocatorBuilder builder){
        return builder.routes().route(r -> r
                // Assertion (judgment condition)
                .path("/api/user/**")
                // Destination URI, the address of the route to the microservice
                .uri("lb://user")
                // Register custom gateway filter
                .filter(new CustomerGatewayFilter())
                // Route ID, unique
                .id("user-service2")).build();
    }
}
Custom filter factory

In the above custom filter, is there any way to customize the filter factory class? In this way, you can configure the filter in the configuration file. Now you need to implement a filter factory.

The custom filter factory inherits AbstractGatewayFilterFactory, and the custom class name should be "name" + GatewayFilterFactory

package com.gateway;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import reactor.core.publisher.Mono;
import java.util.Arrays;
import java.util.List;

/**
 * Naming rules must be based on "name" + GatewayFilterFactory
 */
public class RequestTimeGatewayFilterFactory extends AbstractGatewayFilterFactory <RequestTimeGatewayFilterFactory.Config>{

    private Logger log= LoggerFactory.getLogger(RequestTimeGatewayFilterFactory.class);

    private static final String KEY = "print";

    public RequestTimeGatewayFilterFactory(){
        super(Config.class);
    }

    /**
     * Set the properties in the Config configuration class through the shortcutFieldOrder method,
     * By default, the values are assigned in the order of shortcutFieldOrder
     */
    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList(KEY);
    }

    @Override
    public GatewayFilter apply(Config config) {
        // grab configuration from Config object
        return (exchange, chain) -> {
//            //If you want to build a "pre" filter you need to manipulate the
//            //request before calling chain.filter
//            ServerHttpRequest.Builder builder = exchange.getRequest().mutate();
//            //use builder to manipulate the request
//            return chain.filter(exchange.mutate().request(builder.build()).build());
            Long beginTime=System.currentTimeMillis();
            log.info("start time:{}",beginTime);
            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                //ServerHttpResponse response = exchange.getResponse();
                //Manipulate the response in some way
                Long endTime=System.currentTimeMillis();
                if(config.isPrint()){
                    log.info("Request time:{}",endTime-beginTime);
                }
            }));
        };
    }

    public static class Config{
        //Put the configuration properties for your filter here
        private boolean print;

        public boolean isPrint(){
            return print;
        }

        public void setPrint(boolean print){
            this.print=print;
        }
    }
}

    @Bean
    public RequestTimeGatewayFilterFactory elapsedGatewayFilterFactory() {
        return new RequestTimeGatewayFilterFactory();
    }

configuration file

spring:
  application:
    name: gateway
  # Routing rules
  cloud:
    gateway:
      routes:
        - id: user-service            # Route ID, globally unique
          uri: lb://User # LB: / / obtains the service request address from the registry according to the service name
          predicates:                 # Assertion (judgment condition)
            - Path=/api/user/**       # For the request matching the corresponding URL, append the matching request to the target URI
          filters:
            # Current limiting filter
            - name: RequestRateLimiter
              args:
                redis-rate-limiter.replenishRate: 1 # Token bucket fill rate per second
                redis-rate-limiter.burstCapacity: 2 # Total capacity of token bucket
                key-resolver: '#{@keyResolver}' # Use a SpEL expression to reference bean s by name
            # Custom filter, parameter print=true
            - RequestTime=true
Global filter

GlobalFilter: Global filter. It does not need to be configured in the configuration file. It works on all routes. Finally, it is packaged into a filter recognizable by GatewayFilterChain through GatewayFilterAdapter. It is the core filter that converts the URI of the request service and route into the request address of the real service. It does not need to be loaded during system initialization and acts on each route.

include:

Unified authentication through global filter

Next, we customize the global filter and judge whether the user logs in through the token to complete unified authentication

Create filter
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
 * Permission verification filter
 */
@Component
public class AccessFilter implements GlobalFilter, Ordered {
    private Logger log= LoggerFactory.getLogger(AccessFilter.class);

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String token=exchange.getRequest().getQueryParams().getFirst("token");
        if(token==null){
            log.warn("token is null");
            ServerHttpResponse response = exchange.getResponse();
            response.setStatusCode(HttpStatus.UNAUTHORIZED);
            DataBuffer dataBuffer=response.bufferFactory().wrap("result".getBytes());
            return response.writeWith(Mono.just(dataBuffer));
        }
        log.info("token is ok");
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return 1;
    }
}

Gateway current limiting

Common current limiting algorithms include:

  • Counter algorithm: limit a fixed number of times within the time range
  • Leaky bucket algorithm: water flows into the bucket at any rate and out at a certain rate. When the water exceeds the bucket flow, it is discarded. It will lead to request accumulation and excessive pressure on the gateway; Discard the request; As long as the algorithm is to protect micro services and hurt itself.
  • Token bucket algorithm: Based on leaky bucket algorithm, it can limit the request rate and allow a certain degree of burst calls. In this algorithm, there is a bucket for storing a fixed number of tokens. There is a mechanism in the algorithm, which puts tokens into the bucket at a certain rate. Each request call must first obtain the token. Only after obtaining the token can the execution continue. Otherwise, choose to wait for the available token. Or refuse directly. The token releasing action is continuous. If the token in the bucket reaches the online level, the token will be discarded.

spring cloud gateway uses this algorithm internally, which is roughly described as follows:

  • All requests need to get an available token before being processed;
  • According to the current limit, set to add tokens to the bucket at a certain rate
  • The bucket sets the maximum limit for placing tokens. When the bucket is full, the newly added tokens will be discarded or rejected.
  • After the request arrives, first obtain the token in the token bucket, and then carry out other business logic with the token. After processing the business logic, delete the token directly.
  • The token bucket has a minimum limit. When the token in the bucket reaches the minimum limit, the token will not be deleted after the request is processed, so as to ensure sufficient current limit.
Gateway current limiting

Add dependency

        <!--spring dta redis reactive rely on -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
        </dependency>
        <!-- redis common pool2 Object pool dependency-->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
        </dependency>

configuration file

spring:
  application:
    name: gateway
  # Routing rules
  cloud:
    gateway:
      routes:
        - id: user-service            # Route ID, globally unique
          uri: lb://User # LB: / / obtains the service request address from the registry according to the service name
          predicates:                 # Assertion (judgment condition)
            - Path=/api/user/**       # For the request matching the corresponding URL, append the matching request to the target URI
          filters:
            # Current limiting filter
            - name: RequestRateLimiter
              args:
                redis-rate-limiter.replenishRate: 1 # Token bucket fill rate per second
                redis-rate-limiter.burstCapacity: 2 # Total capacity of token bucket
                key-resolver: '#{@pathKeyResolver}' # Use a SpEL expression to reference bean s by name

Register current limiting rule class

@Configuration
public class KeyResolverConfiguration {

    private Logger log= LoggerFactory.getLogger(KeyResolverConfiguration.class);

    /**
     * Limit current according to URI
     * @return
     */
    @Bean
    public KeyResolver pathKeyResolver(){
        log.info("register pathKeyResolver");
        // JDK 1.8
        return exchange -> Mono.just(exchange.getRequest().getURI().getPath());
    }
}
    /**
     * Current limiting according to parameters
     * @return
     */
    @Bean
    public KeyResolver keyResolver(){
        log.info("register pathKeyResolver");
        // JDK 1.8
        return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("userId"));
    }
    /**
     * Current limiting according to IP
     * @return
     */
    @Bean
    public KeyResolver keyResolver(){
        log.info("keyResolver");
        // JDK 1.8
        return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
    }

lver");
// JDK 1.8
return exchange -> Mono.just(exchange.getRequest().getURI().getPath());
}
}

/**
 * Current limiting according to parameters
 * @return
 */
@Bean
public KeyResolver keyResolver(){
    log.info("register pathKeyResolver");
    // JDK 1.8
    return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("userId"));
}
/**
 * Current limiting according to IP
 * @return
 */
@Bean
public KeyResolver keyResolver(){
    log.info("keyResolver");
    // JDK 1.8
    return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
}

Keywords: Spring Cloud gateway

Added by Manat on Fri, 28 Jan 2022 01:43:12 +0200