Teach you how to build a new generation of service Gateway for spring cloud project integration Gateway

Posted by kavitam on Tue, 04 Jan 2022 03:14:26 +0100

Introduction to Gateway

What is an API gateway?

As an API architecture, it is used to protect, enhance and control access to API services. An API gateway is an application or service (providing REST API interface services) the previous system is used to manage authorization, access control and traffic restrictions, so that the REST API interface services are protected by the API gateway and transparent to all callers. Therefore, the business system hidden behind the API gateway can focus on creating and managing services without dealing with these strategic infrastructure.

What are the functions of API gateways?

What are the classifications and functions of API gateways?

What is Gateway

Spring Cloud Gateway is a gateway officially developed by spring based on Spring 5.0, Spring Boot 2.0 and Project Reactor. Spring Cloud Gateway aims to provide a simple and effective unified API routing management method for microservice architecture. As a gateway in the Spring Cloud ecosystem, Spring Cloud Gateway aims to replace ZUUL. It not only provides a unified routing method, but also provides the basic functions of the gateway based on the Filter chain, such as security, monitoring / embedding point, and current limiting.

Why Gateway

Spring Cloud Gateway can be regarded as a zuul 1 The upgraded version and substitute of X uses Netty to realize asynchronous IO earlier than Zuul 2, so as to realize a simple and better implementation than zuul 1 X is a more efficient API gateway that works closely with Spring Cloud.
Spring Cloud Gateway clearly distinguishes between Router and Filter, and a great feature is that it has built-in many out of the box functions, which can be used through SpringBoot configuration or manual coding chain call.
For example, there are 10 built-in routers, so that we can directly configure them and route according to Header, Path, Host or Query as we like.
For example, it distinguishes between general Filter and global Filter, and has built-in 20 kinds of filters and 9 kinds of global filters, which can also be used directly. Of course, it is also very convenient to customize the Filter.

The most important concepts

  • Route: This is the basic building block of the gateway. It is defined by an ID, a URI, a set of assertions and a set of filters. If the assertion is true, the route matches.

  • Predicate: the input class is a ServerWebExchange. We can use it to match anything from an HTTP request, such as headers. If the request matches the assertion, it will be routed.

  • Filter: an instance of GatewayFilter in the Spring framework. Using the filter, you can modify the request before or after it is routed.

Workflow

The client sends a request to the Spring Cloud Gateway, then finds the route matching the request in the gateway handler mapping and sends it to the Gateway Web Handler.

The Handler sends the request to our actual service business logic through the specified filter chain, and then returns. The dotted line is used between filters because the filter may execute business logic between ("pre") or after ("post") sending proxy requests.

Filter in the "pre" type of filter, you can perform parameter verification, permission verification, traffic monitoring, protocol conversion, etc. In the "post" type filter, you can modify the response content and response header, log output, traffic monitoring and so on.

The main core is route forwarding + execution filter chain

Integrated Gateway getting started configuration

1. Create module: springcloud gateway gateway 9527

2.pom.xml

The gateway dependency is mainly added. Mining tips: do not introduce web dependency, otherwise an error will be reported and the project will not start. As follows:

<?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">
    <parent>
        <artifactId>springcloud</artifactId>
        <groupId>com.dyh.springcloud</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>springcloud-gateway-gateway9527</artifactId>
    <dependencies>
        <!--gateway-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <!-- eureka-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <!--  General package-->
        <dependency>
            <groupId>com.dyh.springcloud</groupId>
            <artifactId>springcloud-api-commons</artifactId>
            <version>${project.version}</version>
        </dependency>

        <!--monitor-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

    </dependencies>

</project>

3.application.yml

server:
  port: 9527

spring:
  application:
    name: springcloud-gateway
eureka:
  instance:
    hostname: springcloud-gateway-service
  client: #The service provider is registered in the eureka service list
    service-url:
      register-with-eureka: true
      fetch-registry: true
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/

4. Main startup class GatewayMain

package com.dyh.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

/**
 * @ClassName: GatewayMain
 * @author: dyh
 * @since: 2021/12/24 18:19
 */

@SpringBootApplication
@EnableEurekaClient
public class GatewayMain {
    public static void main(String[] args) {
        SpringApplication.run(GatewayMain.class, args);
    }
}

6. Test

Then start Eureka 7001, Eureka 7002 services and the Gateway service of our new Gateway to see if the Gateway service is successfully registered in Eureka registry?

You can see that the Gateway successfully registered the registry. How does the Gateway map? Let's take the springcloud provider payment8001 producer service as an example to map it. Open the paymentcontroller control layer and demonstrate the get method, because we accessed it before http://localhost:8001/payment/get/1 However, when we visit, we expose our port 8001 to the outside, which is unsafe. However, we do not want to expose the real port number of 8001. We hope to add a layer of our Gateway port 9527 on the outside package of 8001. Therefore, we need to configure a new routing configuration in yml. As follows:

server:
  port: 9527

spring:
  application:
    name: springcloud-gateway
  cloud:
    gateway:
      routes:
        - id: payment_route # The id of the route. There are no rules, but it is required to be unique. It is recommended to cooperate with the service name
          uri: http://localhost:8001
          predicates:
            - Path=/payment/get/** # Assert that the path matches the route

eureka:
  instance:
    hostname: springcloud-gateway-service
  client: #The service provider is registered in the eureka service list
    service-url:
      register-with-eureka: true
      fetch-registry: true
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/

Then we visit http://localhost:9527/payment/get/1 , it can be accessed successfully, indicating that the routing and assertion of our gateway are configured successfully! As shown below:

We configure YML now, and another configuration scheme is through hard coding. The Bean of RouteLocator is injected into the code to solve the problem of too many YML file configurations and too large files. Then start rolling up! We just need to demonstrate that we can access the Baidu news website of the Internet through the 9527 gateway.

Create a new config configuration file as follows:

package com.dyh.springcloud.gateway.config;

import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * A routing rule with id routr name is configured
 * When accessing address http://localhost:9527/guonei Automatically forwarded to http://news.baidu.com/guonei
 */
@Configuration
public class GateWayConfig {
    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder routeLocatorBuilder) {
        RouteLocatorBuilder.Builder routes = routeLocatorBuilder.routes();
        routes.route("patn_route_buba", r -> r.path("/guonei").uri("http://news.baidu.com/guonei")).build();
        return routes.build();
    }
}

After configuration, restart the service and we will visit http://localhost:9527/guonei , it can be successfully forwarded to the following interface, which shows that our GateWay performs routing mapping configuration through coding.

Integrating Gateway to realize dynamic routing

Look at our YML configuration file. What we configure is http://localhost:8001 It is dead, but in our microservices, the producer service cannot have a machine, so it must be configured for load balancing.
This dependency is required for load balancing - subsequent yml configurations will start with lb (dynamic routing protocol)


By default, the Gateway will create a dynamic route with the micro service name on the registry as the path according to the service list registered in the registry for forwarding, so as to realize the function of dynamic routing. Then we need to modify the Gateway's YML configuration file, enable the function of dynamically creating routes from the registry, use the microservice name for routing, and modify the route address providing the service to the producer's service name after matching. The specific configuration is as follows:

server:
  port: 9527
spring:
  application:
    name: springcloud-gateway
  cloud:
    gateway:
        # The locator needs to be opened, otherwise through lb: / / Mode request not found
      locator:
        enabled: true # Enable the function of dynamically creating routes from the registry, and use the microservice name for routing
      routes:
        - id: payment_route # The id of the route. There are no rules, but it is required to be unique. It is recommended to cooperate with the service name
          #The routing address of the service provided after matching
          #uri: http://localhost:8001
          #lb is a dynamic routing protocol, and the following springgroup-payment-service is the name of the service to jump.
          uri: lb://SPRINGCLOUD-PAYMENT-SERVICE
          predicates:
            - Path=/payment/get/** # Assert that the path matches the route
eureka:
  instance:
    hostname: springcloud-gateway-service
  client: #The service provider is registered in the eureka service list
    service-url:
      register-with-eureka: true
      fetch-registry: true
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/

In order to test load balancing, we need to start Eureka 7001, Eureka 7002, producer services 8001 and 8002, and gateway 9527. Remember to add the get method of the return port to the 8002 service. visit http://localhost:9527/payment/get/1 Load balancing is successfully realized. The tests are as follows:


PS:
Error report picture:

There is a problem here. lb when looking for the instance under the service name springcluster-payment-service in eureka, I 8001 did not display the ip address. Please visit http://localhost:9527/payment/get/1 It keeps reporting errors, and 8002 is displayed. Visit http://localhost:9527/payment/get/1 It succeeds (polling is the default, 8002 succeeds and 8001 reports an error)

It can be seen from this that it is found that the 80018002 main startup class is annotated with @ EnableDiscoveryClient, and the 80018002yml file is annotated with preferred IP address: true. The IP address can be displayed in the # access path. If you want to know more about it, you can read the previous notes Teach you how to build the spring cloud project (VII) Eureka implements service discovery and Teach you how to build the spring cloud project (VI) actor micro service information improvement and Eureka self-protection mechanism

In this way, I finally successfully accessed 80018002, realized load balancing, and enjoyed myself.....

Common Predicate assertions

When we start our springcloud-gateway-gateway9527 service, we can see that the console has the following interface:

Spring Cloud Gateway takes Route matching as a part of the basic framework of Spring WebFlux HandlerMapping. It contains many built-in routepredictefactories, all of which match different attributes of HTTP requests. Multiple Route predict factories are combined. When Spring Cloud Gateway creates a Route object, it uses routepredictefactory to create a Predicate object, which can be assigned to Route. Spring Cloud Gateway contains many built-in Route Predicate factors. All of these predicates match different attributes of HTTP. Multiple Predicate factories can be combined and through logical and.
Then we can also see the routing configuration in our YML configuration file. We use path, that is, the requested path matches the configuration value. As shown in the following figure:

Our common routing configuration is to implement a set of matching rules so that the request can find the corresponding Route for processing. As shown below:

Let's take After as an example. Others are similar. Let's first see how the official website is configured, as shown in the following figure:

We can see that the red area is a series of time. The official website uses foreign time. How can we use the current time? Just use the following class to obtain our current domestic time. The default is Shanghai. As follows:

package com.dyh.springcloud.test;

import java.time.ZonedDateTime;

/**
 * @ClassName: Test
 * @author: dyh
 * @since: 2021/12/26 17:04
 */
public class Test {
    public static void main(String[] args) {
        ZonedDateTime time =  ZonedDateTime.now();//Use default time
        System.out.println(time);
        //2021-12-26T17:06:46.141+08:00[Asia/Shanghai]
    }

}

After obtaining the time, we configure the YML file. We only configure the payment/get method, as follows:

server:
  port: 9527
spring:
  application:
    name: springcloud-gateway
  cloud:
    gateway:
        # The locator needs to be opened, otherwise through lb: / / Mode request not found
      locator:
        enabled: true # Enable the function of dynamically creating routes from the registry, and use the microservice name for routing
      routes:
        - id: payment_route # The id of the route. There are no rules, but it is required to be unique. It is recommended to cooperate with the service name
          #The routing address of the service provided after matching
          #uri: http://localhost:8001
          #lb is a dynamic routing protocol, and the following springgroup-payment-service is the name of the service to jump.
          uri: lb://SPRINGCLOUD-PAYMENT-SERVICE
          predicates:
            - Path=/payment/get/** # Assert that the path matches the route
            - After=2021-12-26T17:07:22.955+08:00[Asia/Shanghai] #The assertion cannot be accessed until after the current time
eureka:
  instance:
    hostname: springcloud-gateway-service
  client: #The service provider is registered in the eureka service list
    service-url:
      register-with-eureka: true
      fetch-registry: true
      defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/

Let's first test whether the access is successful, because the time now must be later than the configured time, as shown in the following figure:

Let's modify the time, postpone the time by one day, and then test the method of payment/get, as shown in the following figure:

You can see that After configuration, you can't access it. For example, After, we can use it when we launch new functions. For example, your company goes online at 10 p.m., you can go online in advance, and then wait until the set time to take effect. 11 kinds of assertions can be configured according to your own needs.

Use of Filter

Filter is one of the three cores of gateway. The routing filter can be used to modify incoming HTTP requests and returned HTTP responses. The routing filter can only be used by specifying routes. Gateway has built-in multiple routing filters, which are generated by the GatewayFilter project class.

Function of Filter

When we have many services, such as user service, goods service and sales service in the figure below, when the client requests the Api of each service, each service needs to do the same things, such as authentication, flow restriction, log output, etc.

For such repetitive work, is there any way to do better? The answer is yes. Add an Api Gateway service with global permission control, flow restriction and log output on the upper layer of the micro service, and then forward the request to the specific business service layer. This Api Gateway service serves as a service boundary. External requests to access the system must first pass through the gateway layer.

Filter lifecycle

Similar to zuul, Spring Cloud Gateway has only pre and post life cycles. Before routing, it needs to be processed by a "pre" filter. After processing the returned response, it can be processed by a "post" filter. Parameter verification, permission verification, traffic monitoring, log output, protocol conversion, etc. can be done in the "pre" type filter. In the "post" type filter, you can modify the response content and response header, log output, traffic monitoring, etc.

For example, the user service in the figure above, after receiving the response from the business service, is processed by a "post" type filter, and finally returns the response to the client.

Different from zuul, in addition to the "pre" and "post" filters, in Spring Cloud Gateway, the filter can be divided into two other types from the scope of action. One is the gateway filter for a single route, and its writing in the configuration file is similar to predict; The other is the global gateway filer for all routes. Now we will explain these two filters from the dimension of scope division.

The Filter types of Spring Cloud Gateway are GatewayFilter (single) and GlobalFilter (global)

Spring Cloud Gateway is divided into GatewayFilter and GlobalFilter according to the scope of action. The differences between the two are as follows:

  • GatewayFilter: need to pass spring cloud. routes. Filters are configured under specific routes and only work on the current route or through spring cloud. Default filters are configured globally and act on all routes.

  • GlobalFilter: a global filter, which does not need to be configured in the configuration file and acts on all routes. It is finally packaged into a filter recognizable by GatewayFilterChain through the GatewayFilterAdapter. It is a 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 configured. It is loaded during system initialization, And act on each route.

Introduction to GatewayFilter

The gateway filter factory is similar to the Predicate factory described in the previous article, and is in the configuration file application The configuration in YML follows the idea that the Convention is greater than the configuration. Only the name of the GatewayFilter Factory needs to be configured in the configuration file instead of writing all the class names. For example, AddRequestHeaderGatewayFilterFactory only needs to write AddRequestHeader in the configuration file instead of all the class names. The GatewayFilter Factory configured in the configuration file will eventually be processed by the corresponding filter factory class.

The list of built-in filter factories of Spring Cloud Gateway is as follows:

Each filter factory gives detailed use cases in the official documents. There are 30 versions now, Click to view the official website , if you don't know, you can also go to org springframework. cloud. gateway. filter. Factory look at the source code of each filter factory. In our actual development process, the general GatewayFilter cannot be satisfied, so we need to customize the filter.
Let's start defining the filter ourselves!

It mainly implements the two interfaces globalfilter and ordered, and then implements the specific business logic, as follows:

package com.dyh.springcloud.gateway.filter;

import lombok.extern.slf4j.Slf4j;
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;

import java.util.Date;

/**
 * @ClassName: MyLogGatewatFilter
 * @author: dyh
 * @since: 2021/12/26 17:19
 */

@Component
@Slf4j
public class MyLogGatewatFilter implements GlobalFilter, Ordered {
    //You need a specified user name to access me,
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("************come in MyLogGatewatFilter " + new Date());
        //Determine whether to carry the uname key
        String uname = exchange.getRequest().getQueryParams().getFirst("uname");
        //Illegal user, please leave
        if (uname == null) {
            log.info("************User name is null,illegal user");
            exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
            return exchange.getResponse().setComplete();
        }
        //The legitimate user performs the next filter chain for filter verification
        return chain.filter(exchange);
    }

    //This 0 number represents the order in which the filter is loaded. The smaller the number, the higher the priority. Because it is global, it must be the first.
    @Override
    public int getOrder() {
        return 0;
    }
}

The filter mainly refers to the path to be accessed. It needs to carry the parameter uname. If it is not carried, it will verify that it is illegal, report an error and cannot be accessed. Only when the document is written and legal can the access be successful. We can access it http://localhost:9527/payment/get/1?uname=zs The test is successful, as shown in the figure below:

Then access without parameters fails, as shown in the following figure:

Introduction to GlobalFilter

The GlobalFilter built in the Spring Cloud Gateway framework is as follows:

In the figure above, each GlobalFilter acts on each router and can meet most requirements. However, if you encounter business customization, you may need to write a GlobalFilter to meet your needs. The following case will describe how to write your own GlobalFilter, which will verify whether the request contains the request parameter "token", and how to not forward the route if it does not contain the request parameter "token", otherwise normal logic will be executed. The code is as follows:

 
package com.dyh.springcloud.gateway.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.http.HttpStatus;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
 * @ClassName: TokenFilter
 * @author: dyh
 * @since: 2021/12/26 17:25
 */
public class TokenFilter implements GlobalFilter, Ordered {

    Logger logger= LoggerFactory.getLogger( TokenFilter.class );
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String token = exchange.getRequest().getQueryParams().getFirst("token");
        if (token == null || token.isEmpty()) {
            logger.info( "token is empty..." );
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }
        return chain.filter(exchange);
    }

    @Override
    public int getOrder() {
        return -100;
    }
}

 

The TokenFilter above needs to implement GlobalFilter and Ordered interfaces, which is very similar to the implementation of GatewayFilter. Then obtain the ServerHttpRequest according to ServerWebExchange, and then complete the request according to whether the ServerHttpRequest contains the parameter token. If not, terminate the forwarding. Otherwise, execute the normal logic.
Then, you need to inject TokenFilter into the Spring Ioc container in the project startup class. The code is as follows:

package com.dyh.springcloud.gateway.config;

import com.dyh.springcloud.gateway.filter.TokenFilter;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class GateWayConfig {

    @Bean
    public TokenFilter tokenFilter(){
        return new TokenFilter();
    }

}

Start the project, visit http://localhost:9527/payment/get/1 , as shown below:

You can see that the request was terminated without being forwarded, and the following log was printed on the console:

The above log shows that the request entered the logic without "token".

Our Spring Cloud Gateway has finished learning here, so easy!

In the next article, we will learn about the Spring Cloud Config distributed configuration center. The article is continuously updated. Welcome to pay attention!

Topics: Java Spring Cloud gateway