Notes - Spring Cloud (ten): Spring Cloud Gateway (routing)

Spring Cloud (ten): Spring Cloud Gateway (routing)

This article mainly introduces what is Spring Cloud Gateway,

Insert picture description here

Overview

Spring Cloud Gateway is a brand new project of Spring Cloud. The project is a gateway developed based on Spring 5.0, Spring Boot 2.0 and Project Reactor technologies. It aims to provide a simple and effective unified API routing management method for the microservice architecture.

As a gateway in the Spring Cloud ecosystem, Spring Cloud Gateway aims to replace Netflix Zuul. It not only provides a unified routing method, but also provides basic functions of the gateway based on the Filter chain, such as: security, monitoring, burying and current limiting Wait.

Features of Spring Cloud Gateway:

  • Based on Spring Framework 5, Project Reactor and Spring Boot 2.0
  • Dynamic routing
  • Predicates and Filters act on specific routes
  • Integrated Hystrix circuit breaker
  • 集成 Spring Cloud DiscoveryClient
  • Easy-to-write Predicates and Filters
  • Limiting
  • Path rewriting

vs Netflix Zuul

Zuul is based on Servlet 2.5 (using 3.x) and uses blocking API. It does not support any long connections, such as WebSockets. The Spring Cloud Gateway is built on Spring Framework 5, Project Reactor and Spring Boot 2, uses non-blocking API, supports WebSockets, and because it is tightly integrated with Spring, it will be a better development experience.

the term

  • Route : This is the basic building block of a gateway. It is defined by an ID, a target URI, a set of assertions and a set of filters. If the assertion is true, the route matches.
  • Predicate (assertion) : This is a Java 8 Predicate . The input type is one ServerWebExchange. We can use it to match any content from the HTTP request, such as headers or parameters.
  • Filter : This is org.springframework.cloud.gateway.filter.GatewayFilteran example, we can use it to modify the request and response.

Process

Insert picture description here

The client sends a request to Spring Cloud Gateway. Then find the route matching the request in the Gateway Handler Mapping and send it to the Gateway Web Handler. The Handler then sends the request to our actual service to execute the business logic through the specified filter chain, and then returns.
The filters are separated by a dotted line because the filters may execute business logic before ("pre") or after ("post") the proxy request is sent.

Actual combat

We used Zuul to implement a gateway before, and here we use Spring Cloud Gateway to replace it to achieve the same function. Here we continue to use the services that have been written before and start them in sequence:

  • eureka
  • producer
  • consumer

Create a new standard Spring Boot project, name it gateway, and introduce the following dependency coordinates in pom.xml

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

The content of the application.yml configuration file is as follows

spring:
  application:
    name: cloud-gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true
#      routes:
#        - id: default_path_to_http
#          uri: https://myurl
#          order: 10000
#          predicates:
#            - Path=/**
#          filters:
#            - SetPath=/
server:
  port: 10000
eureka:
  client:
    service-url:
      defaultZone: http://localhost:7000/eureka/
logging:
  level:
    org.springframework.cloud.gateway: debug

Configuration instructions:

  • spring.cloud.gateway.discovery.locator.enabled: Whether it is combined with the service registration in the discovery component, and forwarded to the specific service instance through the serviceId. The default falsesetting is trueto enable the function of automatically creating routes based on serviceId through the service center.
  • spring.cloud.gateway.routesIt is an array used to cooperate with specific routing rules. Here I created a default_path_to_httproute with id , the configuration of which is to forward unmatched requests to https://myurl. In fact, after the service discovery is turned on, if you only use the routing rules created by default, this route is not configured, so I commented it out first.
  • Gateway service listens on port 10000
  • Specify the address of the registry to use the service discovery function
  • Adjust the log level of related packages to facilitate troubleshooting

The startup class of Spring Boot does not need to be modified, it can be started directly, and we can see our gateway service in Eureka after startup

Insert picture description here

Then we visit http://localhost:10000/consumer/hello/yujian as before using Zuul

We expected to return "Hello yujian!" like direct access to the consumer, but in fact it went wrong and returned a 404 error. Let's take a look at the log

2020-08-23 15:05:09.562 DEBUG 16484 --- [freshExecutor-0] o.s.c.g.r.RouteDefinitionRouteLocator    : RouteDefinition ReactiveCompositeDiscoveryClient_EUREKA-PRODUCER applying {pattern=/EUREKA-PRODUCER/**} to Path
2020-08-23 15:05:09.564 DEBUG 16484 --- [freshExecutor-0] o.s.c.g.r.RouteDefinitionRouteLocator    : RouteDefinition ReactiveCompositeDiscoveryClient_EUREKA-PRODUCER applying filter {regexp=/EUREKA-PRODUCER/(?<remaining>.*), replacement=/${remaining}} to RewritePath
2020-08-23 15:05:09.566 DEBUG 16484 --- [freshExecutor-0] o.s.c.g.r.RouteDefinitionRouteLocator    : RouteDefinition matched: ReactiveCompositeDiscoveryClient_EUREKA-PRODUCER
2020-08-23 15:05:09.567 DEBUG 16484 --- [freshExecutor-0] o.s.c.g.r.RouteDefinitionRouteLocator    : RouteDefinition ReactiveCompositeDiscoveryClient_EUREKA-CONSUMER applying {pattern=/EUREKA-CONSUMER/**} to Path
2020-08-23 15:05:09.569 DEBUG 16484 --- [freshExecutor-0] o.s.c.g.r.RouteDefinitionRouteLocator    : RouteDefinition ReactiveCompositeDiscoveryClient_EUREKA-CONSUMER applying filter {regexp=/EUREKA-CONSUMER/(?<remaining>.*), replacement=/${remaining}} to RewritePath
2020-08-23 15:05:09.571 DEBUG 16484 --- [freshExecutor-0] o.s.c.g.r.RouteDefinitionRouteLocator    : RouteDefinition matched: ReactiveCompositeDiscoveryClient_EUREKA-CONSUMER
2020-08-23 15:05:09.572 DEBUG 16484 --- [freshExecutor-0] o.s.c.g.r.RouteDefinitionRouteLocator    : RouteDefinition ReactiveCompositeDiscoveryClient_CLOUD-GATEWAY applying {pattern=/CLOUD-GATEWAY/**} to Path
2020-08-23 15:05:09.574 DEBUG 16484 --- [freshExecutor-0] o.s.c.g.r.RouteDefinitionRouteLocator    : RouteDefinition ReactiveCompositeDiscoveryClient_CLOUD-GATEWAY applying filter {regexp=/CLOUD-GATEWAY/(?<remaining>.*), replacement=/${remaining}} to RewritePath
2020-08-23 15:05:09.576 DEBUG 16484 --- [freshExecutor-0] o.s.c.g.r.RouteDefinitionRouteLocator    : RouteDefinition matched: ReactiveCompositeDiscoveryClient_CLOUD-GATEWAY

You can see that Spring Cloud Gateway does automatically create corresponding routes for our producer and consumer, but the pattern/regexp here are all capitalized. Then let's try it out in uppercase.

Visiting http://localhost:20000/EUREKA-CONSUMER/hello/?name=yujian did return "Hello, yujian!", then look at the log again

2020-08-23 15:06:07.664 DEBUG 16484 --- [ctor-http-nio-3] o.s.c.g.h.RoutePredicateHandlerMapping   : Route matched: ReactiveCompositeDiscoveryClient_EUREKA-CONSUMER
2020-08-23 15:06:07.664 DEBUG 16484 --- [ctor-http-nio-3] o.s.c.g.h.RoutePredicateHandlerMapping   : Mapping [Exchange: GET http://localhost:20000/EUREKA-CONSUMER/hello/?name=yujian] to Route{id='ReactiveCompositeDiscoveryClient_EUREKA-CONSUMER', uri=lb://EUREKA-CONSUMER, order=0, predicate=Paths: [/EUREKA-CONSUMER/**], match trailing slash: true, gatewayFilters=[[[RewritePath /EUREKA-CONSUMER/(?<remaining>.*) = '/${remaining}'], order = 1]], metadata={jmx.port=52177, management.port=9000}}
2020-08-23 15:06:07.664 DEBUG 16484 --- [ctor-http-nio-3] o.s.c.g.h.RoutePredicateHandlerMapping   : [a6395175-5] Mapped to org.springframework.cloud.gateway.handler.FilteringWebHandler@514419ac
2020-08-23 15:06:07.665 DEBUG 16484 --- [ctor-http-nio-3] o.s.c.g.handler.FilteringWebHandler      : Sorted gatewayFilterFactories: [[GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.RemoveCachedBodyFilter@4f263698}, order = -2147483648], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.AdaptCachedBodyGlobalFilter@7810580e}, order = -2147482648], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.NettyWriteResponseFilter@c42b297}, order = -1], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.ForwardPathFilter@15a7e17}, order = 0], [[RewritePath /EUREKA-CONSUMER/(?<remaining>.*) = '/${remaining}'], order = 1], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.RouteToRequestUrlFilter@70115457}, order = 10000], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.LoadBalancerClientFilter@a96a3c8}, order = 10100], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.WebsocketRoutingFilter@7fb61a52}, order = 2147483646], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.NettyRoutingFilter@5e56c74a}, order = 2147483647], [GatewayFilterAdapter{delegate=org.springframework.cloud.gateway.filter.ForwardRoutingFilter@1815b188}, order = 2147483647]]
2020-08-23 15:06:09.609 DEBUG 16484 --- [freshExecutor-0] o.s.c.g.r.RouteDefinitionRouteLocator    : RouteDefinition ReactiveCompositeDiscoveryClient_EUREKA-PRODUCER applying {pattern=/EUREKA-PRODUCER/**} to Path
2020-08-23 15:06:09.612 DEBUG 16484 --- [freshExecutor-0] o.s.c.g.r.RouteDefinitionRouteLocator    : RouteDefinition ReactiveCompositeDiscoveryClient_EUREKA-PRODUCER applying filter {regexp=/EUREKA-PRODUCER/(?<remaining>.*), replacement=/${remaining}} to RewritePath
2020-08-23 15:06:09.613 DEBUG 16484 --- [freshExecutor-0] o.s.c.g.r.RouteDefinitionRouteLocator    : RouteDefinition matched: ReactiveCompositeDiscoveryClient_EUREKA-PRODUCER
2020-08-23 15:06:09.615 DEBUG 16484 --- [freshExecutor-0] o.s.c.g.r.RouteDefinitionRouteLocator    : RouteDefinition ReactiveCompositeDiscoveryClient_EUREKA-CONSUMER applying {pattern=/EUREKA-CONSUMER/**} to Path
2020-08-23 15:06:09.616 DEBUG 16484 --- [freshExecutor-0] o.s.c.g.r.RouteDefinitionRouteLocator    : RouteDefinition ReactiveCompositeDiscoveryClient_EUREKA-CONSUMER applying filter {regexp=/EUREKA-CONSUMER/(?<remaining>.*), replacement=/${remaining}} to RewritePath
2020-08-23 15:06:09.617 DEBUG 16484 --- [freshExecutor-0] o.s.c.g.r.RouteDefinitionRouteLocator    : RouteDefinition matched: ReactiveCompositeDiscoveryClient_EUREKA-CONSUMER
2020-08-23 15:06:09.617 DEBUG 16484 --- [freshExecutor-0] o.s.c.g.r.RouteDefinitionRouteLocator    : RouteDefinition ReactiveCompositeDiscoveryClient_CLOUD-GATEWAY applying {pattern=/CLOUD-GATEWAY/**} to Path
2020-08-23 15:06:09.618 DEBUG 16484 --- [freshExecutor-0] o.s.c.g.r.RouteDefinitionRouteLocator    : RouteDefinition ReactiveCompositeDiscoveryClient_CLOUD-GATEWAY applying filter {regexp=/CLOUD-GATEWAY/(?<remaining>.*), replacement=/${remaining}} to RewritePath
2020-08-23 15:06:09.619 DEBUG 16484 --- [freshExecutor-0] o.s.c.g.r.RouteDefinitionRouteLocator    : RouteDefinition matched: ReactiveCompositeDiscoveryClient_CLOUD-GATEWAY

It can be seen that Spring Cloud Gateway automatically creates a route for our consumer, similar to the following

routes:
  - id: CompositeDiscoveryClient_CONSUMER
    uri: lb://CONSUMER
    order: 0
    predicates:
      - Path=/CONSUMER/**
    filters:
      - RewritePath=/CONSUMER/(?<segment>.*), /$\{
    
    segment}

But this way of capitalizing the serviceId is quite painful. Although the Eureka registry displays all capitals by default, is it really good to put the capitalized path in the URL? The only benefit I can think of is that the serviceId and path can be clearly distinguished.

If the uppercase URL automatically becomes lowercase in the browser, you can try: clear the cache, use the incognito mode (command+shift+n), and use it directly in the terminal curl.

The above is the default routing rule based on service discovery. What if we want to customize the routing rule?

For example, our service with this customer-related services (ah, now it single function, only with customers say hi, but this has no effect), we hope that this service path to /customer/begin with, specific to this case, that is /costomer/hello/{name}. And, we need to add a response header to each response X-Response-Default-Foo: Default-Bar.

Let's modify the configuration, the main thing is to add a route, other configurations remain unchanged

routes:
  - id: service_customer
    uri: lb://CONSUMER
    order: 0
    predicates:
      - Path=/customer/**
    filters:
      - StripPrefix=1
      - AddResponseHeader=X-Response-Default-Foo, Default-Bar

The new one StripPrefixcan accept a non-negative integer, and the corresponding specific implementation is StripPrefixGatewayFilterFactorythat it can be seen from the name that its function is to remove the prefix, and that integer corresponds to the number of layers. Specifically in this example, we access through the Spring Cloud Gateway /customer/hello/yibo, then when the gateway service forwards the request backwards, it will be removed /customer, and the microservice will receive it /hello/yujian.

We now visit http://localhost:20000/customer/hello/cloud and we can see that the data can be returned normally and the response header has been added. At this time, http://localhost:20000/EUREKA-CONSUMER/hello/cloud can still return data normally, but there is no custom response header.

Insert picture description here

Spring Cloud Gateway also supports the definition of routing through Java's streaming API. The following is an equivalent route configured through the configuration file above, and can be used with the configuration file.

@Bean
public RouteLocator customerRouteLocator(RouteLocatorBuilder builder) {
    
    
    // @formatter:off
    return builder.routes()
            .route(r -> r.path("/fluent/customer/**")
                         .filters(f -> f.stripPrefix(2)
                                        .addResponseHeader("X-Response-Default-Foo", "Default-Bar"))
                         .uri("lb://CONSUMER")
                         .order(0)
                         .id("fluent_customer_service")
            )
            .build();
    // @formatter:on
}

to sum up

In this article, we briefly learned about Spring Cloud Gateway and used it to implement a simple gateway service. It not only introduces the default routing for microservices by combining with the registry Eureka, but also introduces how to customize routing through configuration files and APIs. I believe you already have a preliminary understanding of Spring Cloud Gateway. We will continue to discover more powerful features of Spring Cloud Gateway in future articles.

Guess you like

Origin blog.csdn.net/qq_44534541/article/details/109261352