乐趣区

关于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 执行测试,成果如下:

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

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

退出移动版