Service Gateway SpringCloudGateway and Predicate assertion factory

Introduction to Gateway

Spring Cloud Gateway is a product of Spring Cloud itself. It is developed based on Spring 5 and Spring Boot 2.0. Spring Cloud Gateway appears to replace zuul. In the high version of Spring Cloud, zuul 2.0 is not integrated. Spring Cloud Gateway uses a high-performance Reactor Schema communication framework Netty.

The goal of Spring Cloud Gateway is not only to provide a unified routing method, but also to provide basic functions of the gateway based on the Filter chain, such as: security, monitoring/indicators, and current limiting.

Features of Spring Cloud Gateway

  • Based on Spring 5, Project Reactor, Spring Boot 2.0
  • Integrated Hystrix circuit breaker by default
  • Integrate Spring Cloud DiscoveryClient (EurekaClient) by default
  • Predicates and Filters act on specific routes, easy to write Predicates and Filters
  • Support dynamic routing, current limiting, path rewriting

The core concept of Spring Cloud Gataway

Spring Cloud Gataway has several core components:

  • Filter:

The Filter of Spring Cloud Gateway is similar to the filter of Zuul. It can perform some business processing before and after the request is sent. There are two types of Filter here, namely Gateway Filter gateway filter and Global Filter global filter. The difference between them is in the follow-up will talk about.

  • Route:

The basic component module of gateway configuration is similar to Zuul’s routing configuration module. A Route module is defined by an ID, a destination URI, a set of assertions and a set of filters. If the assertion is true, the route matches and the target URI is accessed. To put it bluntly, it is to route the url request to the corresponding resource (service), or how the Gateway should forward the request to the downstream microservice and to whom.

  • Predicate (assertion/url judgment):

This is a Java 8 Predicate that can be used to match anything from an HTTP request, such as headers or parameters. The asserted input type is a ServerWebExchange. A simple understanding is to deal with the matching rules of HTTP requests, and under what circumstances can resources be hit to continue accessing.

How Spring Cloud Gateway works


The client makes a request to Spring Cloud Gateway. If the gateway handler mapping determines that the request matches a route, it is sent to the gateway web handler. This handler runs the request through a request-specific filter chain. The reason filters are separated by a dotted line is so that a filter can run logic before and after the proxy request is sent. All “pre” filter logic is executed. Then make a proxy request. After a proxy request is made, the “post” filter logic is run.

Getting started with Gataway

Create project import dependencies
<!--Service Registration and Discovery-->
 <dependency>
     <groupId>org.springframework.cloud</groupId>
     <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
 </dependency>
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
Startup class configuration
@SpringBootApplication
@EnableDiscoveryClient
public class GatewayApplication {<!-- -->
    public static void main(String[] args) {<!-- -->
        SpringApplication.run(GatewayApplication.class, args);
    }
}
yml configuration
server:
  port: 10060
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:10010/eureka/
  instance:
    instance-id: gateway-server
spring:
  application:
    name: gateway-server
  cloud:
    gateway:
      discovery:
        locator:
          enabled: false #Open service name access method
          lower-case-service-id: true #service name lowercase
      routes:
        - id: user-server #Specify the service name
          uri: lb://user-server #Go to the registration center to find the service name
          predicates: #Assertion, matching access path
            - Path=/user/** #Service access path
          filters:
            - StripPrefix=1 #The /user access path will be removed when the request is forwarded

simple explanation

  • spring.cloud.gateway.discovery.locator.enabled=false: Do not open service name access method
  • spring.cloud.gateway.discovery.locator.lower-case-service-id: true Ignore the case of the service name, both uppercase and lowercase can match
  • spring.cloud.gateway.routes.id : specifies the service name of the route, which can be defined by yourself
  • spring.cloud.gateway.routes.uri=lb://user-server : Go to the registry to find services, and use load balancing to request. In fact, it is to find the service to call.
  • spring.cloud.gateway.routes.predicates: Assert, the Path=/user/** used here, that is, if the matching access path matches /user/, the request can be routed (distributed) to the user-server service.
  • spring.cloud.gateway.routes.filters : The use of StripPrefix=1 here is mainly to process the prefix /user, and the prefix access will be removed when accessing the target service. This needs to be defined according to the url situation.

Predicate assertion factory

What is an assertion factory: official explanation – Spring Cloud Gateway matches routes as part of the Spring WebFlux HandlerMapping infrastructure. Spring Cloud Gateway includes a number of built-in route assertion factories. All of these assertions match against different attributes of the HTTP request. You can combine multiple route assertion factories with logical and statements.

In Spring Cloud Gateway, built-in routing assertion factories for different scenarios, such as
  • Query Route Predicate Factory: do route matching according to query parameters
  • RemoteAddr Route Predicate Factory: do route matching according to ip
  • Header Route Predicate Factory: Route matching according to the parameters in the request header
  • Host Route Predicate Factory: Route matching based on host name
  • Method Route Predicate Factory : Route matching according to the method
  • Cookie Route Predicate Factory: Match according to the attribute value in the cookie
  • Before Route Predicate Factory: Match between specified times
  • After Route Predicate Factory: Match before the specified time
  • Weight Route Predicate Factory: Distribute traffic to different hosts according to weight

Gateway’s Filter

Gateway’s filter can be “pre” and “post” types from the life cycle. According to the scope of action, it can be divided into gateway filter for single route, and Global Filer for all routes

Built-in Gateway filter

A Filter for a single route that allows modifying HTTP requests or HTTP responses in some way. Filters can act on certain specific request paths. Gateway has many built-in GatewayFilter factories. If you want to use these filters, you only need to configure the name of the GatewayFilter Factory in the configuration file

Custom Gateway Filter

In the Spring Cloud Gateway custom filter, the filter needs to implement the two interfaces GatewayFilter and Ordered

public class RequestTimeFilter implements GatewayFilter, Ordered {<!-- -->
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {<!-- -->
        Long startTime = new Date(). getTime();
        return chain.filter(exchange).then(
                Mono. fromRunnable(() -> {<!-- -->
                    Long endTime = new Date(). getTime();
                    System.out.println("Current request consumption time: " + (endTime-startTime));
                })
        );
    }

    @Override
    public int getOrder() {<!-- -->
        return 0;
    }
}

The Filter is configured in the corresponding route

@Configuration
public class FilterConfig {<!-- -->

    //Configure Filter to act on that access rule
    @Bean
    public RouteLocator customerRouteLocator(RouteLocatorBuilder builder) {<!-- -->

        return builder.routes().route(r -> r.path("/services/user2/**")
                //Remove 2 prefixes
                        .filters(f -> f.stripPrefix(2)
                        .filter(new RequestTimeFilter())
                        .addResponseHeader("X-Response-test", "test"))
                        .uri("lb://user-server")
                        .order(0)
                        .id("test-RequestTimeFilter")
                ).build();
    }
}
Custom GlobalFilter

GlobalFilter: The global filter does not need to be configured in the configuration file. It acts on all routes and is finally packaged into a filter recognizable by GatewayFilterChain through GatewayFilterAdapter. It converts the URI of the request business and the route into the request address of the real business service. The core filter does not need to be configured. It is loaded when the system is initialized and acts on each route.
A Filter that simulates a login check

@Component
public class TimeGlobalFilter implements GlobalFilter, Ordered {<!-- -->
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {<!-- -->
        List<String> tokens = exchange.getRequest().getHeaders().get("token");
        if (tokens == null || tokens. size() == 0) {<!-- -->
            //response object
            ServerHttpResponse response = exchange. getResponse();
            // build error result
            HashMap<String,Object> data = new HashMap<>();
            data. put("code",401);
            data.put("message","not logged in");

            DataBuffer buffer = null;
            try {<!-- -->
                byte[] bytes = "Log in first and then enter~~~~".getBytes("utf-8");
                buffer = response.bufferFactory().wrap(bytes);

                //The corresponding setting is completed, and the following filter will not continue to be executed
                //response.setComplete();
                response.setStatusCode(HttpStatus.UNAUTHORIZED);
                response.getHeaders().add("Content-Type","application/json;charset=UTF-8");
            } catch (UnsupportedEncodingException e) {<!-- -->
                e.printStackTrace();
            }

            // write the result to the client
            return response.writeWith(Mono.just(buffer));
        }
        return chain. filter(exchange);
    }

    @Override
    public int getOrder() {<!-- -->
        return 0;
    }
}

Use buffer = response.bufferFactory().wrap(bytes) to construct the response content, and write the content to the client through response.writeWith(Mono.just(buffer));

Gateway cross-domain configuration
spring:
  cloud:
    globalcors: #cross-domain configuration
            cors-configurations:
              '[/**]':
                allowedOrigins: "https://docs.spring.io" #Allowed sites
                allowedMethods: #Allowed request methods
                  - GET
                  - POST
                  - DELETE
                  - PUT
                  - HEAD
                  -CONNECT
                  - TRACE
                  -OPTIONS
                allowHeaders: #Allowed request headers
                  -Content-Type
Gateway timeout
global timeout
spring:
  cloud:
    gateway:
      httpclient:
        connect-timeout: 1000
        response-timeout: 5s
Specify route timeout configuration
spring:
  cloud:
    gateway:
      routes:
      - id: per_route_timeouts
        uri: https://example.org
        predicates:
          -name: Path
            args:
              pattern: /delay/{<!-- -->timeout}
        metadata:
          response-timeout: 200
          connect-timeout: 200