SpringCloud gateway (the most complete history)

Crazy maker culture circle distributed Java chat rooms] [one hundred million flow combat series -25 [ blog park entrance total ]

Foreword

Crazy maker culture circle (high concurrent learning community created by the author Nene) Springcloud high concurrent series of articles, will introduce three versions of the highly concurrent spike:

First, version 1: springcloud + zookeeper spike

2, version 2: springcloud + redis distributed lock spike

Third, version 3: springcloud + Nginx + Lua high-performance version of the spike

As well as several important articles about Springcloud :

A, Springcloud high concurrency configuration | in the history of the whole, to understand the full article

Two, Feign Ribbon Hystrix relationship among | the most complete history, the depth of analysis

Three, SpringCloud Gateway Comments | the most complete history

This is the "SpringCloud gateway explain" chapter, as we interpret if done using SpringCloud gateway.

1.1 SpringCloud Gateway Profile

SpringCloud Gateway is a new project Spring Cloud, the project is based on the gateway technology developed by Spring 5.0, Spring Boot 2.0 and Project Reactor, etc., it aims to provide a simple and effective unified micro Services Architecture API routing management.

SpringCloud Gateway Spring Cloud ecosystem as a gateway, the goal is to replace Zuul, Spring Cloud in version 2.0 and above, there is no integration of the latest high-performance version of the new 2.0 version of the above Zuul, Zuul it is still used by non-Reactor model before 2.0 the old version. In order to improve the performance of the gateway, SpringCloud Gateway based WebFlux framework implemented using the underlying framework WebFlux high-performance mode communication frame Reactor Netty.

Spring Cloud Gateway's goal is not only to provide a unified routing, and provides the basic functions of gateway-based approach Filter chain, such as: security, monitoring / indicators, and current limiting.

Declare in advance: the Spring Cloud Gateway underlying the use of high-performance communications framework Netty .

1.2 SpringCloud Gateway feature

SpringCloud official of SpringCloud Gateway features are described below:

(1) based on the Spring Framework 5, Project Reactor and Spring Boot 2.0

(2) integrated circuit breaker Hystrix

(3)集成 Spring Cloud DiscoveryClient

(. 4) Filters Predicates and act on a specific route, and easily prepared Predicates Filters

(5) gateway has some advanced features: dynamic routing, current limiting, rewrite path

From the above features, the features and Zuul are not very different. The main difference SpringCloud Gateway and Zuul, or in the underlying communication framework.

Briefly explain the above three terms:

( . 1 ) the Filter (filter) :

And a filter Zuul conceptually similar, it may be used to intercept and modify the request, and in response to the upstream secondary treatment. Org.springframework.cloud.gateway.filter.GatewayFilter filter is a class instance.

(2) Route (route):

The basic building blocks gateway configuration, and module configuration similar Zuul routes. A Route Module , the URI a target, a set of assertions and a set of filters is defined by an ID. If the assertion is true, the route matches, the target URI is accessed.

( 3 ) Predicate (assertion) :

This is a Java 8 Predicate, and it can be used to match any of the content from the HTTP request, such as headers or parameters. Assertions input type is a ServerWebExchange.

1.3 SpringCloud Gateway and architecture

Spring in the second half of 2017 ushered in the Webflux, Webflux appeared to fill the gaps in the Spring of reactive programming, Webflux of reactive programming is not just programming style changes, but also for a series of well-known frameworks, provides a response visit to the development packages, such as Netty, Redis, and so on.

Webflux SpringCloud Gateway use in the reactor-netty reactive programming assembly, the underlying communication framework Netty used. Here Insert Picture Description

1.3.1 SpringCloud Zuul's IO model

Springcloud Zuul in the integrated version, using the Tomcat container, using conventional Servlet IO processing model.

As you know, servlet lifecycle is managed by the servlet container. Servlet container configured startup servlet object and calls the init () to initialize; Destory servlet invoked when closed container () destruction servlet; runtime container accepts the request, and assign a thread for each request (typically acquires idle thread from the thread pool) then call the service ().

Drawbacks: servlet is a simple network IO model, when a request comes servlet container, servlet container will be for a binding thread in this scenario is not high concurrency model is applicable, but once the concurrent rise in the number of threads will go up, and the thread resources is expensive (on-line context switch, a large memory consumption) seriously affect the processing time of the request. In some simple business scenarios, you do not want to assign a thread for each request, only need one or several concurrent threads will be able to cope with the huge request, this business scenarios under the servlet model no advantage.Here Insert Picture Description

So Springcloud Zuul a blocking process is based on the model servlet, i.e. implements a servlet spring (the DispatcherServlet) request process all requests by the servlet blocking processing. So Springcloud Zuul not get rid of the drawbacks servlet model. Although Zuul 2.0 start using Netty, and already have a mature Zuul 2.0 cases of large-scale cluster deployments, however, Springcloud has been no official change integrated version of the plan.

1.3.2 Webflux model

Webflux Servlet model replaces the old threading model. With a small amount of request and response io threading operation, these threads is called a thread Loop, and the service framework to reactive programming process, reactive programming is very flexible, the user may be submitted to the service in the blocking operation in response to the frame work thread execution, without blocking operation still can be treated in the Loop thread, greatly improving the utilization of Loop thread. Official structure:

Here Insert Picture Description

Webflux虽然可以兼容多个底层的通信框架,但是一般情况下,底层使用的还是Netty,毕竟,Netty是目前业界认可的最高性能的通信框架。而Webflux的Loop线程,正好就是著名的Reactor 模式IO处理模型的Reactor线程,如果使用的是高性能的通信框架Netty,这就是Netty的EventLoop线程。

关于Reactor线程模型,和Netty通信框架的知识,是Java程序员的重要、必备的内功,个中的原理,具体请参见尼恩编著的《Netty、Zookeeper、Redis高并发实战》一书,这里不做过多的赘述。

1.3.3 Spring Cloud Gateway的处理流程

客户端向 Spring Cloud Gateway 发出请求。然后在 Gateway Handler Mapping 中找到与请求相匹配的路由,将其发送到 Gateway Web Handler。Handler 再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前(“pre”)或之后(“post”)执行业务逻辑。 Here Insert Picture Description

1.4 Spring Cloud Gateway路由配置方式

1.4.1 基础URI一种路由配置方式

如果请求的目标地址,是单个的URI资源路径,配置文件示例如下:

server:
  port: 8080
spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
        -id: url-proxy-1
          uri: https://blog.csdn.net
          predicates:
            -Path=/csdn

各字段含义如下:

id:我们自定义的路由 ID,保持唯一

uri:目标服务地址

predicates:路由条件,Predicate 接受一个输入参数,返回一个布尔值结果。该接口包含多种默认方法来将 Predicate 组合成其他复杂的逻辑(比如:与,或,非)。

上面这段配置的意思是,配置了一个 id 为 url-proxy-1的URI代理规则,路由的规则为:当访问地址http://localhost:8080/csdn/1.jsp时,会路由到上游地址https://blog.csdn.net/1.jsp。

1.4.2 基于代码的路由配置方式

转发功能同样可以通过代码来实现,我们可以在启动类 GateWayApplication 中添加方法 customRouteLocator() 来定制转发规则。

package com.springcloud.gateway;
 
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
 
@SpringBootApplication
public class GatewayApplication {
 
    public static void main(String[] args) {
        SpringApplication.run(GatewayApplication.class, args);
    }
 
    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes()
                .route("path_route", r -> r.path("/csdn")
                        .uri("https://blog.csdn.net"))
                .build();
    }
 
}

我们在yaml配置文件中注销掉相关路由的配置,重启服务,访问链接:http://localhost:8080/ csdn, 可以看到和上面一样的页面,证明我们测试成功。

上面两个示例中 uri 都是指向了我的CSDN博客,在实际项目使用中可以将 uri 指向对外提供服务的项目地址,统一对外输出接口。

1.4.3 和注册中心相结合的路由配置方式

在uri的schema协议部分为自定义的lb:类型,表示从微服务注册中心(如Eureka)订阅服务,并且进行服务的路由。

一个典型的示例如下:

server:
  port: 8084
spring:
  cloud:
    gateway:
      routes:
      -id: seckill-provider-route
        uri: lb://seckill-provider
        predicates:
        - Path=/seckill-provider/**

      -id: message-provider-route
        uri: lb://message-provider
        predicates:
        -Path=/message-provider/**

application:
  name: cloud-gateway

eureka:
  instance:
    prefer-ip-address: true
  client:
    service-url:
      defaultZone: http://localhost:8888/eureka/


注册中心相结合的路由配置方式,与单个URI的路由配置,区别其实很小,仅仅在于URI的schema协议不同。单个URI的地址的schema协议,一般为http或者https协议。

1.5 详解:SpringCloud Gateway 匹配规则

Spring Cloud Gateway 的功能很强大,我们仅仅通过 Predicates 的设计就可以看出来,前面我们只是使用了 predicates 进行了简单的条件匹配,其实 Spring Cloud Gataway 帮我们内置了很多 Predicates 功能。

Spring Cloud Gateway 是通过 Spring WebFlux 的 HandlerMapping 做为底层支持来匹配到转发路由,Spring Cloud Gateway 内置了很多 Predicates 工厂,这些 Predicates 工厂通过不同的 HTTP 请求参数来匹配,多个 Predicates 工厂可以组合使用。

1.5.1 Predicate 断言条件介绍

Predicate 来源于 Java 8,是 Java 8 中引入的一个函数,Predicate 接受一个输入参数,返回一个布尔值结果。该接口包含多种默认方法来将 Predicate 组合成其他复杂的逻辑(比如:与,或,非)。可以用于接口请求参数校验、判断新老数据是否有变化需要进行更新操作。

在 Spring Cloud Gateway 中 Spring 利用 Predicate 的特性实现了各种路由匹配规则,有通过 Header、请求参数等不同的条件来进行作为条件匹配到对应的路由。网上有一张图总结了 Spring Cloud 内置的几种 Predicate 的实现。 Here Insert Picture Description [ 说白了 Predicate 就是为了实现一组匹配规则,方便让请求过来找到对应的 Route 进行处理,接下来我们接下 Spring Cloud GateWay 内置几种 Predicate 的使用。

1.5.2 通过请求参数匹配

Query Route Predicate 支持传入两个参数,一个是属性名一个为属性值,属性值可以是正则表达式。

server:

  port: 8080

spring:

  application:

​    name: api-gateway

  cloud:

​    gateway:

​      routes:

​        -id: gateway-service

​          uri: https://www.baidu.com

​          order: 0

​          predicates:

​            -Query=smile

这样配置,只要请求中包含 smile 属性的参数即可匹配路由。

使用 curl 测试,命令行输入:

curl localhost:8080?smile=x&id=2

经过测试发现只要请求汇总带有 smile 参数即会匹配路由,不带 smile 参数则不会匹配。

还可以将 Query 的值以键值对的方式进行配置,这样在请求过来时会对属性值和正则进行匹配,匹配上才会走路由。

server:

  port: 8080

spring:

  application:

​    name: api-gateway

  cloud:

​    gateway:

​      routes:

​        -id: gateway-service

​          uri: https://www.baidu.com

​          order: 0

​          predicates:

​            -Query=keep, pu.

这样只要当请求中包含 keep 属性并且参数值是以 pu 开头的长度为三位的字符串才会进行匹配和路由。

使用 curl 测试,命令行输入:

curl localhost:8080?keep=pub

测试可以返回页面代码,将 keep 的属性值改为 pubx 再次访问就会报 404,证明路由需要匹配正则表达式才会进行路由。

Header Route Predicate 和 Cookie Route Predicate 一样,也是接收 2 个参数,一个 header 中属性名称和一个正则表达式,这个属性值和正则表达式匹配则执行。

server:

  port: 8080

spring:

  application:

​    name: api-gateway

  cloud:

​    gateway:

​      routes:

​        -id: gateway-service

​          uri: https://www.baidu.com

​          order: 0

​          predicates:

​            - Header=X-Request-Id, \d+

使用 curl 测试,命令行输入:

curl http://localhost:8080 -H "X-Request-Id:88"

则返回页面代码证明匹配成功。将参数-H "X-Request-Id:88"改为-H "X-Request-Id:spring"再次执行时返回404证明没有匹配。

Cookie Route Predicate 可以接收两个参数,一个是 Cookie name ,一个是正则表达式,路由规则会通过获取对应的 Cookie name 值和正则表达式去匹配,如果匹配上就会执行路由,如果没有匹配上则不执行。

server:

  port: 8080

spring:

  application:

​    name: api-gateway

  cloud:

​    gateway:

​      routes:

​        -id: gateway-service

​          uri: https://www.baidu.com

​          order: 0

​          predicates:

​            - Cookie=sessionId, test

使用 curl 测试,命令行输入:

curl http://localhost:8080 --cookie "sessionId=test"

则会返回页面代码,如果去掉--cookie "sessionId=test",后台汇报 404 错误。

1.5.5 通过 Host 匹配

Host Route Predicate 接收一组参数,一组匹配的域名列表,这个模板是一个 ant 分隔的模板,用.号作为分隔符。它通过参数中的主机地址作为匹配规则。

server:

  port: 8080

spring:

  application:

​    name: api-gateway

  cloud:

​    gateway:

​      routes:

​        -id: gateway-service

​          uri: https://www.baidu.com

​          order: 0

​          predicates:

​            - Host=**.baidu.com

使用 curl 测试,命令行输入:

curl http://localhost:8080 -H "Host: www.baidu.com"

curl http://localhost:8080 -H "Host: md.baidu.com"

经测试以上两种 host 均可匹配到 host_route 路由,去掉 host 参数则会报 404 错误。

1.5.6 通过请求方式匹配

可以通过是 POST、GET、PUT、DELETE 等不同的请求方式来进行路由。

server:

  port: 8080

spring:

  application:

​    name: api-gateway

  cloud:

​    gateway:

​      routes:

​        -id: gateway-service

​          uri: https://www.baidu.com

​          order: 0

​          predicates:

​            - Method=GET

使用 curl 测试,命令行输入:

# curl 默认是以 GET 的方式去请求

curl http://localhost:8080

测试返回页面代码,证明匹配到路由,我们再以 POST 的方式请求测试。

# curl 默认是以 GET 的方式去请求

curl -X POST http://localhost:8080

返回 404 没有找到,证明没有匹配上路由

1.5.7 通过请求路径匹配

Path Route Predicate 接收一个匹配路径的参数来判断是否走路由。

server:

  port: 8080

spring:

  application:

​    name: api-gateway

  cloud:

​    gateway:

​      routes:

​        -id: gateway-service

​          uri: http://ityouknow.com

​          order: 0

​          predicates:

​            -Path=/foo/{segment}

如果请求路径符合要求,则此路由将匹配,例如:/foo/1 或者 /foo/bar。

使用 curl 测试,命令行输入:

curl http://localhost:8080/foo/1

curl http://localhost:8080/foo/xx

curl http://localhost:8080/boo/xx

经过测试第一和第二条命令可以正常获取到页面返回值,最后一个命令报404,证明路由是通过指定路由来匹配。

1.5.8 通过请求 ip 地址进行匹配

Predicate 也支持通过设置某个 ip 区间号段的请求才会路由,RemoteAddr Route Predicate 接受 cidr 符号(IPv4 或 IPv6 )字符串的列表(最小大小为1),例如 192.168.0.1/16 (其中 192.168.0.1 是 IP 地址,16 是子网掩码)。

server:

  port: 8080

spring:

  application:

​    name: api-gateway

  cloud:

​    gateway:

​      routes:

​        - id: gateway-service

​          uri: https://www.baidu.com

​          order: 0

​          predicates:

​            - RemoteAddr=192.168.1.1/24

可以将此地址设置为本机的 ip 地址进行测试。

curl localhost:8080

如果请求的远程地址是 192.168.1.10,则此路由将匹配。

1.5.10 组合使用

server:
  port: 8080
spring:
  application:
    name: api-gateway
  cloud:
    gateway:
      routes:
        - id: gateway-service
          uri: https://www.baidu.com
          order: 0
          predicates:
            - Host=**.foo.org
            - Path=/headers
            - Method=GET
            - Header=X-Request-Id, \d+
            - Query=foo, ba.
            - Query=baz
            - Cookie=chocolate, ch.p

各种 Predicates 同时存在于同一个路由时,请求必须同时满足所有的条件才被这个路由匹配。

一个请求满足多个路由的断言条件时,请求只会被首个成功匹配的路由转发

1.6 Springcloud gateway 高级功能

1.6.1 实现熔断降级

为什么要实现熔断降级?

在分布式系统中,网关作为流量的入口,因此会有大量的请求进入网关,向其他服务发起调用,其他服务不可避免的会出现调用失败(超时、异常),失败时不能让请求堆积在网关上,需要快速失败并返回给客户端,想要实现这个要求,就必须在网关上做熔断、降级操作。

为什么在网关上请求失败需要快速返回给客户端?

因为当一个客户端请求发生故障的时候,这个请求会一直堆积在网关上,当然只有一个这种请求,网关肯定没有问题(如果一个请求就能造成整个系统瘫痪,那这个系统可以下架了),但是网关上堆积多了就会给网关乃至整个服务都造成巨大的压力,甚至整个服务宕掉。因此要对一些服务和页面进行有策略的降级,以此缓解服务器资源的的压力,以保证核心业务的正常运行,同时也保持了客户和大部分客户的得到正确的相应,所以需要网关上请求失败需要快速返回给客户端。

server.port: 8082

spring:
  application:
    name: gateway
  redis:
      host: localhost
      port: 6379
      password: 123456
  cloud:
    gateway:
      routes:
        - id: rateLimit_route
          uri: http://localhost:8000
          order: 0
          predicates:
            - Path=/test/**
          filters:
            - StripPrefix=1
            - name: Hystrix
              args:
                name: fallbackCmdA
                fallbackUri: forward:/fallbackA

  hystrix.command.fallbackCmdA.execution.isolation.thread.timeoutInMilliseconds: 5000

这里的配置,使用了两个过滤器:

(1)过滤器StripPrefix,作用是去掉请求路径的最前面n个部分截取掉。

StripPrefix=1就代表截取路径的个数为1,比如前端过来请求/test/good/1/view,匹配成功后,路由到后端的请求路径就会变成http://localhost:8888/good/1/view。

(2)过滤器Hystrix,作用是通过Hystrix进行熔断降级

当上游的请求,进入了Hystrix熔断降级机制时,就会调用fallbackUri配置的降级地址。需要注意的是,还需要单独设置Hystrix的commandKey的超时时间

fallbackUri配置的降级地址的代码如下:

package org.gateway.controller;

import org.gateway.response.Response;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class FallbackController {

    @GetMapping("/fallbackA")
    public Response fallbackA() {
        Response response = new Response();
        response.setCode("100");
        response.setMessage("服务暂时不可用");
        return response;
    }
}

1.6.2 分布式限流

从某种意义上讲,令牌桶算法是对漏桶算法的一种改进,桶算法能够限制请求调用的速率,而令牌桶算法能够在限制调用的平均速率的同时还允许一定程度的突发调用。在令牌桶算法中,存在一个桶,用来存放固定数量的令牌。算法中存在一种机制,以一定的速率往桶中放令牌。每次请求调用需要先获取令牌,只有拿到令牌,才有机会继续执行,否则选择选择等待可用的令牌、或者直接拒绝。放令牌这个动作是持续不断的进行,如果桶中令牌数达到上限,就丢弃令牌,所以就存在这种情况,桶中一直有大量的可用令牌,这时进来的请求就可以直接拿到令牌执行,比如设置qps为100,那么限流器初始化完成一秒后,桶中就已经有100个令牌了,这时服务还没完全启动好,等启动完成对外提供服务时,该限流器可以抵挡瞬时的100个请求。所以,只有桶中没有令牌时,请求才会进行等待,最后相当于以一定的速率执行。 Here Insert Picture Description

在Spring Cloud Gateway中,有Filter过滤器,因此可以在“pre”类型的Filter中自行实现上述三种过滤器。但是限流作为网关最基本的功能,Spring Cloud Gateway官方就提供了RequestRateLimiterGatewayFilterFactory这个类,适用在Redis内的通过执行Lua脚本实现了令牌桶的方式。具体实现逻辑在RequestRateLimiterGatewayFilterFactory类中,lua脚本在如下图所示的文件夹中: Here Insert Picture Description

首先在工程的pom文件中引入gateway的起步依赖和redis的reactive依赖,代码如下:

配置如下:


server:
  port: 8081
spring:
  cloud:
    gateway:
      routes:
      - id: limit_route
        uri: http://httpbin.org:80/get
        predicates:
        - After=2017-01-20T17:42:47.789-07:00[America/Denver]
        filters:
        - name: RequestRateLimiter
          args:
            key-resolver: '#{@userKeyResolver}'
            redis-rate-limiter.replenishRate: 1
            redis-rate-limiter.burstCapacity: 3
  application:
    name: cloud-gateway
  redis:
    host: localhost
    port: 6379
    database: 0

在上面的配置文件,指定程序的端口为8081,配置了 redis的信息,并配置了RequestRateLimiter的限流过滤器,该过滤器需要配置三个参数:

  • burstCapacity,令牌桶总容量。

  • replenishRate,令牌桶每秒填充平均速率。

  • key-resolver, the resolver for the name of Bean object keys of limiting. It acquires the SpEL expression Bean Spring objects from a container according to # {@ beanName}.

Here restrictor according to user ID, the request path parameters must carry userId

@Bean

KeyResolver userKeyResolver() {
  return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("user"));
}

 

KeyResolver resolve the need to achieve a method, such as to limit current, it is necessary to judge with the userid The userid. After the realization of complete KeyResolver, this class will need to register Bean Ioc container.

If you need to limit the flow based on IP, obtain current limiting Key bean definitions are:

@Bean
public KeyResolver ipKeyResolver() {
  return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getHostName());
}

Available through exchange of information object to the request, here with the HostName, if you want the user to do the limiting words here can obtain a user ID or user name for the current request it, such as:

If the required flow restrictor interface according to the URI, then the need to get request uri address as limiting key, Bean object is defined:

 
@Bean
KeyResolver apiKeyResolver() {
  return exchange -> Mono.just(exchange.getRequest().getPath().value());
}

Available through exchange of information object to the request, here with the HostName, if you want the user to do the limiting words here can obtain a user ID or user name for the current request it, such as:

If the required flow restrictor interface according to the URI, then the need to get request uri address as limiting key, Bean object is defined:

 
@Bean
KeyResolver apiKeyResolver() {
  return exchange -> Mono.just(exchange.getRequest().getPath().value());
}

Finally, tell us about the crazy maker culture circle: Crazy maker culture circle, a highly concurrent Java learning community [ blog park entrance total ]

Crazy maker culture circle, a commitment to delivering: interview + interview must-must-must-interview + base + actual principle books " Netty Zookeeper Redis combat high concurrency "

img


Crazy maker culture circles Java Sike series

  • Java (Netty) chat program [traffic] one hundred million real open source project combat
  • Netty source, principle, JAVA NIO principle
  • Java questions face clean sweep
  • Crazy maker culture circle [blog park entrance total]

Guess you like

Origin www.cnblogs.com/crazymakercircle/p/11704077.html