Spring cloud Gateway, service Gateway

Keywords: Spring Cloud Microservices gateway

When the services in the project are subdivided into many, a unified gateway is needed to control the request and response. In the micro service framework, in order to improve the performance of the gateway, the spring cloud gateway is implemented based on the WebFlux framework, and the bottom layer of the WebFlux framework uses the high-performance Reactor mode communication framework Netty.

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

Gateway service gateway

Gateway functions:

  • Identity authentication and authority verification
  • Service routing and load balancing
  • Request current limiting

Gateway technology implementation:

  • Gateway
  • zuul

zuul is a servlet based implementation, which belongs to blocking programming, while springcloud Gateway is based on webFlux provided in spring5, which belongs to the implementation of responsive programming and has better performance

Build gateway service

Steps of building gateway

  1. The key new module introduces Gateway dependency and nacos service discovery dependency
   <dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    <dependency>
      <groupId>org.springframework.cloud</groupId>
      <artifactId>spring-cloud-starter-gateway</artifactId>
    </dependency>
  1. Routing configuration and nacos registration
spring:
  application:
    name: gateway # Service name
  cloud:
    nacos:
      server-addr: 127.0.0.1:8848 # nacos service address
    gateway:
      routes: # Configure routing
        - id: user-service # Routing id, user-defined, as long as it is unique
#          uri: http://127.0.0.1: destination address of 8082 # route
          uri: lb://Target address lb of userservice # route indicates load balancing. / / service name
          predicates: # Route assertion is to judge whether the request conforms to the routing rules
            - Path=/user/** # Path matching rule, compliant only if it starts with / user

The configurable contents of the gateway include:

  • Route id: unique identifier
  • uri: routing destination. lb and http are supported
  • Predictions: route assertion, which determines whether the request meets the requirements and then forwards it
  • Filters: routing filters that process requests or responses

Access the userservice service through the Gateway, 127.0.0.1:10010/user can access the user service

Service gateway structure diagram

Route assertion

  • The assertion rules written in the configuration file are just strings, which are read and processed by the Predicate factory and transformed into conditions for routing judgment
  • There are more than a dozen assertion factories provided by spring cloud gateway

For example, Path=/user / * * matches by path. This rule is handled by PathRoutePredicateFactory

Basic predicate factory in 11

nameexplainExample
AfterRequest after a certain point in time- After=2021-09-13T17:32:32.345-07:00(America/Denver)
BeforeRequests before a certain point in time- Beforer=2021-09-13T17:32:32.345+07:00(America/Denver)
BetweenRequests before two points in time- between=2021-09-13T17:32:32.345+07:00(America/Denver),2021-09-13T17:32:32.345+07:00(America/Denver)
CookieThe request must contain a cookie- Cookie=XXX
HeaderThe request must contain a header-Header=token,X-Request
HostThe request must be to access a host- Host=**,www.baidu.com
MethodRequest mode- Method=GET,POST
PathRequest path rule-Path=/test/**
QueryThe request must contain the specified parameters-Query=name,XXX
RemoteAddrThe ip address of the requester must be in the specified range- RemoteAddr=192.168.1.1/24
WeightWeight processing

Detailed usage reference: https://www.cnblogs.com/wgslucky/p/11396579.html

Explanation on the official website: https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gateway-request-predicates-factories

Routing filter

Gateway Filter is a filter provided in the gateway, which can process the requests entering the gateway and the responses returned by the microservice:

Spring provides 31 different routing filter factories, such as:

nameexplain
AddRequestHeaderAdd a request header to the current request
RemoveRequestHeaderRemove request header from request
AddResponseHeaderAdd a response header to the response
RemovResponseHeaderRemove the response header of the response
RequestRateLimiterLimit requested traffic
......

Explanation on the official website: https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gatewayfilter-factories

Example: add request header to userservice

spring:
  application:
    name: gateway # Service name
  cloud:
    nacos:
      server-addr: 127.0.0.1:8848 # nacos service address
    gateway:
      routes: # Configure routing
        - id: user-service # Routing id, user-defined, as long as it is unique
#          uri: http://127.0.0.1: destination address of 8082 # route
          uri: lb://Target address lb of userservice # route indicates load balancing. / / service name
          predicates: # Route assertion is to judge whether the request conforms to the routing rules
            - Path=/user/** # Path matching rule, compliant only if it starts with / user
          filters: # filter
            - AddRequestHeader=Message,spring cloud is good # Add request header
        - id: order-service
          uri: http://127.0.0.1:8082
          predicates:
            - Path=/order/**
      default-filters: # The default filter works for all routes
        - AddRequestHeader=Message,spring cloud is good

Test by reading the request header in the controller:

 @GetMapping("/{id}")
    public UserEntity getUser(@PathVariable String id, @RequestHeader("Message") String message){
        // Print out request header information
        logger.info("Request header:"+ message);
    }

Global filter

The function of GlobalFilter is to process all requests and microservice responses entering the gateway. It is the same as that of GatewayFilter. The difference is that the processing logic of GatewayFilter is fixed through configuration definition, while the logic of GlobalFilter needs to implement its own code

How to define

Implement GlobalFilter interface

/**
* Process the current request,
* @param exchange Request context, which can obtain request, response and other information
* @param chain Used to delegate requests to the next filter
* @return Mono<Void> Returns the end of the current filter business
*/
public interface GlobalFilter {
    Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
}

Example: define a global interceptor to judge whether the request parameters are compliant

  • Whether the parameter has authorization
  • Is authorization admin

If satisfied, release; otherwise, write back the response 401

//@Order(-1)
@Component
public class AuthorizationFilter implements GlobalFilter, Ordered {
    private static final Logger logger = LoggerFactory.getLogger(AuthorizationFilter.class);
    /**
     * @description: Verified user
     */
    private static final String ADMIN_NAME = "admin";
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 1. Get request parameters
        ServerHttpRequest request = exchange.getRequest();
        MultiValueMap<String, String> queryParams = request.getQueryParams();
        // 2. Get parameter value
        String authorization = queryParams.getFirst("authorization");
        // 3. Judge whether it is admin
        if (!ADMIN_NAME.equals(authorization)) {
            // 4. If the conditions are not met, write back the response
            ServerHttpResponse response = exchange.getResponse();
            Map<String, Object> responseData = Maps.newHashMap();
            responseData.put("code", 401);
            responseData.put("message", "Illegal request");
            responseData.put("cause", "user is not admin");
            try {
                // Convert information to JSON
                ObjectMapper objectMapper = new ObjectMapper();
                byte[] data = objectMapper.writeValueAsBytes(responseData);
                // Output error message to page
                DataBuffer buffer = response.bufferFactory().wrap(data);
                response.setStatusCode(HttpStatus.UNAUTHORIZED);
                response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
                return response.writeWith(Mono.just(buffer));
            } catch (Exception e) {
                logger.error(e.getMessage(), e);
            }
        }
        // 5. Comply with the rules and let go
        return chain.filter(exchange);

    }

    /**
     * @description: Define the filter execution order. The smaller the value, the higher the priority
     */
    @Override
    public int getOrder() {
        return -1;
    }
}

Start project, access path http://localhost:10010/user/1 , an error is reported {"code": 401, "cause": "user is not admin", "message": "illegal request"}

Revisit: http://localhost:10010/user/1?authorization=admin, successfully obtained the result {userId ":" 002 "," name ":" Li Si "}

There will be multiple filters in the actual project. How to control their execution order? (refer to the above example code)

  • @Order(XX) adds the annotation on the filter class

    XX is a specific value. The smaller the value, the higher the priority

  • Implement the Ordered interface and override getOrder()

   /**
       * @description: Define the filter execution order. The smaller the value, the higher the priority
       */
      @Override
      public int getOrder() {
          return -1;
      }

The above two methods are equivalent. Select one of them to use

Filter execution sequence

Three types of filters will be encountered when requesting to enter the gateway:

  • Current routing filter
  • DefaultFilter
  • GlobalFilter

After requesting a route, the current route filter, DefaultFilter and GlobalFilter will be merged into a filter chain (Collection), and each filter will be executed in turn after sorting

How to merge the three filters if they are not of the same type?

The current routing filter and DefaultFilter are actually GatewayFilter, while GlobalFilter will be converted to GatewayFilter through the GatewayFilterAdapter adapter, and then the three will be combined into a filter chain

summary

  • Each filter must set an int order. The smaller the order, the higher the priority
  • GlobalFilter can customize the order value by implementing the Ordered interface or the @ order annotation
  • The order of the routing filter and the DefaultFilter is specified by spring and is incremented from 1 by default
  • When the filter order value is the same, it will be executed in the order of defaultfilter > routing filter > globalfilter

Refer to the source code:
Org.springframework.cloud.gateway.route.routedefinitiongetfilters method in routelocator, first load the defaultFilter, then load the filters of a route, and finally merge

private List<GatewayFilter> getFilters(RouteDefinition routeDefinition) {
        List<GatewayFilter> filters = new ArrayList();
        if (!this.gatewayProperties.getDefaultFilters().isEmpty()) {
            filters.addAll(this.loadGatewayFilters("defaultFilters", new ArrayList(this.gatewayProperties.getDefaultFilters())));
        }

        if (!routeDefinition.getFilters().isEmpty()) {
            filters.addAll(this.loadGatewayFilters(routeDefinition.getId(), new ArrayList(routeDefinition.getFilters())));
        }

        AnnotationAwareOrderComparator.sort(filters);
        return filters;
    }

The handle method in org.springframework.cloud.gateway.handler.FilteringWebHandler loads the global filter GlobalFilter, then merges with the previous filters, and sorts according to the order to form a filter chain

public Mono<Void> handle(ServerWebExchange exchange) {
        Route route = (Route)exchange.getRequiredAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
        List<GatewayFilter> gatewayFilters = route.getFilters();
        List<GatewayFilter> combined = new ArrayList(this.globalFilters);
        combined.addAll(gatewayFilters);
        AnnotationAwareOrderComparator.sort(combined);
        if (logger.isDebugEnabled()) {
            logger.debug("Sorted gatewayFilterFactories: " + combined);
        }

        return (new FilteringWebHandler.DefaultGatewayFilterChain(combined)).filter(exchange);
    }

Cross domain

Domain name inconsistency is cross domain, including:

  • Different domain names
  • Different ports

Cross domain problem: the browser prohibits cross domain ajax requests between the originator and the server, and the requests will be intercepted by the browser

Solution: CORS

The configuration is as follows:

spring:
  cloud:
    gateway:
      globalcors: # Global cross domain processing
        add-to-simple-url-handler-mapping: true # Solve the problem that the options request is blocked
        cors-configurations:
          '[/**]':
            allowedOrigins: # Allow those sites to cross domain
              - "http://localhost:8090"
              - "http://www.baidu.com"
            allowedMethods: # Allow cross domain ajax request mode
              - "GET"
              - "POST"
              - "PUT"
            allowedHeaders: "*" # Allow cross domain request header information
            allowedCredentials: true # Allow cookies
            maxAge: 36000 # Validity period of this cross domain detection

Please refer to the official documentation for more configurations

Posted by bruceleejr on Tue, 14 Sep 2021 22:43:35 -0700