• Spring Cloud(九):Spring Cloud Gateway 断言、过滤器、限流、源码分析


    Spring Cloud Gateway 介绍

    在这里插入图片描述

    Spring Cloud Gateway 是Spring Cloud官方推出的第二代网关框架,定位于取代 Netflix Zuul。Spring Cloud Gateway 旨在为微服务架构提供一种简单且有效的 API 路由的管理方式,并基于 Filter 的方式提供网关的基本功能,例如说安全认证、监控、限流等等。

    Spring Cloud Gateway 是由 WebFlux + Netty + Reactor 实现的响应式的 API 网关。它不能在传统的 servlet 容器中工作,也不能构建成 war 包

    官网文档:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/

    Spring Cloud Gateway 的工作原理跟 Zuul 的差不多,最大的区别就是 Gateway 的 Filter 只有 pre 和 post 两种

    在这里插入图片描述
    客户端向 Spring Cloud Gateway 发出请求,如果请求与网关程序定义的路由匹配,则该请求就会被发送到网关 Web 处理程序,此时处理程序运行特定的请求过滤器链。

    过滤器之间用虚线分开的原因是过滤器可能会在发送代理请求的前后执行逻辑。所有 pre 过滤器逻辑先执行,然后执行代理请求;代理请求完成后,执行 post 过滤器逻辑。

    • 路由(route) : 路由是网关中最基础的部分,路由信息包括一个ID、一个目的URI、一组断言工厂、一组Filter组成
    • 断言(predicates) : Java8中的断言函数,SpringCloud Gateway中的断言函数类型是Spring5.0框架中的ServerWebExchange。断言函数允许开发者去定义匹配Http request中的任何信息,比如请求头和参数等。如果断言为真,则说明请求的URL和配置的路由匹配
    • 过滤器(Filter) : SpringCloud Gateway中的filter分为Gateway FilIer和Global Filter。Filter可以对请求和响应进行处理

    Spring Cloud Gateway 使用

    spring:
      application:
        name: gateway-demo
      #配置nacos注册中心地址
      cloud:
        nacos:
          discovery:
            server-addr: 127.0.0.1:8848
    
        gateway:
          #设置路由:路由id、路由到微服务的uri、断言
          routes:
            - id: order_route  #路由ID,全局唯一,建议配置服务名
              uri: lb://mall-order  #lb 整合负载均衡器ribbon,loadbalancer
              predicates:
                - Path=/order/**   # 断言,路径相匹配的进行路由 mall-order(ip:8020/api/v5) --> ip:8020/order/api/v5
                - Header=X-Request-Id, \d+
    
            - id: user_route   #路由ID,全局唯一,建议配置服务名
              uri: lb://mall-user  #lb 整合负载均衡器ribbon,loadbalancer
              predicates:
                - Path=/user/**   # 断言,路径相匹配的进行路由
                - Header=X-Request-Id, \d+
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    路由断言工厂(Route Predicate Factories)配置

    过滤器工厂( GatewayFilter Factories)配置

    GatewayFilter是网关中提供的一种过滤器,可以对进入网关的请求和微服务返回的响应做处理

    https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gatewayfilter-factories

    在这里插入图片描述

    自定义过滤器工厂

    继承AbstractNameValueGatewayFilterFactory且我们的自定义名称必须要以GatewayFilterFactory结尾并交给spring管理

    @Component
    @Slf4j
    public class CheckAuthGatewayFilterFactory extends AbstractNameValueGatewayFilterFactory {
    
        @Override
        public GatewayFilter apply(NameValueConfig config) {
    
            return (exchange, chain) -> {
                log.info("调用CheckAuthGatewayFilterFactory==="
                        + config.getName() + ":" + config.getValue());
                // TODO
                return chain.filter(exchange);
            };
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    gateway:
      routes:
        - id: order-route
          uri: lb://order-service
          predicates:
            - Path=/order/**
            - Header=X-Request-Id, \d+
          filters:
            - AddRequestHeader=X-Request-color, red
            - CheckAuth=mxgender,man
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    全局过滤器(Global Filters)配置

    全局过滤器的作用也是处理一切进入网关的请求和微服务响应,与GatewayFilter的作用一样。

    • GatewayFilter:网关过滤器,需要通过spring.cloud.routes.filters配置在具体的路由下,只作用在当前特定路由上,也可以通过配置spring.cloud.default-filters让它作用于全局路由上。
    • GlobalFilter:全局过滤器,不需要再配置文件中配置,作用在所有的路由上,最终通过GatewayFilterAdapter包装成GatewayFilterChain能够识别的过滤器。

    https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#global-filters

    在这里插入图片描述

    自定义全局过滤器

    自定义全局过滤器定义方式是实现GlobalFilter接口。每一个过滤器都必须指定一个int类型的order值,order值越小,过滤器优先级越高,执行顺序越靠前。GlobalFilter通过实现Ordered接口来指定order值

    @Component
    @Slf4j
    public class CheckAuthFilter implements GlobalFilter, Ordered {
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            //获取token
            String token = exchange.getRequest().getHeaders().getFirst("token");
            if (null == token) {
                log.info("token is null");
                ServerHttpResponse response = exchange.getResponse();
                response.getHeaders().add("Content-Type",
                        "application/json;charset=UTF-8");
                // 401 用户没有访问权限
                response.setStatusCode(HttpStatus.UNAUTHORIZED);
                byte[] bytes = HttpStatus.UNAUTHORIZED.getReasonPhrase().getBytes();
                DataBuffer buffer = response.bufferFactory().wrap(bytes);
                // 请求结束,不继续向下请求
                return response.writeWith(Mono.just(buffer));
            }
            //TODO 校验token进行身份认证
            log.info("校验token");
            return chain.filter(exchange);
        }
    
        @Override
        public int getOrder() {
            return 2;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    在这里插入图片描述

    超时

    spring:
      cloud:
        gateway:
          httpclient:
            connect-timeout: 1000
            response-timeout: 5s
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    日志

    https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#reactor-netty-access-logs

    -Dreactor.netty.http.server.accessLogEnabled=true

    跨域

    通过yml配置的方式
    https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#cors-configuration

    spring:
      cloud:
        gateway:
        globalcors:
          cors-configurations:
            '[/**]':
              allowedOrigins: "*"
              allowedMethods:
              - GET
              - POST
              - DELETE
              - PUT
              - OPTION
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    通过java配置的方式

    @Configuration
    public class CorsConfig {
        @Bean
        public CorsWebFilter corsFilter() {
            CorsConfiguration config = new CorsConfiguration();
            config.addAllowedMethod("*");
            config.addAllowedOrigin("*");
            config.addAllowedHeader("*");
    
            UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
            source.registerCorsConfiguration("/**", config);
    
            return new CorsWebFilter(source);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    Spring Cloud Gateway 限流

    Gateway基于redis+lua脚本限流

    spring cloud alibaba官方提供了RequestRateLimiter过滤器工厂,基于redis+lua脚本方式采用令牌桶算法实现了限流

    https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#the-requestratelimiter-gatewayfilter-factory

    在这里插入图片描述

    redis-pom

    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-data-redis-reactiveartifactId>
    dependency>
    
    <dependency>
        <groupId>org.apache.commonsgroupId>
        <artifactId>commons-pool2artifactId>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    redis-applicaiton

    spring:
      redis:
        host: redis.localhost.com
        password: xxxxxxx
        port: 6379
        database: 0
        timeout: 5000
        lettuce:
          pool:
            max-active: 8
            max-wait: 10000
            max-idle: 100
            min-idle: 10
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    gateway-application

    spring:
      cloud:
        gateway:
          routes:
            - id: order-route
              uri: lb://order-service
              predicates:
                - Path=/order/**
                - Header=X-Request-Id, \d+
              filters:
                - AddRequestHeader=X-Request-color, red
                - CheckAuth=mxgender,man
                - name: RequestRateLimiter
                  args:
                    redis-rate-limiter.replenishRate: 1 #令牌桶每秒填充速率
                    redis-rate-limiter.burstCapacity: 2 #令牌桶的总容量
                    key-resolver: "#{@keyResolver}" #使用SpEL表达式,从Spring容器中获取Bean对象
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    keyResolver-bean

    @Configuration
    public class RateLimiterConfig {
        @Bean
        KeyResolver keyResolver() {
            //url限流
            return exchange -> Mono.just(exchange.getRequest().getURI().getPath());
            //参数限流
    //        return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("user"));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    lua 脚本
    在这里插入图片描述

    Gateway整合sentinel限流

    从 1.6.0 版本开始,Sentinel 提供了 Spring Cloud Gateway 的适配模块,可以提供两种资源维度的限流:

    • route 维度:即在 Spring 配置文件中配置的路由条目,资源名为对应的 routeId
    • 自定义 API 维度:用户可以利用 Sentinel 提供的 API 来自定义一些 API 分组

    https://github.com/alibaba/Sentinel/wiki/网关限流

    pom

    
     <dependencies>
    
         <dependency>
             <groupId>org.springframework.cloudgroupId>
             <artifactId>spring-cloud-starter-gatewayartifactId>
         dependency>
    
         <dependency>
             <groupId>com.alibaba.cloudgroupId>
             <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
         dependency>
    
         <dependency>
             <groupId>com.alibaba.cloudgroupId>
             <artifactId>spring-cloud-starter-alibaba-nacos-configartifactId>
         dependency>
    
         <dependency>
             <groupId>com.alibaba.cloudgroupId>
             <artifactId>spring-cloud-alibaba-sentinel-gatewayartifactId>
         dependency>
    
         <dependency>
             <groupId>com.alibaba.cloudgroupId>
             <artifactId>spring-cloud-starter-alibaba-sentinelartifactId>
         dependency>
    
         <dependency>
             <groupId>com.alibaba.cspgroupId>
             <artifactId>sentinel-datasource-nacosartifactId>
         dependency>
    
         <dependency>
             <groupId>org.projectlombokgroupId>
             <artifactId>lombokartifactId>
         dependency>
     dependencies>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    applicaiton

    server:
      port: 8033
    spring:
      application:
        name: sentinel-gateway-demo
      main:
        allow-bean-definition-overriding: true
      #配置nacos注册中心地址
      cloud:
        nacos:
          discovery:
            server-addr: nacos.mendd.com:8848
            namespace: dev
            ephemeral: true
    
        sentinel:
          transport:
            # 添加sentinel的控制台地址
            dashboard: 127.0.0.1:8080
          datasource:
            gateway-flow-rules:
              nacos:
                server-addr: nacos.localhost.com:8848
                dataId: ${spring.application.name}-gateway-flow-rules
                groupId: SENTINEL_GROUP
                data-type: json
                rule-type: gw-flow
            gateway-api-rules:
              nacos:
                server-addr: nacos.localhost.com:8848
                dataId: ${spring.application.name}-gateway-api-rules
                groupId: SENTINEL_GROUP
                data-type: json
                rule-type: gw-api-group
            degrade-rules:
              nacos:
                server-addr: nacos.localhost.com:8848
                dataId: ${spring.application.name}-degrade-rules
                groupId: SENTINEL_GROUP
                data-type: json
                rule-type: degrade
            system-rules:
              nacos:
                server-addr: nacos.localhost.com:8848
                dataId: ${spring.application.name}-system-rules
                groupId: SENTINEL_GROUP
                data-type: json
                rule-type: system
    
        gateway:
          #设置路由:路由id、路由到微服务的uri、断言
          routes:
            - id: order_route  #路由ID,全局唯一,建议配合服务名
              uri: lb://order-service  #lb 整合负载均衡器ribbon,loadbalancer
              predicates:
                - Path=/order/**
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    http://localhost:8033/order/testgateway/1?name=mx

    Sentinel网关流控实现原理

    当通过 GatewayRuleManager 加载网关流控规则(GatewayFlowRule)时,无论是否针对请求属性进行限流,Sentinel 底层都会将网关流控规则转化为热点参数规则(ParamFlowRule),存储在 GatewayRuleManager 中,与正常的热点参数规则相隔离。转换时 Sentinel 会根据请求属性配置,为网关流控规则设置参数索引(idx),并同步到生成的热点参数规则中。

    外部请求进入 API Gateway 时会经过 Sentinel 实现的 filter,其中会依次进行 路由/API 分组匹配、请求属性解析和参数组装。Sentinel 会根据配置的网关流控规则来解析请求属性,并依照参数索引顺序组装参数数组,最终传入 SphU.entry(res, args) 中。Sentinel API Gateway Adapter Common 模块向 Slot Chain 中添加了一个 GatewayFlowSlot,专门用来做网关规则的检查。GatewayFlowSlot 会从 GatewayRuleManager 中提取生成的热点参数规则,根据传入的参数依次进行规则检查。若某条规则不针对请求属性,则会在参数最后一个位置置入预设的常量,达到普通流控的效果。

    在这里插入图片描述

    源码

    在这里插入图片描述

    适配器模式典范 GlobalFilter -> GatewayFilter = GatewayFilterAdapter

    private static List<GatewayFilter> loadFilters(List<GlobalFilter> filters) {
    	return filters.stream().map(filter -> {
    		GatewayFilterAdapter gatewayFilter = new GatewayFilterAdapter(filter);
    		if (filter instanceof Ordered) {
    			int order = ((Ordered) filter).getOrder();
    			return new OrderedGatewayFilter(gatewayFilter, order);
    		}
    		return gatewayFilter;
    	}).collect(Collectors.toList());
    }
    
    
    private static class GatewayFilterAdapter implements GatewayFilter {
    
    	private final GlobalFilter delegate;
    
    	GatewayFilterAdapter(GlobalFilter delegate) {
    		this.delegate = delegate;
    	}
    
    	@Override
    	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
    		return this.delegate.filter(exchange, chain);
    	}
    
    	@Override
    	public String toString() {
    		final StringBuilder sb = new StringBuilder("GatewayFilterAdapter{");
    		sb.append("delegate=").append(delegate);
    		sb.append('}');
    		return sb.toString();
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
  • 相关阅读:
    ARC129E Yet Another Minimization 题解 【网络流笔记】
    携职教育:退费暂停!2022年中级会计职称考试停考通知公布
    狂神redis笔记10
    数据结构学习笔记——查找算法
    【虚拟机开不了】linux、centOS虚拟机出现entering emergency mode解决方案
    内核移植学习
    elasticsearch聚合之bucket terms聚合
    c语言练习44:深入理解strstr
    Android 学习之多状态布局的一种实现方案
    3d模型里地毯的材质怎么赋予?---模大狮模型网
  • 原文地址:https://blog.csdn.net/menxu_work/article/details/127792146