As an alternative to Netflix Zuul, Spring Cloud Gateway is a very practical microservice Gateway, which plays a very important role in Spring Cloud microservice architecture. This article reviews the common usage scenarios of the Spring Cloud Gateway, hoping to provide some help to microservice developers.


Microservices gateway SpringCloudGateway



1. An overview of the

The Spring Cloud Gateway is an official gateway developed by Spring based on Spring 5.0, Spring Boot2.0 and Project Reactor technologies. Spring Cloud Gateway aims to provide simple, effective and unified API route management for microservice architecture. As the Gateway in the Spring Cloud ecosystem, Spring Cloud Gateway aims to replace Netflix Zuul. It not only provides a unified routing mode, but also provides basic gateway functions based on Filer chain, such as security, monitoring/burying point, and traffic limiting.


2. Core concepts

The gateway provides full HOSTING API services and provides rich API management functions to help enterprises manage large-scale apis to reduce management costs and security risks, including protocol adaptation, protocol forwarding, security policies, brush prevention, traffic, and log monitoring. Generally speaking, the URL or interface information exposed by a gateway is called routing information. Anyone who has developed gateway middleware or used Zuul will know that the core of a gateway is the Filter and the Filter Chain. The Sprig Cloud Gateway also has the concepts of routing and filters. Here are some important concepts in Spring Cloud Gateway.


  • Routing. A route is the most basic part of a gateway. The route information consists of an ID, a destination URL, a set of assertions, and a set of filters. If the assertion route is true, the requested URL matches the configuration

  • Assertions. Assertion functions in Java8. The assertion function input type in the Spring Cloud Gateway is ServerWebExchange in the Spring5.0 framework. The assertion function in the Spring Cloud Gateway allows developers to define and match any information from an HTTP request, such as request headers and parameters.

  • The filter. A standard Spring webFilter. The filters in Spring Cloud Gateway are divided into two types: Gateway Filter and Global filter. The Filter modifies the request and response



As shown in the figure above, the Spring cloudGateway makes the request. The route matching the request is then found in the Gateway Handler Mapping and sent to the Gateway Web Handler. The Handler then sends the request through the specified filter chain to our actual service to perform the business logic, and then returns.


Quick start

Take Spring Boot framework development as an example, start a Gateway service module (Consul as the registry) and a back-end service module. Client requests are routed through the Gateway service to the back-end service.


Prerequisites:

  • Consul: Version 1.5.0.

  • Spring Bot: Version 2.1.5.

  • Spring Cloud: Version Greenwich.SR1.

  • Redis: version 5.0.5.



1. Microservice development

Here is an example of developing microservices using the Spring Boot framework, starting a service and registering with Consul.


Introducing dependencies:

<dependency>    <groupId>org.springframework.cloud</groupId>    <artifactId>spring-cloud-starter-consul-discovery</artifactId></dependency>Copy the code


To register the service into Consul, configure the configuration file as follows:

Spring: Application: name: service-consumer Cloud: Consul: host: 127.0.0.1 port: 8500 Discovery: service-name: service-consumerCopy the code


Define RestController as follows to publish the HTTP interface.

@RestController@RequestMapping("/user")public class UserController {    @Resource    private UserService userService;    @GetMapping(value = "/info")    public User info() {        return userService.info();    }}Copy the code

Note: This is a server configuration to which the request is routed through the Gateway.



2. Gateway configuration Create a Gateway service with the following dependencies:

<dependency>    <groupId>org.springframework.cloud</groupId>    <artifactId>spring-cloud-starter-gateway</artifactId></dependency><dependency>    <groupId>org.springframework.cloud</groupId>    <artifactId>spring-cloud-starter-consul-discovery</artifactId></dependency>Copy the code


The startup class configuration is as follows:

@SpringBootApplication@EnableDiscoveryClientpublic class GatewayApplication {    public static void main(String[] args) {        SpringApplication.run(GatewayApplication.class, args);    }}Copy the code


The Spring Cloud Gateway provides routing functions for client requests. The main configurations are as follows:

server:  port: 8098spring:  application:    name: service-gateway  cloud:    gateway:      discovery:        locator:          enabled: true             lower-case-service-id: trueConsul: host: 127.0.0.1Gateway gateway to Consul port: 8500 Discovery: service-name: service-gatewayCopy the code


Now use http://localhost:8089/service-consumer/user/info to access the service, the gateway can be routed to the service forward, put forward the request to the specific back-end services. In this case, the URL prefix service-consumer is a string after the service name of the back-end service registered in Consul is converted to lowercase letters.


Best practices

01

Gateway Gateway configuration



The configuration of gateway for routing and forwarding is defined in the development specification in the second part of this paper. In addition to the above configuration, the following configuration methods can also be used:


gateway:      discovery:        locator:          enabled: true          lower-case-service-id: true      routes:      - id: service_consumer        uri: lb://service-consumer        predicates:        - Path= /consumer/**        filters:        - StripPrefix=1Copy the code


The Path predicAT is configured to forward all requests starting with /consumer/** to the lb://service-consumer uri. Lb ://service-consumer (the name of the service in the registry) is the load-balancing address of the service-consumer service, and the StripPrefix filter is used to remove /consumer before forwarding. At the same time will spring. Cloud. Gateway. Discovery. Locator. Enabled to false, if you don’t change, Before http://localhost:8081/service-consumer/user/info address of such a request can also be normal visit, when founded two router for each service.

Part 2 and this section describe two configuration modes, both of which can implement the routing and forwarding function of requests. Parameters of the spring. Cloud. Gateway. Discovery. The locator, enabled to true, indicates that the function of the open gateway service registration and discovery, And the Spring Cloud Gateway automatically creates a router for each service based on service discovery, which forwards the request path starting with the service name to the corresponding service. Spring. Cloud. Gateway. Discovery. A locator. LowerCaseServiceId is configured to service the request path name lowercase (because the service registry, to the registry when the service name to uppercase).


gateway:      discovery:        locator:          enabled: true          lower-case-service-id: trueCopy the code


02

Gateway Cross-domain access

Spring Cloud Gateway is also designed for cross-domain access, which can be addressed using the following configuration:

spring:  cloud:    gateway:      globalcors:        corsConfigurations:          '[/ * *]':            allowedOrigins: "https://docs.spring.io"            allowedMethods:            - GET            allowHeaders:            - Content-TypeCopy the code


In the example above, get requests from https://docs.spring.io are allowed to access and the server is allowed to carry the content-Type field in the request header.


03

Gateway filter

Spring Cloud Gateway’s filter lifecycle is not as rich as Zuul’s, which has only two: “Pre” and “post” :

  • Pre: This filter is invoked before the request is routed. You can use this filter to authenticate, select requested microservices in the cluster, and log debugging information.

  • Post: This filter is executed after routing to the server. This filter can be used to add HTTP headers, statistics and metrics to the response, send the response from the microservice to the client, and so on.


Spring Cloud Gateway filters are divided into two types: GatewayFilter and Globalfilter. GlobalFilter is applied to all routes, while Gatewayfilter is applied to a single route or a group of routes.

Gatewayfilter allows you to modify the REQUESTED HTTP request or response, or to make special restrictions based on the request or response. More often, Gatewayfilter can be used to do some specific routing configuration.

The following configuration is related to the AddRequestParameter Gatewayfilter configuration.


spring:  application:    name: service-gateway  cloud:    gateway:     discovery:        locator:         enabled: true     routes:     - id: parameter_route      uri: http://localhost:8504/user/info      filters:      - AddRequestParameter=foo, bar      predicates:      - Method=GETCopy the code


In the preceding configuration, the forwarding address is specified. All GET methods are automatically configured with foo=bar. When the request meets the preceding routing conditions, the parameters added by the Gateway Gateway can be received on the back-end service.

Another common filter, StripPrefix Gateway filter, is introduced.

The configuration is as follows:

spring:  cloud:    gateway:      routes:      - id: stripprefixfilter        uri: lb://service-consumer        predicates:        - Path=/consumer/**        filters:        - StripPrefix=1Copy the code


When the client side using the http://localhost:8098/consumer/user/info path for the request, If according to the configurations of the Gateway will transform the request for http://localhost:8098/service-consumer/user/info. This serves as the final destination of the front-end request.


04

Gateway request Matching

Gateway The Gateway can match requests to different back-end services in different ways.

The header is used to match requests to different services. The configuration is as follows:

spring:  cloud:    gateway:      routes:      - id: header_route        uri: http://baidu.com        predicates:        - Header=X-Request-Id, \d+Copy the code


Run the curl test: curl http://localhost:8080 -h “x-request-id :666666”.


If Host is used to match, the configuration is as follows:

spring:  cloud:    gateway:      routes:      - id: host_route        uri: http://baidu.com        predicates:        - Host=**.baidu.comCopy the code


Curl http://localhost:8098 -h “Host: www.baidu.com”

Routes can be routed by POST, GET, PUT, or DELTE:

spring:  cloud:    gateway:      routes:      - id: method_route        uri: http://baidu.com        predicates:        - Method=GETCopy the code


Test with curl http://localhost:8098 and return the page code as success.

The preceding is a single matching route. If multiple routes are combined for routing, route forwarding is performed only when all routes meet the conditions.


05

Gateway fusing

The Spring Cloud Gateway can also take advantage of Hystrix’s circuit-breaker features to degrade services in the event of heavy traffic, and Hystrix dependencies must be added to the project.

<dependency>  <groupId>org.springframework.cloud</groupId>  <artifactId>spring-cloud-starter-netflix-hystrix</artifactId></dependency>    Copy the code


After configuration, the Gateway uses fallbackCMD as the name to generate a HystrixCommand object for fusing. If you want to add the callback after fusing, you need to add the following configuration:

spring:  cloud:    gateway:      routes:      - id: hystrix_route        uri: lb://consumer-service        predicates:        - Path=/consumer/**        filters:        - name: Hystrix          args:            name: fallbackcmd            fallbackUri: forward:/fallback        - StripPrefix=1hystrix:    command:    fallbackcmd:      execution:        isolation:          thread:            timeoutInMilliseconds: 5000 # Timeout period. If the timeout period is not set, fuses may not be triggeredCopy the code


In the preceding configuration, the return path after a fusing is given. Therefore, add /fallback to the Gateway service module as the return path when a service fusing occurs.

@RestControllerpublic class GatewayController {    @RequestMapping(value = "/fallback")    public String fallback() {return "fallback nothing";    }}Copy the code


FallbackUri: Forward :/fallback Specifies the path to call fallback. When the Hystrix fallback is called, the request is forwarded to /fallback and the return value of this path is returned as the result.


06

Gateway Retry router

With simple configuration, the Spring Cloud Gateway can support request retry capabilities.

spring:  cloud:    gateway:      routes:      - id: header_route        uri: http://localhost:8504/user/info        predicates:        - Path=/user/**        filters:        - name: Retry          args:            retries: 3            status: 503        - StripPrefix=1Copy the code


The Retry GatewayFilter controls the Retry mechanism using the following parameters:

  • Retries: indicates the number of retries. The default value is three.

  • State of statuses: HTTP return codes, please refer to the values: org. Springframework. HTTP. HttpStatus.

  • Methods: need to specify which method request retry logic, the default value is the GET method, reference values: org. Springframework. HTTP. HttpMethod.

  • Series: a list of status codes configuration, reference values: org. Springframework. HTTP. HttpStatus. Series. The default value is SERVER_ERROR. The value is 5, that is, 5XX(the status code starting with 5). There are five values in total.

When the background service is unavailable, logs of three requests are seen on the console, proving that this configuration is valid.


07

Gateway Performs traffic limiting operations

The Spring Cloud Gateway itself integrates the flow limiting operation. The Gateway needs to use Redis for traffic limiting. Add Redis dependency to poM file:

<dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-data-redis-reactive</artifactId></dependency>Copy the code


The configuration file is as follows:

spring:  cloud:    gateway:      routes:      - id: rate_limit_route        uri: lb://service-consumer        predicates:        - Path=/user/**        filters:        - name: RequestRateLimiter          args:            key-resolver: "#{@hostAddrKeyResolver}"ReplenishRate: 1 Redis-rate-limiter. BurstCapacity: 3-stripprefix =1 Consul: host: 127.0.0.1 port: replenishRate: 1 Redis-rate-limiter. BurstCapacity: 3-stripprefix =1 Consul: host: 127.0.0.1 port: replenishRate: 1 Redis-rate-limiter. 8500 discovery: service-name: service-gateway instance-id: service-gateway-233 redis: host: localhost port: 6379Copy the code


In the configuration query above, the Redis information is configured and the RequestRateLimiter flow-limiting filter is configured. The filter needs to be configured with three parameters:

  • BurstCapacity: Total capacity of the token bucket.

  • ReplenishRate: Average replenishment rate of the token pass per second.

  • Key-resolver: The name of the Bean object used by the resolver to limit flow. It uses the SpEL expression #{@beanname} to get the bean object from the Spring container.

Note: The name under filter must be RequestRateLimiter.


The bean behind the key-resolver parameter needs to be implemented by itself and then injected into the Spring container. KeyResolver needs to implement the Resolve method. For example, to limit traffic by IP address, hostAddress is required. Once KeyResolver is implemented, you need to register the beans of this class with the Ioc container. You can also limit traffic by URI, as with hostname limiting. For example, add the following implementation to the Gateway module:

public class HostAddrKeyResolver implements KeyResolver {    @Override    public Mono<String> resolve(ServerWebExchange exchange) {        return Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());    }    public HostAddrKeyResolver hostAddrKeyResolver() {        return new HostAddrKeyResolver();    }}Copy the code


Inject the class into the Spring container:

@SpringBootApplication@EnableDiscoveryClientpublic class GatewayApplication {    public static void main(String[] args) {        SpringApplication.run(GatewayApplication.class, args);    }    @Bean    public HostAddrKeyResolver hostAddrKeyResolver() {return new HostAddrKeyResolver();    }}Copy the code


Based on the above configuration, traffic limiting can be implemented for ip-based access requests.


08

Custom Gatewayfilter



Spring Cloud Gateway has built-in filters to meet the needs of many scenarios. Of course, you can also customize filters. To customize filters in the Spring Cloud Gateway, the filters need to implement the GatewayFilter and Ordered interfaces.

The following example implements the Gatewayfilter, which can record the time consumed by each request in the form of a log, as follows:

public class RequestTimeFilter implements GatewayFilter, Ordered {    private static final Log log = LogFactory.getLog(GatewayFilter.class);    private static final String REQUEST_TIME_BEGIN = "requestTimeBegin";    @Override    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {        exchange.getAttributes().put(REQUEST_TIME_BEGIN, System.currentTimeMillis());        return chain.filter(exchange).then(                Mono.fromRunnable(() -> {                    Long startTime = exchange.getAttribute(REQUEST_TIME_BEGIN);                    if(startTime ! = null) { log.info("Request path:"+exchange.getRequest().getURI().getRawPath() + "Consumption of time:" + (System.currentTimeMillis() - startTime) + "ms"); }})); } @Override public intgetOrder() {        return 0;    }}Copy the code


The above code defines its own implementation of the filter. Ordered’s int getOrder () method is used to prioritize filters; higher values lower the priority. There is also a filter(ServerWebExchange Exchange, GatewayFilterChain chain) method, in which the start time of the request is recorded and stored in ServerWebExchange. Here is a filter of type “Pre”. Then the run() method equivalent to the “post” filter in the inner class of chain.filter() prints the time consumed by the request.


Next, register the filter with the Router as follows.

 @Bean    public RouteLocator customerRouteLocator(RouteLocatorBuilder builder) {        return builder.routes()                .route(r -> r.path("/user/**")                        .filters(f -> f.filter(new RequestTimeFilter())                                .addResponseHeader("X-Response-Default-Foo"."Default-Bar"))                        .uri("http://localhost:8504/user/info")                        .order(0)                        .id("customer_filter_router")                )                .build();    }Copy the code


In addition to configuring our custom filters in the code described above, we can also configure them directly in the application.yml file, which is not described here.

Start the program, through the curl http://localhost:8098/user/info console will print out the request to consume time, log is as follows:

. The 15:13:31 2019-05-22. 19780-221 the INFO [ctor - HTTP - nio - 4] O.S.C loud. Gateway. Filter. GatewayFilter: request path: / user/INFO elapsed time: 54ms... The 16:46:23 2019-05-22. 29928-785 the INFO [ctor - HTTP - nio - 1] O.S.C loud. Gateway. Filter. GatewayFilter: request path: / user/info3 elapsed time: 5ms....Copy the code



09

Custom GlobalFilter

Spring Cloud Gateway is divided into GatewayFilter and GlobalFilter according to scope. The differences are as follows:

  • GatewayFilter: need to spring. Cloud. Routes. Filters configured under the specific route, the only role in the current routing on or through the spring. The cloud. The default – filters configured in the global, effect on all routes.

  • GlobalFilter: a GlobalFilter that does not need to be configured in the configuration file. It applies to all routes and is packaged by the GatewayFilterAdapter as a filter that can be recognized by the GatewayFilterChain. 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 configured. It is loaded when the system is initialized and acts on each route.


The Gatewayfilter was defined in the previous section and Globalfilter is implemented below:

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 and cannot be accessed." );            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);            return exchange.getResponse().setComplete();        }        return chain.filter(exchange);    }    @Override    public int getOrder() {        return 0;    }}Copy the code


The above code implements Globalfilter. The logic is to determine whether the request contains a parameter token. If it does not, the verification fails and is valid for all requests. If there is a token, the device forwards the token to a specific back-end service. If there is no token, the verification fails.

Through the curl http://localhost:8098/user/info access, because the path does not contain token parameters, cannot pass the check, print log is as follows:

The 15:27:11 2019-05-22. 5956-078 the INFO [ctor - HTTP - nio - 1] com. Song. Gateway. TokenFilter: token is empty, can't visit...Copy the code


Through the curl http://localhost:8098/user/info? If the token=123 is used for access, the result returned by the back-end service can be obtained.