Gateway Gateway application analysis and implementation

Quick start

Introduction service Implementation

(1) Create scA-Gateway module

Select 01-nacos-config, right-click new-> Module and select Maven project.

Step 1: Create the SCA-Gateway module with the following pom.xml file:

<? The XML version = "1.0" encoding = "utf-8"? > < project XMLNS = "http://maven.apache.org/POM/4.0.0" XMLNS: xsi = "http://www.w3.org/2001/XMLSchema-instance" Xsi: schemaLocation = "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" > < modelVersion > 4.0.0 < / modelVersion > < the parent > < artifactId > 01 - sca < / artifactId > < groupId > com. Cy < / groupId > 1.0 the SNAPSHOT < version > < / version > < / parent > < groupId > org. Cy < / groupId > < artifactId > sca - gateway < / artifactId > 1.0 the SNAPSHOT < version > < / version > < dependencies > < the dependency > < groupId > org. Springframework. Cloud < / groupId > <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> </dependencies> </project>Copy the code

(2) Add the configuration

Add the bootstrap. Yml

server:
  port: 9000
spring:
  application:
    name: sca-gateway
  cloud:
    gateway:
      routes:
        - id: route01
          uri: http://localhost:8081/
          predicates: ### Match rules
              - Path=/nacos/provider/echo/**
          filters:
              - StripPrefix=1 Remove layer 1 paths from path, such as nacos, before forwarding
Copy the code

Route is one of the most basic components of gateway. It represents a specific routing information carrier. It mainly defines the following information:

  1. Id, the Route identifier, which is different from other routes.

  2. Uri, the destination URI to which the route points, that is, the microservice to which the client request is ultimately forwarded.

  3. Predicate, predicate, predicate, predicate, predicate, predicate, predicate, predicate, predicate, predicate, predicate

  4. Filter: A filter is used to modify request and response information.

(3) Start the project

Step 3: Start the project for access testing, as shown in the figure below:

Start an 8081 provider that must have a provider/echo/** request method

http://localhost:9000/nacos/provider/echo/hello
Copy the code

Load balancing design

Why load balancing?

All services are mapped at the gateway level. Therefore, when accessing services, you need to search for corresponding services based on service ID (service name) and forward requests from the gateway layer in a balanced manner to balance the processing capacity of service instances.

Load balancing in Gateway?

All services are mapped at the gateway level. Therefore, when accessing services, you need to search for corresponding services based on service ID (service name) and forward requests from the gateway layer in a balanced manner to balance the processing capacity of service instances.

Load balancing in Gateway?

(1) Add discovery dependencies

In the SCA-Gateway project

Step 1: Add service discovery dependencies to the project as follows:

<dependency>
  <groupId>com.alibaba.cloud</groupId>
  <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
Copy the code

(2) Modify the configuration file

Step 2: Modify its configuration file with the following code

server:
  port: 9000
spring:
  application:
    name: sca-gateway
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 Service discovery, while the current service is registered with NACOS
    gateway:
      discovery:
        locator:
          enabled: true
      routes:
        - id: route01
          ##uri: http://localhost:8081/
          uri: lb://nacos-provider # lb is a service prefix and cannot be written arbitrarily
          predicates: ### Match rules
            - Path=/nacos/provider/echo/**
          filters:
            - StripPrefix=1 Remove layer 1 paths from path, such as nacos, before forwarding
Copy the code

Lb refers to getting microservices by name from NACOS and following a load balancing policy. At the same time, it is recommended to open the Gateway log during the development phase. The code is as follows:

logging:
  level:
    org.springframework.cloud.gateway: debug
Copy the code

(3) Start the service for testing

Step 3: Start the service, conduct access tests, and refresh analysis repeatedly, as shown in the figure:

Perform process analysis

According to the official description, the specific working process of the Gateway is as shown in the figure:

The client makes a request to the Spring Cloud Gateway. If the Gateway Handler Mapping determines that the request matches the route through a collection of assertions, it sends it to the Gateway Web Handler. The Gateway Web Handler invokes the filter through a chain of filter sets configured in the identified route (the so-called chain of responsibility pattern). The reason the filters are separated by dashed lines is that the Filter can run logic before and after the agent request is sent. The logic of processing is that the first filter is executed when the request is processed and the second filter is executed when the processing returns the corresponding value.

Assertion enhancement analysis

Predicate is also a term used to determine conditions. The route is executed only after the Predicate results are true. The essence of assertions is to define routing and forwarding conditions.

Predicate built-in Factory

The SpringCloud Gateway includes assertion factories that need to be built in, all of which match the different attributes of the HTTP request as follows:

Datetime-based assertion factories This type of assertion is based on time, and there are three main types:

1) AfterRoutePredicateFactory: whether the request date later than the specified date

2) BeforeRoutePredicateFactory: whether the request date before the specified date

3) BetweenRoutePredicateFactory: whether the request date within a specified time period

- After = 2020-12-31 T23:59:59. 789 + 08:00 Asia/Shanghai
Copy the code

The request is forwarded only when the request time is After the configured time. If the request time is not After the configured time, 404 Not Found is displayed. Also, when only one Predicate is configured for the predicates configuration item and Path is not configured, the default value of Path is /. So this configuration will forward the access to GATEWAY_URL/ to the /** of the user-Center microservice. The time value is available from zonedDatetime.now ().

Based on the remote address of assertion factory RemoteAddrRoutePredicateFactory: receive an IP address, to determine whether a request to the host address in the specified address, such as:

- RemoteAddr = 192.168.1.1/24
Copy the code

Based on the assertion Cookie factory, CookieRoutePredicateFactory: accepts two parameters, the Cookie name and a regular expression. Determines whether the requested cookie has the given name and the value matches the regular expression. Such as:

-Cookie=chocolate, ch
Copy the code

Based on the header of assertion factory, HeaderRoutePredicateFactory: accepts two parameters, the title name and regular expressions. Determines whether the request Header has the given name and the value matches the regular expression. Such as:

-Header=X-Request-Id, \d+
Copy the code

Assertions based on Host plant, HostRoutePredicateFactory: receiving a parameter, the Host name. Check whether the requested Host meets the matching rules, for example:

-Host=**.testhost.org
Copy the code

Based on the Method that request Method factory, MethodRoutePredicateFactory receives a parameter, to determine whether a request type matching with the specified type. Such as:

-Method=GET
Copy the code

Based on the Path that request Path factory PathRoutePredicateFactory, receiving a parameter, judge whether the request URI part meet the Path rules, such as:

-Path=/foo/{segment}
Copy the code

Based on Query request parameters of assertion factory, QueryRoutePredicateFactory: accepts two parameters, request param and regular expression, to determine whether a request parameter with the given name and value matches the regular expression. Such as:

-Query=baz, ba.
Copy the code

Based on routing weight of assertion factory, WeightRoutePredicateFactory: receive a/group name, weight, and then for routing according to the weight forward within the same group, such as:

routes:
-id: weight_route1 
-uri: host1 predicates:
-Path=/ehco/**
-Weight=group2, 2

-id: weight_route2 
-uri: host2 predicates:
-Path=/ehco/**
-Weight= group2, 8
Copy the code

Predicate uses case actions

Built-in routing assertion factory application cases, such as:

server:
  port: 9000
spring:
  application:
    name: sca-gateway
  cloud:
    nacos:
      server-addr: localhost:8848
    gateway:
      discovery:
        locator:
          enabled: true # Enable the function of creating routes using the serviceId of the service center
      routes:
        - id: bd-id
          ##uri: http://localhost:8081/
          uri: lb://nacos-provider
          predicates: ### Match rules
              - Path=/nacos/provider/echo/**
              - Before = 2021-01-30 T00:00:00) 000 + 08:00
              - Method=GET
          filters:
            -  StripPrefix=1 Delete layer 1 path before forwarding
Copy the code

Note: If the conditions are not met, route forwarding cannot be performed, and a 404 exception occurs. Time is not satisfied

Predicate custom analysis and implementation

Service Description: Set the value of paging page through assertions.

Business implementation:

(1) Define assertions

Step 1: Define the assertions

package com.cy.predicates;

import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;

import javax.sound.midi.Soundbank;
import java.net.SocketTimeoutException;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

@Component
class PageRoutePredicateFactory extends AbstractRoutePredicateFactory<PageRoutePredicateFactory.Config> {

    public PageRoutePredicateFactory(a) {
        super(PageRoutePredicateFactory.Config.class);
    }

    @Override
    public List<String> shortcutFieldOrder(a) {
        // The sequence of parameters must be the same as that in the configuration file
        System.out.println("Arrays::="+Arrays.asList("minPage"."maxPage").toString());
        return Arrays.asList("minPage"."maxPage");
    }

    @Override
    public Predicate<ServerWebExchange> apply(PageRoutePredicateFactory.Config config) {
        return new Predicate<ServerWebExchange>() {
            @Override
            public boolean test(ServerWebExchange serverWebExchange) {
                String page=serverWebExchange.getRequest().getQueryParams().getFirst("Page");
                System.out.println("page:"+page);
                System.out.println("Bool"+page! =null||"".equals(page.trim()));
                System.out.println("\"\".equals(page.trim())::"+!"".equals(page.trim()));
                
                if(page! =null&&!"".equals(page.trim())) {
                    int pageInt= Integer.parseInt(page);
                    System.out.println("pageInt:"+pageInt);
                    return pageInt > config.getMinPage() && pageInt < config.getMaxPage();
                  
                }
                return true; }}; }static class Config {// Must be a static inner class
        private int minPage;
        private int maxPage;

        public int getMinPage(a) {
            return minPage;
        }

        public void setMinPage(int minPage) {
            this.minPage = minPage;
        }

        public int getMaxPage(a) {
            return maxPage;
        }

        public void setMaxPage(int maxPage) {
            this.maxPage = maxPage; }}}Copy the code

(2) Modify the configuration file

Add the Page assertion setting bootstrap.yml

server:
  port: 9000
spring:
  application:
    name: sca-gateway
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 Service discovery, while the current service is registered with NACOS
    gateway:
      discovery:
        locator:
          enabled: true
      routes:
        - id: route01
          ##uri: http://localhost:8081/
          uri: lb://nacos-provider # lb is a service prefix and cannot be written arbitrarily
          predicates: ### Match rules
            - Path=/nacos/provider/echo/**
            - Before = 2021-06-30 T00:00:00) 000 + 08:00
            - Method=GET
            - Page = 10, 20
          filters:
            - StripPrefix=1 Remove layer 1 paths from path, such as nacos, before forwarding

logging:
  level:
    org.springframework.cloud.gateway: debug
Copy the code

(3) Restart the Gateway service

Restart the Gateway service, enter the URL and parameters in the browser, and check the output:

http://localhost:9000/nacos/provider/echo/hello?Page=15
Copy the code

Filter enhancement analysis

A Filter is a process of request and response in the process of request transmission.

Filter Life cycle

In Gateway, the Filter has only two life cycles: “Pre” and “Post.” As shown in the figure:Among them:

1) PRE: this filter is invoked before the request is routed. Based on this filter, authentication can be realized, requested micro-services can be selected in the cluster, debugging information can be recorded, etc.

2) POST: This filter is executed after routing to the microservice. Such filters can be used to add standard HTTP headers to responses, collect statistics and metrics, send responses from microservices to clients, and so on.

Local filter analysis

The Filter of Gateway can be divided into two types: GatewayFilter and GlobalFilter. Among them:

  1. GatewayFilter: Applies to a single route or a group of routes.

  2. GlobalFilter: Applies to all routes.

There are many different types of Gateway routing filters built into the SpringCloud Gateway. Details are as follows: Case Study:

  1. Based on the AddRequestHeaderGatewayFilterFactory, add the Header to the original request. For example, add a name to the original requestX-Request-FooAnd has a value ofBarRequest header:
spring:
  cloud:
    gateway:
      routes:
        - id: add_request_header_route
          uri: https://example.org
          filters:
            - AddRequestHeader=X-Request-Foo, Bar
Copy the code

2) based on AddRequestParameterGatewayFilterFactory, add request parameters and values to the original request, for example, as the original request to add called foo, values for the parameters of the bar, namely: foo = bar.

spring:
  cloud:
    gateway:
      routes:
        - id: add_request_parameter_route
          uri: https://example.org
          filters:
            - AddRequestParameter=foo, bar
Copy the code

3) based on PrefixPathGatewayFilterFactory, add a prefix to the request of the original path path, for example, the configuration to access ${GATEWAY_URL} / hello will be forwarded to the uri/mypath/hello.

spring:
  cloud:
    gateway:
      routes:
        - id: prefixpath_route
          uri: https://example.org
          filters:
            - PrefixPath=/mypath
Copy the code

4) based on RequestRateLimiterGatewayFilterFactory, realization of current limiting operation, algorithm for token bucket algorithm, the configuration is as follows:

spring:
  cloud:
    gateway:
      routes:
        - id: requestratelimiter_route
          uri: https://example.org
          filters:
            - name: RequestRateLimiter
              args:
                redis-rate-limiter.replenishRate: 10
                redis-rate-limiter.burstCapacity: 20
Copy the code

5) based on RequestSizeGatewayFilterFactory, setting allows to receive maximum request packet size, configuration example:

spring:
  cloud:
    gateway:
      routes:
        - id: request_size_route
      uri: http://localhost:8080/upload
      predicates:
        - Path=/upload
      filters:
        - name: RequestSize
          args:
            # in bytes
            maxSize: 5000000
Copy the code

If the size of the request packet exceeds the set value, 413 Payload Too Large and an errorMessage are returned

Global filters and customizations

A GlobalFilter applies to all routes and does not need to be configured. Loaded during system initialization and applied to each route. Global filters can be used to verify permissions and security. Common global filters are shown below:The built-in filter has been able to complete most of the functions, but for some business function processing developed by the enterprise, or we need to write our own filter to achieve, so we together through the form of code to define a filter, to complete the unified permission verification. For example, when a client requests services for the first time, the server authenticates the user information (login), encrypts the user information to form a token, and sends the token to the client as a login credential. Each subsequent request, the client carries the authenticated token and the server decrypts the token to determine whether the token is valid.

package com.cy.filters;

import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Component
public class AuthGlobalFilter implements GlobalFilter.Ordered {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String token=exchange.getRequest().getQueryParams().getFirst("token");
        if(!"admin".equals(token)){
            System.out.println("Authentication failed");
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }

    @Override
    public int getOrder(a) {
        return 0; }}Copy the code

If the url accessed does not contain the parameter “token=admin”, an exception may occur, as shown in the figure:

Current limiting design and implementation

The gateway is the common entrance of all requests, so traffic limiting can be carried out at the gateway, and there are many ways to limit traffic. Sentinel component is used to implement traffic limiting on the gateway. Sentinel supports traffic limiting for mainstream gateways such as SpringCloud Gateway and Zuul. Please refer to the following website:

https://github.com/alibaba/spring-cloud-alibaba/wiki/Sentinel
Copy the code

Traffic limiting Quick start

(1) Add dependencies

Add the following two dependencies to the original SCA-Spring cloud-Gateway dependency, for example:

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
Copy the code

(2) Add sentinel and rules

Add sentinel and routing rules (no need to set them if they already exist)

In the same directory as routes, configure sentinel and add http://localhost to the address

routes:
  - id: route01
    uri: lb://nacos-provider
    predicates: ### Match rules
      - Path=/provider/echo/**
sentinel:
  transport:
    dashboard: http://localhost:8180 #Sentinel console address
    port: 8719 The client monitors the API port
  eager: true  # Cancel Sentinel console lazy loading, i.e. connect when project starts
Copy the code

All Configuration contents

server:
  port: 9000
spring:
  application:
    name: sca-gateway
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848 Service discovery, while the current service is registered with NACOS
    gateway:
      discovery:
        locator:
          enabled: true # Enable the feature to find service instances by service name (ID)
      routes:
        - id: route01 # optionally specifies a value that uniquely identifies the route
          ##uri: http://localhost:8081/ # Address of a service
          uri: lb://nacos-provider
          predicates: ### Predicate object (define access rules)
            - Path=/nacos/provider/echo/**
            - Before = 2021-06-30 T00:00:00) 000 + 08:00
            - Method=GET
            #- Query=name,tony

          filters:  ## Gateway handles all requests as an entry point, implemented underneath by a filter
            - StripPrefix=1 # remove the first part of the path section of the URL (the first two "/" and the content between)
            #- AddRequestHeader=tedu, CGB
            #- AddRequestParameter=page, 10

    sentinel:  The gateway element contains sentinel, and the gateway element contains sentinel.
       eager: true Register at startup
       transport:
         dashboard: http://localhost:8180  # sentinel console
         port: 8719 # Sentinel client port
Copy the code

(3) Start the gateway project

Detect the Gateway menu of the Sentinel console.

At startup, add the JVM parameters for Sentinel. Using this menu, the Gateway service can display a different menu in the Sentinel console (close the gateway project if no changes are found, close sentinel, then restart Sentinel, restart the gateway project). The code is as follows.

-Dcsp.sentinel.app.type=1
Copy the code

If it is in IDEA, you can configure it according to the following figure

After Sentinel console is started, the interface is as shown in the figure:

(4) Set the traffic limiting policy

Set the traffic limiting policy in the Sentinel panel, as shown in the figure:

(5) Realize current limiting operation

The native interfaceCustom display JSON string

Create a Config config folder and create the GatewayConfig class.

package com.cy.config;

import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.server.ServerResponse;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.HashMap;
import java.util.Map;

@Configuration
public class GatewayConfig {

    public GatewayConfig(a){
        GatewayCallbackManager.setBlockHandler(new BlockRequestHandler() {
            /** This method is used when limiting traffic occurs */
            @Override
            public Mono<ServerResponse> handleRequest( ServerWebExchange serverWebExchange, Throwable throwable) {
                Map<String,Object> map=new HashMap<>();
                map.put("code".429);
                map.put("message"."request is blocked");
                try {
                    //jackson
                    String str=new ObjectMapper().writeValueAsString(map);
                    return ServerResponse.ok().body(Mono.just(str), String.class);
                } catch (JsonProcessingException e) {
                    e.printStackTrace();
                    throw newRuntimeException(e); }}}); }}Copy the code

Display user-defined traffic limiting content

Traffic limiting based on request attributes

Define the attribute-based traffic limiting policy with the specified routeId as shown in the figure below:Postman was used for test analysis

Custom API dimension traffic limiting

Custom API grouping, a more fine-grained definition of traffic limiting rules, allows us to take advantage of the API provided by Sentinel to group request paths and then set traffic limiting rules on the groups.

(1) Create an API group

(2) Create a grouping process

(3) Access test

Conduct an access test, as shown in the figure

http://localhost:9000/nacos/provider/echo/a1
Copy the code

http://localhost:9000/nacos/provider/echo/a2
Copy the code