关于java:新一代服务网关Gateway的实践笔记

3、SpringCloud Gateway

Spring Cloud Gateway 是Spring Cloud团队的一个全新我的项目,基于Spring 5.0、SpringBoot2.0、Project Reactor 等技术开发的网关。旨在为微服务架构提供一种简略无效对立的API路由治理形式。

Spring Cloud Gateway 作为SpringCloud生态系统中的网关,指标是代替Netflix Zuul。Gateway不仅提供对立路由形式,并且基于Filter链的形式提供网关的基本功能。例如:平安,监控/指标,和限流。

总结:微服务网关就是一个零碎,通过裸露该微服务网关零碎,不便咱们进行相干的鉴权,安全控制,日志对立解决,易于监控,限流等相干性能。

实现微服务网关的技术有很多,

  • nginx:Nginx (engine x) 是一个高性能的HTTP和反向代理web服务器,同时也提供了IMAP/POP3/SMTP服务
  • zuul :Zuul 是 Netflflix 出品的一个基于 JVM 路由和服务端的负载均衡器。
  • spring-cloud-gateway:是spring 出品的基于spring的网关我的项目,集成断路器,门路重写,性能比Zuul好。

咱们应用gateway这个网关技术,无缝连接到基于spring cloud的微服务开发中来。

gateway官网:

https://spring.io/projects/sp…

3.1 Gateway工作原理

咱们在学习Gateway之前,先弄清楚Gateway的工作原理,前面应用它的各个性能时,就晓得该如何应用了,工作流程图如下:

Gateway的执行流程如下:

1:Gateway的客户端回向Spring Cloud Gateway发动申请,申请首先会被HttpWebHandlerAdapter进行提取组装成网关的上下文,而后网关的上下文会传递到DispatcherHandler。

2:DispatcherHandler是所有申请的散发处理器,DispatcherHandler次要负责散发申请对应的处理器,比方将申请散发到对应RoutePredicateHandlerMapping(路由断言处理器映射器)。

3:路由断言解决映射器次要用于路由的查找,以及找到路由后返回对应的FilteringWebHandler。

4:FilteringWebHandler次要负责组装Filter链表并调用Filter执行一系列Filter解决,而后把申请转到后端对应的代理服务解决,处理完毕后,将Response返回到Gateway客户端。
在Filter链中,通过虚线宰割Filter的起因是,过滤器能够在转发申请之前解决或者接管到被代理服务的返回后果之后解决。所有的Pre类型的Filter执行结束之后,才会转发申请到被代理的服务解决。被代理的服务把所有申请结束之后,才会执行Post类型的过滤器。

3.2 Gateway路由

Gateway路由配置分为基于配置的动态路由设置和基于代码动静路由配置,

动态路由是指在application.yml中把路由信息配置好了,而动静路由则反对在代码中动静加载路由信息,更加灵便,咱们接下来把这2种路由操作都实现一次。

3.2.1 业务阐明

如上图:

1:用户所有申请以/order开始的申请,都路由到 hailtaxi-order服务
2:用户所有申请以/driver开始的申请,都路由到 hailtaxi-driver服务
3:用户所有申请以/pay开始的申请,都路由到 hailtaxi-pay服务

3.2.2 基于配置路由设置

如上图所示,正是Gateway动态路由配置:

1:用户所有申请以/order开始的申请,都路由到 hailtaxi-order服务
2:用户所有申请以/driver开始的申请,都路由到 hailtaxi-driver服务
3:用户所有申请以/pay开始的申请,都路由到 hailtaxi-pay服务

配置参数阐明:

routes:路由配置
- id:惟一标识符
uri:路由地址,能够是 lb://IP:端口     也能够是   lb://${spring.application.name}
predicates:断言,是指路由条件
- Path=/driver/**:路由条件。Predicate 承受一个输出参数,返回一个布尔值后果。这里示意匹配所有以driver开始的申请。
filters:过滤器
- StripPrefix=1:实在路由的时候,去掉第1个门路,门路个数以/宰割辨别

测试url:http://localhost:8001/driver/…

3.2.3 基于代码路由配置

咱们同样实现下面的性能,但这里基于代码形式实现。所有路由规定咱们能够从数据库中读取并加载到程序中。基于代码的路由配置咱们只须要创立RouteLocator并增加路由配置即可,代码如下:

/***
 * 路由配置
 * @param builder
 * @return
 */
@Bean
public RouteLocator routeLocator(RouteLocatorBuilder builder) {

    return builder.routes()
        .route("hailtaxi-driver", r -> r.path("/driver/**").uri("lb://hailtaxi-driver"))
        .route("hailtaxi-order", r -> r.path("/order/**").uri("lb://hailtaxi-order"))
        .route("hailtaxi-pay", r -> r.path("/pay/**").uri("lb://hailtaxi-pay"))
        .build();
}

在实在场景中,基于配置文件的形式更直观、简介,但代码的路由配置是更弱小,能够实现很丰盛的性能,能够把路由规定存在数据库中,每次间接从数据库中加载规定,这样的益处是能够动静刷新路由规定,通常利用于权限零碎动静配置。

spring: 
    cloud: 
        gateway:
          #路由配置
          routes:
            #惟一标识符
            - id: hailtaxi-driver
              uri: lb://hailtaxi-driver
              #路由断言
              predicates:
                - Path=/driver/**
            #惟一标识符
            - id: hailtaxi-order
              uri: lb://hailtaxi-order
              #路由断言
              predicates:
                - Path=/order/**
            #惟一标识符
            - id: hailtaxi-pay
              uri: lb://hailtaxi-pay
              #路由断言
              predicates:
                - Path=/pay/**

3.2.4 Gateway-Predicate

下面路由匹配规定中咱们都用了- Path形式,其实就是门路匹配形式,除了门路匹配形式,Gateway还反对很多丰盛的匹配形式,咱们对这些形式别离进行解说。

对于Predicate学习地址,能够参考官网:

https://docs.spring.io/spring…

或者:

https://cloud.spring.io/sprin…

routes上面的属性含意如下:

id:咱们自定义的路由 ID,放弃惟一

uri:指标服务地址

predicates:路由条件,Predicate 承受一个输出参数,返回一个布尔值后果。该属性蕴含多种默认办法来将 Predicate 组合成其余简单的逻辑(比方:与,或,非)

Predicate 来源于 Java 8,Predicate 承受一个输出参数,返回一个布尔值后果。该接口蕴含多种默认办法来将 Predicate 组合成其余简单的逻辑(比方:与,或,非)。

在 Spring Cloud Gateway 中 Spring 利用 Predicate 的个性实现了各种路由匹配规定,通过 Header、申请参数等不同的条件来作为条件匹配到对应的路由。

上面的一张图(来自网络)总结了 Spring Cloud 内置的几种 Predicate 的实现:

咱们在这里解说几个断言匹配 形式。

Cookie:

Gateway的Cookie匹配接管两个参数:一个是 Cookie name ,一个是正则表达式。路由规定就是通过获取对应的 Cookie name 值和正则表达式去匹配,如果匹配上就会执行路由,如果没有匹配上则不执行。如下配置:

    gateway:
      routes:
      - id: hailtaxi-driver
        uri: lb://hailtaxi-driver
        predicates:
        - Path=/driver/**
        - Cookie=username,itheima

这里示意申请携带了cookie为username的数据,并且值为itheima,就容许通过。

Header 匹配:

Header 匹配 和 Cookie 匹配 一样,也是接管两个参数,一个 header 中属性名称和一个正则表达式,这个属性值和正则表达式匹配则执行。配置如下:

    gateway:
      routes:
      - id: hailtaxi-driver
        uri: lb://hailtaxi-driver
        predicates:
        - Path=/driver/**
        - Header=token,^(?!\d+$)[\da-zA-Z]+$

下面的匹配规定,就是申请头要有token属性,并且值必须为数字和字母组合的正则表达式,例如携带token=19and30就能够通过拜访。

申请形式匹配:

通过申请的形式是 POST、GET、PUT、DELETE 等进行路由。配置如下:

    gateway:
      routes:
      - id: hailtaxi-driver
        uri: lb://hailtaxi-driver
        predicates:
        - Path=/driver/**
        - Method=GET,POST

通过yml的残缺代码如下(正文掉java的配置

server:
  port: 8001
spring:
  application:
    name: hailtaxi-gateway
  main:
    allow-bean-definition-overriding: true
  cloud:
    #Consul配置
    consul:
      host: 127.0.0.1
      port: 8500
      discovery:
        #注册到Consul中的服务名字
        service-name: ${spring.application.name}
        #注册的服务的实例 Id,最好不要反复,这里参考官网倡议的形式 带随机数 默认:利用名:port
        #instance-id: ${spring.application.name}:${vcap.application.instance_id:${spring.application.i nstance_id:${random.value}}}
        # 自定义实例id为:利用名:ip:port
        instance-id: ${spring.application.name}:${spring.cloud.client.ip-address}:${server.port}
        prefer-ip-address: true
        # 开启服务注册
        register: true
        # 开启服务发现
        enabled: true
        #2 分钟之后健康检查未通过勾销注册
        health-check-critical-timeout: 2m
        #consul 健康检查的轮询周期
        health-check-interval: 10s
    gateway:
      #路由配置
      routes:
        #惟一标识符
        - id: hailtaxi-driver
          uri: lb://hailtaxi-driver
          #路由断言
          predicates:
            - Path=/driver/**
            - Cookie=username,itheima
            - Header=token,^(?!\d+$)[\da-zA-Z]+$
            - Method=GET,POST
        #惟一标识符
        - id: hailtaxi-order
          uri: lb://hailtaxi-order
          #路由断言
          predicates:
            - Path=/order/**
        #惟一标识符
        - id: hailtaxi-pay
          uri: lb://hailtaxi-pay
          #路由断言
          predicates:
            - Path=/pay/**
            

3.2.5、断言源码分析

Cookie断言来说,首先看它的体系结构

public class CookieRoutePredicateFactory
        extends AbstractRoutePredicateFactory<CookieRoutePredicateFactory.Config> {

    /**
     * Name key.
     */
    public static final String NAME_KEY = "name";

    /**
     * Regexp key.
     */
    public static final String REGEXP_KEY = "regexp";

    public CookieRoutePredicateFactory() {
        super(Config.class);
    }
    
    /*
      通过shortcutFieldOrder办法设置Config配置类中的属性,须要依据具体的规定来设置
      通过shortcutType办法获取具体规定,具体参看:org.springframework.cloud.gateway.support.ShortcutConfigurable.ShortcutType
      规定包含以下几种:
        DEFAULT : 依照shortcutFieldOrder程序顺次赋值
    */
    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList(NAME_KEY, REGEXP_KEY);
    }

    @Override
    public Predicate<ServerWebExchange> apply(Config config) {
        return new GatewayPredicate() {
            @Override
            public boolean test(ServerWebExchange exchange) {
                List<HttpCookie> cookies = exchange.getRequest().getCookies()
                        .get(config.name);
                if (cookies == null) {
                    return false;
                }
                for (HttpCookie cookie : cookies) {
                    if (cookie.getValue().matches(config.regexp)) {
                        return true;
                    }
                }
                return false;
            }

            @Override
            public String toString() {
                return String.format("Cookie: name=%s regexp=%s", config.name,
                        config.regexp);
            }
        };
    }
    
    /*
     外部配置类是用来接管在配置文件中配置的参数的
      routes:
        #惟一标识符
        - id: hailtaxi-driver
          uri: lb://hailtaxi-driver
          #路由断言
          predicates:
            - Cookie=username,itheima
    */
    @Validated
    public static class Config {

        @NotEmpty
        private String name;

        @NotEmpty
        private String regexp;

        public String getName() {
            return name;
        }

        public Config setName(String name) {
            this.name = name;
            return this;
        }

        public String getRegexp() {
            return regexp;
        }

        public Config setRegexp(String regexp) {
            this.regexp = regexp;
            return this;
        }

    }

}

只管Spring Cloud Gateway曾经蕴含了很多路由匹配规定,有时候咱们须要开发自定义路由匹配规定来满足需要,上面简略的介绍一下如何自定义路由匹配规定。

案例

需要:转发带token的申请到hailtaxi-drvier服务中,这里定义申请带token是指蕴含某个申请头的申请,至于是什么申请头能够由配置指定

1、批改配置文件

    gateway:
      #路由配置
      routes:
        #惟一标识符
        - id: hailtaxi-driver
          uri: lb://hailtaxi-driver
          #路由断言
          predicates:
            # 自定义一个Token断言,如果申请蕴含Authorization的token信息则通过
            - Token=Authorization

2、创立 RoutePredicateFactory

断言工厂默认命名规定必须依照”名称”+RoutePredicateFactory,如上TokenRoutePredicateFactory的断言名称为Token

@Slf4j
@Component // 要交给spring容器治理
public class TokenRoutePredicateFactory extends AbstractRoutePredicateFactory<TokenRoutePredicateFactory.Config> {

    public TokenRoutePredicateFactory() {
        super(Config.class);
    }

    public Predicate<ServerWebExchange> apply(Config config) {
        return exchange -> {
            // 打印配置文件参数值
            String headerName = config.getHeaderName();
            HttpHeaders headers = exchange.getRequest().getHeaders();
            List<String> header = headers.get(headerName);
            log.info("Token Predicate headers:{}", header);
            // 断言返回的是boolean值
            return header!=null && header.size()>0;
        };
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("headerName");//指定配置文件中加载到的配置信息应填充到Config的哪个属性上
    }

    @Override
    public ShortcutType shortcutType() {
        return ShortcutType.DEFAULT;
    }

    @Data
    public static class Config { //static class
        private String headerName;//存储从配置文件中加载的配置
    }
}

启动测试:http://localhost:8001/driver/…

3.3 Gateway过滤器

Spring Cloud Gateway依据作用范畴划分为GatewayFilterGlobalFilter,二者区别如下:

  • GatewayFilter : 须要通过spring.cloud.routes.filters 配置在具体路由下,只作用在以后路由上或通过spring.cloud.default-filters配置在全局,作用在所有路由上;gateway内置了多种过滤器工厂,配套的过滤器能够间接应用,如下图所示:

  • GlobalFilter : 全局过滤器,不须要在配置文件中配置,作用在所有的路由上,最终通过GatewayFilterAdapter包装成GatewayFilterChain可辨认的过滤器,它为申请业务以及路由的URI转换为实在业务服务的申请地址的外围过滤器,不须要配置,零碎初始化时加载,并作用在每个路由上。

过滤器作为Gateway的重要性能。罕用于申请鉴权、服务调用时长统计、批改申请或响应header、限流、去除门路等等。

对于Gateway过滤器的更多应用,大家能够参考官网地址:

https://docs.spring.io/spring…

或者:

https://cloud.spring.io/sprin…

3.3.1 过滤器分类

默认过滤器:出厂自带,实现好了拿来就用,不须要实现
  全局默认过滤器
  部分默认过滤器
  
自定义过滤器:依据需要本人实现,实现后需配置,而后能力用哦。
  全局过滤器:作用在所有路由上。
  部分过滤器:配置在具体路由下,只作用在以后路由上。

默认过滤器十好几个,常见如下:

过滤器名称 阐明 对应的类 父类
AddRequestHeader 对匹配上的申请加上Header AddRequestHeaderGatewayFilterFactory AbstractNameValueGatewayFilterFactory
AddRequestParameters 对匹配上的申请路由 AddRequestHeaderGatewayFilterFactory AbstractNameValueGatewayFilterFactory
AddResponseHeader 对从网关返回的响应增加Header AddResponseHeaderGatewayFilterFactory AbstractNameValueGatewayFilterFactory
StripPrefix 对匹配上的申请门路去除前缀 StripPrefixGatewayFilterFactory AbstractGatewayFilterFactory

3.3.2 默认过滤器的应用

所谓默认过滤器就是零碎自带的。有很多,这里简要阐明几个:(通过java配置,正文掉yaml配置

1)增加响应头

AddResponseHeaderGatewayFilterFactory 属于 GatewayFilter

对输入响应头设置属性,比方对输入的响应设置其头部属性名称为:X-Response-Default-MyName , 值为itheima

批改配置文件,配置如下:

spring:
  cloud:
    gateway:
     # 配置全局默认过滤器 作用在所有路由上,也可独自为某个路由配置
      default-filters:
      # 往响应过滤器中退出信息
        - AddResponseHeader=X-Response-Default-MyName,itheima

申请http://localhost:8001/driver/info/1,响应数据增加了X-Response-Default-MyName: itheima,如下图:

2)前缀解决

在我的项目中做开发对接接口的时候,咱们很多时候须要对立API门路,比方对立以/api开始的申请调用hailtaxi-driver服务,但实在服务接口地址又没有/api门路,咱们能够应用Gateway的过滤器解决申请门路。

在gateway中能够通过配置路由的过滤器StripPrefix实现映射门路中的前缀解决,咱们来应用一下该过滤器,再进一步做阐明。

    gateway:
      routes:
      - id: hailtaxi-driver
        uri: lb://hailtaxi-driver
        predicates:
        - Path=/api/driver/**
        filters:
          - StripPrefix=1

此处- StripPrefix=1示意实在申请地址是以后用户申请以/api开始的uri中去除第1个门路/api.

下面配置最终执行如下表:

配置 路由地址 拜访地址
StripPrefix=1 http://localhost:8001/api/dri… http://localhost:18081/driver…
StripPrefix=2 http://localhost:8001/api/sur… http://localhost:18081/driver…

有时候为了简化用户申请地址,比方用户申请http://localhost:8001/info/1咱们想对立路由到http://localhost:18081/driver/info/1,能够应用PrefixPath过滤器减少前缀。

    gateway:
      routes:
      - id: hailtaxi-driver
        uri: lb://hailtaxi-driver
        predicates:
        - Path=/**
        filters:
          - PrefixPath=/driver

下面配置最终执行如下表:

配置 路由地址 拜访地址
– PrefixPath=/driver http://localhost:8001/info/2 http://localhost:18081/driver…

3.3.3自定义GatewayFilter

1、实现GatewayFilter接口

GatewayFilter 个别作用在某一个路由上,须要实例化创立能力应用,部分过滤器须要实现接口GatewayFilter、Ordered

创立com.itheima.filter.PayFilter代码如下:

public class PayFilter implements GatewayFilter,Ordered {

    /***
     * 过滤器执行拦挡
     * @param exchange
     * @param chain
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println("GatewayFilter拦截器执行---pre-----PayFilter");
        return chain.filter(exchange).then(Mono.fromRunnable(()->{
            System.out.println("GatewayFilter拦截器执行---post-----PayFilter");
        }));
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

应用部分过滤器:(应用上面RouteLocator的时候,配置文件中的路由记得正文或删除)

/***
 * 路由配置
 * @param builder
 * @return
 */
 @Bean
    public RouteLocator routeLocator(RouteLocatorBuilder builder) {
        return builder.routes()
                .route("hailtaxi-driver", r -> r.path("/api/driver/**")
                        .and().cookie("username","itheima")
                        .and().header("token","123456")
                                .filters(f->f.filter(new PayFilter()).addResponseHeader("X-Response-Default-MyName", "itheima")
                                        .addRequestHeader("myheader", "1234567")
                                .stripPrefix(1)
                                )
                       // .and().method(HttpMethod.POST)
                        .uri("lb://hailtaxi-driver")
                        //.filter(new PayFilter())
                 )
                .route("hailtaxi-order", r -> r.path("/order/**").uri("lb://hailtaxi-order"))
                .route("hailtaxi-pay", r -> r.path("/pay/**").uri("lb://hailtaxi-pay"))
                .build();
    }

为了更好看到成果,咱们在RouterFilter增加System.out.println("GlobalFilter拦截器执行");再拜访测试。

拜访:http://localhost:8001/api/dri…,留神应用postman发送申请时增加申请头,增加cookie。

2、继承GatewayFilterFactory

如果定义部分过滤器,想在配置文件中进行配置来应用,能够继承AbstractGatewayFilterFactory<T>抽象类或者AbstractNameValueGatewayFilterFactory

整个体系结构为:

这两个抽象类的区别就是前者接管一个参数(像StripPrefix和咱们创立的这种),后者接管两个参数(像AddResponseHeader)

代码的编写能够参考:StripPrefixGatewayFilterFactoryAddRequestHeaderGatewayFilterFactory

过滤器工厂默认命名规定必须依照”名称”+GatewayFilterFactory`,如上StripPrefixGatewayFilterFactory的过滤器名称为StripPrefix

2.1、继承AbstractGatewayFilterFactory

需要:

在网关中对立领取形式,编写一个过滤器:PayMethodGatewayFilterFactory

1、编写过滤器

@Slf4j
@Component //肯定要将其交给spring容器治理
public class PayMethodGatewayFilterFactory extends AbstractGatewayFilterFactory<PayMethodGatewayFilterFactory.Config> {

    public PayMethodGatewayFilterFactory() {
        super(Config.class);
    }

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            String paymethod = config.getPayMethod();
            String msg = config.getMsg();
            log.info("PayMethodGatewayFilterFactory 加载到的配置信息为:{}---{}",paymethod,msg);
            //将paymethod增加到申请头中
            exchange.getRequest().mutate().header("paymethod",paymethod);
            return chain.filter(exchange);
        };
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return  Arrays.asList("payMethod","msg");//指定从yml中提前进去的配置信息填充到配置类中哪个属性,按规定配置
    }

    @Override
    public ShortcutType shortcutType() {
        return ShortcutType.DEFAULT;//默认规定
    }

    /**
     * 加载从yml中提取进去的配置信息
     */
    @Data
    public static class Config {
        private String payMethod;
        private String msg;
    }
}

2、配置文件中应用如下:

    gateway:
      #路由配置
      routes:
        #惟一标识符
        - id: hailtaxi-driver
          uri: lb://hailtaxi-driver
          #路由断言
          predicates:
            - Path=/driver/**
            - Cookie=username,itheima
            - Header=token,^(?!\d+$)[\da-zA-Z]+$
            - Method=GET,POST
            - Token=Authorization
          filters:
            - PayMethod=alipay,业务整合

再次测试,查看hailtaxi-driver 服务接管到申请后是否多了paymethod申请头信息

2.2、继承AbstractNameValueGatewayFilterFactory

间接查看AddRequestHeaderGatewayFilterFactory 源码,剖析即可!

3.3.4 自定义GlobalFilter

定义全局过滤器须要实现GlobalFilter,Ordered接口:

GlobalFilter:过滤器拦挡解决办法
Ordered:过滤器也有多个,这里次要定义过滤器执行程序,外面有个办法getOrder()会返回过滤器执行程序,返回值越小,越靠前执行

需要

咱们创立全局过滤器并实现常见业务用户权限校验,如果申请中有带有一个名字为token的申请参数,则认为申请无效放行,如果没有则拦挡提醒受权有效。

创立全局过滤器:com.itheima.filter.RouterFilter,代码如下:

@Component
public class RouterFilter implements GlobalFilter,Ordered {

    /***
     * 路由拦挡
     * @param exchange
     * @param chain
     * @return
     */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("RouterFilter----------------");
        //获取申请参数
        String token = exchange.getRequest().getQueryParams().getFirst("token");
        //如果token为空,则示意没有登录
        if(StringUtils.isEmpty(token)){
            //没登录,状态设置403
            exchange.getResponse().setStatusCode(HttpStatus.PAYLOAD_TOO_LARGE);
            //完结申请
            return exchange.getResponse().setComplete();
        }
        //放行
        return chain.filter(exchange);
    }

    /***
     * 拦截器程序
     * @return
     */
    @Override
    public int getOrder() {
        return 0;
    }
}

此时申请,咱们不携带token参数,成果如下:

咱们携带token参数则能够失常拜访,成果如下:

3.4 跨域配置

出于浏览器的同源策略限度。同源策略(Sameoriginpolicy)是一种约定,它是浏览器最外围也最根本的平安性能,如果短少了同源策略,则浏览器的失常性能可能都会受到影响。能够说Web是构建在同源策略根底之上的,浏览器只是针对同源策略的一种实现。同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。所谓同源(即指在同一个域)就是两个页面具备雷同的协定(protocol),主机(host)和端口号(port)。

在Spring Cloud Gateway中配置跨域是非常简单的,如上面application.yml所示:

    gateway:
      globalcors:
        corsConfigurations:
          '[/**]':
            allowedOrigins: "*"
            allowedMethods:
              - GET
              - POST
              - PUT

但如果波及到Cookie跨域,下面的配置就不失效了,如果波及到Cookie跨域,须要创立CorsWebFilter过滤器,代码如下:

/**
 * 配置跨域
 * @return
 */
@Bean
public CorsWebFilter corsFilter() {
    CorsConfiguration config = new CorsConfiguration();
    // cookie跨域
    config.setAllowCredentials(Boolean.TRUE);
    config.addAllowedMethod("*");
    config.addAllowedOrigin("*");
    config.addAllowedHeader("*");
    // 配置前端js容许拜访的自定义响应头
    config.addExposedHeader("Authorization");

    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
    source.registerCorsConfiguration("/**", config);
    return new CorsWebFilter(source);
}

3.5 限流

网关能够做很多的事件,比方,限流,当咱们的零碎 被频繁的申请的时候,就有可能 将零碎压垮,所以 为了解决这个问题,须要在每一个微服务中做限流操作,然而如果有了网关,那么就能够在网关零碎做限流,因为所有的申请都须要先通过网关零碎能力路由到微服务中。

3.5.1 漏桶算法解说

漏桶算法是常见的限流算法之一,咱们解说一下漏桶算法:

1)所有的申请在解决之前都须要拿到一个可用的令牌才会被解决;
2)依据限流大小,设置依照肯定的速率往桶里增加令牌;
3)桶设置最大的搁置令牌限度,当桶满时、新增加的令牌就被抛弃或者回绝;
4)申请达到后首先要获取令牌桶中的令牌,拿着令牌才能够进行其余的业务逻辑,解决完业务逻辑之后,将令牌间接删除;
5)令牌桶有最低限额,当桶中的令牌达到最低限额的时候,申请解决完之后将不会删除令牌,以此保障足够的限流

漏桶算法的实现,有很多技术,Guaua是其中之一,redis客户端也有其实现。

3.5.2 限流案例

spring cloud gateway 默认应用redis的RateLimter限流算法来实现,里面来简要实现一下:

1、引入依赖

首先须要引入redis的依赖:

<!--redis-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
    <version>2.2.1.RELEASE</version>
</dependency>

同时不要遗记Redis配置:

  redis:
    host: 127.0.0.1
    port: 6379

2、定义KeyResolver

在Application疏导类中增加如下代码,KeyResolver用于计算某一个类型的限流的KEY也就是说,能够通过KeyResolver来指定限流的Key。

咱们能够依据IP来限流,比方每个IP每秒钟只能申请一次,在GatewayApplication定义key的获取,获取客户端IP,将IP作为key,如下代码:

/***
 * IP限流
 * @return
 */
@Bean(name="ipKeyResolver")
public KeyResolver userKeyResolver() {
    return new KeyResolver() {
        @Override
        public Mono<String> resolve(ServerWebExchange exchange) {
            //获取近程客户端IP
            String hostName = exchange.getRequest().getRemoteAddress().getAddress().getHostAddress();
            System.out.println("hostName:"+hostName);
            return Mono.just(hostName);
        }
    };
}

在路由中配置如下:

    gateway:
      #路由配置
      routes:
        #惟一标识符
        - id: hailtaxi-driver
          uri: lb://hailtaxi-driver
          #路由断言
          predicates:
            - Path=/driver/**
            - Cookie=username,itheima
            - Header=token,^(?!\d+$)[\da-zA-Z]+$
            - Method=GET,POST
            - Token=Authorization
          filters:
            - PayMethod=alipay,业务整合
            - name: RequestRateLimiter #申请数限流 名字不能轻易写 ,应用默认的facatory
              args:
                key-resolver: "#{@ipKeyResolver}"
                redis-rate-limiter.replenishRate: 1
                redis-rate-limiter.burstCapacity: 1

参数阐明:

redis-rate-limiter.replenishRate是您心愿容许用户每秒执行多少申请,而不会抛弃任何申请。这是令牌桶填充的速率
redis-rate-limiter.burstCapacity是指令牌桶的容量,容许在一秒钟内实现的最大申请数,将此值设置为零将阻止所有申请。
key-resolver: “#{@ipKeyResolver}” 用于通过SPEL表达式来指定应用哪一个KeyResolver.

如上配置:
示意 一秒内,容许 一个申请通过,令牌桶的填充速率也是一秒钟增加一个令牌。
最大突发状况 也只容许 一秒内有一次申请,能够依据业务来调整 。

咱们申请http://localhost:8001/driver/info/1?token=aa执行测试,成果如下:

本文由传智教育博学谷 – 狂野架构师教研团队公布,转载请注明出处!

如果本文对您有帮忙,欢送关注和点赞;如果您有任何倡议也可留言评论或私信,您的反对是我保持创作的能源

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理