zoukankan      html  css  js  c++  java
  • Spring Cloud Gateway 扩展支持动态限流

    之前分享过 一篇 《Spring Cloud Gateway 原生的接口限流该怎么玩》, 核心是依赖Spring Cloud Gateway 默认提供的限流过滤器来实现

    原生RequestRateLimiter 的不足

    • 配置方式
    spring:
      cloud:
        gateway:
          routes:
          - id: requestratelimiter_route
            uri: lb://pigx-upms
            order: 10000
            predicates:
            - Path=/admin/**
            filters:
            - name: RequestRateLimiter
              args:
                redis-rate-limiter.replenishRate: 1  
                redis-rate-limiter.burstCapacity: 3
                key-resolver: "#{@remoteAddrKeyResolver}" #SPEL表达式去的对应的bean
            - StripPrefix=1
    
    • RequestRateLimiterGatewayFilterFactory
    public GatewayFilter apply(Config config) {
    	KeyResolver resolver = getOrDefault(config.keyResolver, defaultKeyResolver);
    	RateLimiter<Object> limiter = getOrDefault(config.rateLimiter,
    			defaultRateLimiter);
    	boolean denyEmpty = getOrDefault(config.denyEmptyKey, this.denyEmptyKey);
    	HttpStatusHolder emptyKeyStatus = HttpStatusHolder
    			.parse(getOrDefault(config.emptyKeyStatus, this.emptyKeyStatusCode));
    
    	return (exchange, chain) -> {
    				return exchange.getResponse().setComplete();
    			});
    		});
    	};
    }
    
    • 在实际生产过程中,必定不能满足我们的需求

      生产中路由信息是保存数据库持久化或者配置中心,RequestRateLimiterGatewayFilterFactory 并不能随着持久化数据的改变而动态改变限流参数,不能做到实时根据流量来改变流量阈值

    Sentinel Spring Cloud Gateway 流控支持

    Sentinel 是什么?

    随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性,分布式系统的流量防卫兵。
    从 1.6.0 版本开始,Sentinel 提供了 Spring Cloud Gateway 的适配模块,可以提供两种资源维度的限流:
    route 维度:即在 Spring 配置文件中配置的路由条目,资源名为对应的 routeId
    自定义 API 维度:用户可以利用 Sentinel 提供的 API 来自定义一些 API 分组

    pom 依赖

    <!--Spring Cloud Alibaba 封装的 sentinel 模块-->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
    </dependency>
    
    <!--使用nacos 保存限流规则-->
    <dependency>
        <groupId>com.alibaba.csp</groupId>
        <artifactId>sentinel-datasource-nacos</artifactId>
    </dependency>
    

    配置本地路由规则及其sentinel数据源

    spring:
      application:
        name: sentinel-spring-cloud-gateway
      cloud:
        gateway:
          enabled: true
          discovery:
            locator:
              lower-case-service-id: true
          routes:
          - id: pigx_route
            uri: https://api.readhub.cn
            predicates:
            - Path=/topic/**
        sentinel:
          datasource.ds1.nacos:
            server-addr: 127.0.0.1:8848
            data-id: gw-flow
            group-id: DEFAULT_GROUP
            ruleType: gw-api-group
          filter:
            enabled: true
    

    配置nacos数据源中的限流策略

    • 常用限流策略 常量
    以客户端IP作为限流因子
    public static final int PARAM_PARSE_STRATEGY_CLIENT_IP = 0;
    以客户端HOST作为限流因子
    public static final int PARAM_PARSE_STRATEGY_HOST = 1;
    以客户端HEADER参数作为限流因子
    public static final int PARAM_PARSE_STRATEGY_HEADER = 2;
    以客户端请求参数作为限流因子
    public static final int PARAM_PARSE_STRATEGY_URL_PARAM = 3;
    以客户端请求Cookie作为限流因子
    public static final int PARAM_PARSE_STRATEGY_COOKIE = 4;
    
    • 核心源码解析 SentinelGatewayFilter

    sentinel通过扩展Gateway的过滤器,通过选择的不同GatewayParamParser 过处理请求限流因子和数据源中的配置进行比较
    源码如下:

    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        Route route = exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
    
        Mono<Void> asyncResult = chain.filter(exchange);
        if (route != null) {
            String routeId = route.getId();
            Object[] params = paramParser.parseParameterFor(routeId, exchange,
                r -> r.getResourceMode() == SentinelGatewayConstants.RESOURCE_MODE_ROUTE_ID);
            String origin = Optional.ofNullable(GatewayCallbackManager.getRequestOriginParser())
                .map(f -> f.apply(exchange))
                .orElse("");
            asyncResult = asyncResult.transform(
                new SentinelReactorTransformer<>(new EntryConfig(routeId, EntryType.IN,
                    1, params, new ContextConfig(contextName(routeId), origin)))
            );
        }
    
        Set<String> matchingApis = pickMatchingApiDefinitions(exchange);
        for (String apiName : matchingApis) {
            Object[] params = paramParser.parseParameterFor(apiName, exchange,
                r -> r.getResourceMode() == SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME);
            asyncResult = asyncResult.transform(
                new SentinelReactorTransformer<>(new EntryConfig(apiName, EntryType.IN, 1, params))
            );
        }
    
        return asyncResult;
    }
    
    

    效果演示

    • 以上nacos 配置为 每秒只能通过5个请求,我们使用jmeter 4.0 来并发10个线程测试一下


    • 通过上图可以结果证明sentinel限流确实有效

    动态修改限流参数

    • sentinel-datasource-nacos 作为sentinel的数据源,可以从如上 nacos 管理台实时刷新限流参数及其阈值
    • 目前sentinel dashboard 1.6.2 暂未实现gateway 流控图形化控制 , 1.7.0 会增加此功能

    总结

    欢迎关注我们获得更多的好玩JavaEE 实践

    项目推荐: Spring Cloud 、Spring Security OAuth2的RBAC权限管理系统 欢迎关注

  • 相关阅读:
    程序员之道——编程也是一门艺术
    Spring动态获取IoC容器中管理的Bean
    SVN使用教程之——分支、合并
    WebSphere从Windows迁移至Linux出现org.dom4j.DocumentException异常:Nested exception: prolog 中不允许有内容
    Spring动态获取IoC容器中管理的Bean
    com.microsoft.sqlserver.jdbc.SQLServerException: 将截断字符串或二进制数据
    IMP导入数据 出现ORA01691问题 解决办法
    jquery对象与Dom对象的相互转化
    json<>js
    photoshop快捷键
  • 原文地址:https://www.cnblogs.com/leng-leng/p/13064899.html
Copyright © 2011-2022 走看看