一、概述

spring-cloud-gateway是一个库,可以在spring webflux之上建立一个API网关,它的目的是提供一个简单,有效的方式去路由到APIS并且提供:安全、监控和弹性。gateway构建于spring boot2.x,spring webFlux,and Project Reactor,所以,许多同步库如spring Data和spring security不适用于gateway项目中。因为gateway是异步非阻塞的。
gateway要求spring boot和spring webFlux提供Netty运行环境。它不能工作在传统的servlet容器或打成一个War包。

二、特点

1、能够对任意的请求属性进行路由匹配

2、能对路由进行断言和过滤

3、集成了熔断器

4、集成了spring-cloud-discoveryclient

5、很容易的写断言和过滤

6、限流

7、路径重写

三、三大组件

1、Route。是构建网关的基本模块,他是ID,目标URL,一系列的断言和过滤器组成,如果断言为true,则匹配该路由

2、Rredicate。参考的是Java8的Predicate,开发人员可以匹配HTTP请求中的所有内容,如果请求与断言相匹配则进行路由

3、Filter。指的是spring框架中GatewayFileter的实例,使用过滤器,可以在请求被路由前由前或者之后对请求进行修改。

四、gateway的工作流程

在这里插入图片描述
客户端将请求发给spring cloud gateway,如果gateway handle mapping 确定这个请求和一个路由匹配,它将此请求发给 gateway web handle. 这个 handle 运行这个请求,通过一个filter chain,这个 filter chain在请求路由前后都能执行。

五、gateway的限流

gateway作为网关,与其他网关技术不同的是它能实现限流。gateway使用的是令牌桶算法实现限流。常见的限流算法有:

1、计数器算法:以QPS为100举例,如果1秒钟内钱200ms请求数量到达了100,后面800ms中的请求都会被拒绝,这种情况称为”突刺现象“

2、漏桶算法:可以解决突刺现象。比如创建一个很大的队列来接收请求,一个较小的线程池来处理请求。但是也有极限情况,当队列满了时, 请求也会拒绝掉。

3、令牌桶算法:可以说是漏桶算法的改进。在桶中放令牌,请求获取令牌后才能继续执行。如果桶中无令牌,请求可以选择进行等待或直接拒绝。

在项目中使用gateway网关做限流一般结合的redis,使用令牌桶算法。

六、gateway的断言

gateway包含了许多内置的路由断言工厂。所有的这些断言匹配不同的HTTP请求属性。你能组合这些路由断言工厂。我们一般是在配置文件中配置predicates,当然我们也可以自定义Predicate,如下:

@Component
public class CustomeRoutePredicateFactory extends AbstractRoutePredicateFactory<PathRoutePredicateFactory.Config> {

    public CustomeRoutePredicateFactory() {
        super(PathRoutePredicateFactory.Config.class);
    }

    @Override
    public Predicate<ServerWebExchange> apply(PathRoutePredicateFactory.Config config) {
        System.out.println("TokenRoutePredicateFactory Start...");
        return exchange -> {
            ServerHttpRequest request = exchange.getRequest();
            //take information from the request to see if it
            //matches configuration.
            return matches(config, request);
        };
    }

    private boolean matches(PathRoutePredicateFactory.Config config, ServerHttpRequest request) {
        System.out.println(request.getBody());
        RequestPath path = request.getPath();
        if(path.toString().contains("gateway")) return false;

        return true;
    }
}

yml
routes:
        - id: path_route
          uri: lb://PROVIDER  # lb的意思是负载均衡
          predicates:
            - Custome=/gateway/**    #这个Custome就是自定义类CustomeRoutePredicateFactory的前缀,仿照PathRoutePredicateFactory写的
          filters:
            - name: Hystrix    #表示filter中使用Hystrix来做熔断降级
              args:
                name: fallbackcmd  #名字唯一即可
                fallbackUri: forward:/defaultfallback  #在本gateway中产生的异常或超时将调用本gateway中的control层中的defaultfallback  

上面的逻辑是,获取路径值,如果存在gateway字符串,则返回false。

gateway给我们提供了很多内置的Predicate,如断言时间的 AfterRoutePredicateFactory、BeforeRoutePredicateFactory…,断言cookie的CookieRoutePredicateFactory,断言请求头的HeaderRoutePredicateFactory,一般见其名知其意,且yml中配置时只需要写前缀即可,且一般有两个参数,第一个值一般都是Java的正则表达式。例如

	spring:
  cloud:
    gateway:
      routes:
      - id: cookie_route
        uri: https://example.org
        predicates:
        - Cookie=chocolate, ch.p    #Cookie就是前缀名

七、gateway的filter

gateway的filter有三种:全局Filter、默认Filter和自定义Filter
全局Filter一般是我们定义,全局的filter一般要实现GlobalFilter 和Orderd,我们可以设置getOrder()方法来设置当前Filter的执行权重。order值越小,请求服务前的优先级越高,服务返回后执行的优先级越低

如下

@Bean
    public GlobalFilter customGlobalFilter() {
        return (exchange, chain) -> exchange.getPrincipal()
                .map(Principal::getName)
                .defaultIfEmpty("Default User")
                .map(userName -> {
                    //adds header to proxied request
                    exchange.getRequest().mutate().header("CUSTOM-REQUEST-HEADER", userName).build();
                    return exchange;
                })
                .flatMap(chain::filter);
    }

    @Bean
    public GlobalFilter customGlobalPostFilter() {
        return (exchange, chain) -> chain.filter(exchange)
                .then(Mono.just(exchange))
                .map(serverWebExchange -> {
                    //adds header to response
                    serverWebExchange.getResponse().getHeaders().set("CUSTOM-RESPONSE-HEADER",
                            HttpStatus.OK.equals(serverWebExchange.getResponse().getStatusCode()) ? "It worked": "It did not work");
                    return serverWebExchange;
                })
                .then();
    }

gateway也为我们提供了很多的内置Filter,即默认的Filter,像AddRequestHeaderGatewayFilter,AddRequestParameterGatewayFilter等等。在yml中配置也只需要写前缀即可,如下

spring:
  cloud:
    gateway:
      routes:
      - id: add_request_parameter_route
        uri: https://example.org
        filters:
        - AddRequestParameter=red, blue

下面讲几个常用的gateway内置的Filter:ForwardRoutingFilter和ReactiveLoadBalancerClientFilter

ForwardRoutingFilter:这个过滤器将URI视为是可改变的属性。当URL有forward修饰时,例如:forward://localendpoint,它会使用spring的DispatcherHandle来处理请求,换句话说,就是用DispatcherHandle来处理forward请求转发。
ReactiveLoadBalancerClientFilter:就是用来负载均衡的。当URL有lb时,例如:lb://myservice,它使用springCloud的ReactLoadBalancer去解析服务名为myservice的微服务(将这个myservice解析为实际的主机和端口,并替换当前的URI),找到这个微服务的集群,并负载均衡到某一台,执行服务。

spring:
  cloud:
    gateway:
      routes:
      - id: myRoute
        uri: lb://service
        predicates:
        - Path=/service/**

注意:gateway支持所有的loadbalancer特性

八、gateway的降级熔断

先看看官网的例子,如下

spring:
  cloud:
    gateway:
      routes:
      - id: circuitbreaker_route
        uri: lb://backing-service:8088
        predicates:
        - Path=/consumingServiceEndpoint
        filters:
        - name: CircuitBreaker
          args:
            name: myCircuitBreaker
            fallbackUri: forward:/inCaseOfFailureUseThis
        - RewritePath=/consumingServiceEndpoint, /backingServiceEndpoint

表示如果发生了fallback,则重定向到fallbackUri地址中的controller。正常的话,是到uri中的controller

当然,你也可以使用配置的方式,如下

@Bean
public RouteLocator routes(RouteLocatorBuilder builder) {
    return builder.routes()
        .route("circuitbreaker_route", r -> r.path("/consumingServiceEndpoint")
            .filters(f -> f.circuitBreaker(c -> c.name("myCircuitBreaker").fallbackUri("forward:/inCaseOfFailureUseThis"))
                .rewritePath("/consumingServiceEndpoint", "/backingServiceEndpoint")).uri("lb://backing-service:8088")
        .build();
}

熔断器也可以这样写,没有fallback或处理在gateway应用中,而是有一个注册在主机9994端口下的应用,这个应用可以处理fallback。

spring:
  cloud:
    gateway:
      routes:
      - id: ingredients
        uri: lb://ingredients
        predicates:
        - Path=//ingredients/**
        filters:
        - name: CircuitBreaker
          args:
            name: fetchIngredients
            fallbackUri: forward:/fallback
      - id: ingredients-fallback
        uri: http://localhost:9994
        predicates:
        - Path=/fallback

下面看看我们自己设置的熔断降级,如下

//在当前gateway中的controller,如果Yml中没有配置其他主机的fallback方法,如果配置了,则该方法配置在另外的主机上。
@Configuration
public class CircuitConfig {

    @HystrixCommand(commandKey = "gatewayCommand",fallbackMethod = "gatewayFallback",commandProperties = {
            @HystrixProperty(name = HystrixPropertiesManager.CIRCUIT_BREAKER_ENABLED,value = "true"),
            @HystrixProperty(name =HystrixPropertiesManager.CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS,value = "10000"),
            @HystrixProperty(name =HystrixPropertiesManager.CIRCUIT_BREAKER_ERROR_THRESHOLD_PERCENTAGE,value = "40"),
            @HystrixProperty(name =HystrixPropertiesManager.CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD,value = "5"),
    })
    public String gatewayCommand(){
        return "gatewayCommand method..";
    }

    public String gatewayFallback(){
        return "gatewayFallback method..";
    }
gateway的yml
      routes:
        - id: path_route
          uri: lb://PROVIDER  # lb的意思是负载均衡
          predicates:
            - Custome=/gateway/**
          filters:
            - name: Hystrix
              args:
                name: fallbackcmd
                fallbackUri: forward:/defaultfallback

注意,当前fallbackuri只能使用forward!如果没配置其他的处理fallback的主机,则重定向到当前网关主机的controller路径。

九、gateway整合actuator

需要actuator的依赖。
一般gateway的路由信息是配置注册中心,我们可以使用原始的手工方式刷新路由缓存。使用post请求/actuator/gateway/refresh 即可。当然这需要导入spring的actuator依赖
你也可以检索gateway中所有的路由信息,以手工方式。使用get请求/actuator/gateway/routes
你也可以检索某一个路由信息,使用get请求/actuator/gateway/routes/{id}
总之,可以使用actuator来CRUD路由。

十、总结

1、gateway网关作为整个项目的最上层,其实就是对请求的处理,细化说有:请求路由转发,限流,熔断降级和负载均衡等。但是在项目中一般使用的请求转发和负载均衡。
2、gateway中限流使用的是令牌桶算法。
3、gateway的熔断降级只能在本网关内出现的异常和超时,如果正常请求到别的微服务,而在别的微服务中出现的超时和异常,配置在gateway网关中的熔断降级不起效。
4、gateway使用的异步非阻塞的模型,底层使用的Netty框架,可和springwebFlux、springMvc结合使用。
5、gateway中的Fileter可以理解为是spring中的Aop机制中的环绕通知。有前置和后置,针对请求服务前后而言。Filter的order值越小,执行前置的优先级越大,执行后置的优先级越小。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐