sentinel源码分析-07GatewayFlowSlot网关限流

SpringCloudGateway源码(四)限流组件 本章分析了SpringCloudGateway自带的限流组件。scg通过#apply为每个Route路由创建一个限流过滤器GatewayFilter。限流过滤器主要包含两个可扩展组件:1):解析资源key,比如基于uri限流、基于uri+用户id限流等,提供默认实现PrincipalNameKeyResolver,基于java.security.Principal#getName获取限流资源key;2) 阅读详情

GatewayFlowSlot网关限流

sentinel支持api网关限流策略,其中包括 Spring Cloud Gateway。对于api网关限流,sentinel支持针对不同 route 或自定义的 API 分组进行限流,但是默认不支持 URL 粒度。

自定义分组可以通过api实现或通过dashboard页面配置,用户可以通过自定义分组将一些需要限流的url添加进来,支持精确匹配、前缀匹配、正则匹配,然后针对不同分组设置对应的限流规则,即可进行限流

通过api设置分组

Set<ApiDefinition> definitions = new HashSet<>();
        ApiDefinition api1 = new ApiDefinition("some_customized_api")
            .setPredicateItems(new HashSet<ApiPredicateItem>() {{
                add(new ApiPathPredicateItem().setPattern("/ahas"));
                add(new ApiPathPredicateItem().setPattern("/product/**")
                    .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
            }});
        ApiDefinition api2 = new ApiDefinition("another_customized_api")
            .setPredicateItems(new HashSet<ApiPredicateItem>() {{
                add(new ApiPathPredicateItem().setPattern("/**")
                    .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
            }});
        definitions.add(api1);
        definitions.add(api2);
        GatewayApiDefinitionManager.loadApiDefinitions(definitions);

这里设置了两个分组,并设置了对应匹配分组的规则

或者我们可以通dashboard设置分组,接口在GatewayApiController类,我们着重看一下保存逻辑
在这里插入图片描述

@PostMapping("/save.json")
    public Result<ApiDefinitionEntity> updateApi(HttpServletRequest request, @RequestBody UpdateApiReqVo reqVo) {
        AuthService.AuthUser authUser = authService.getAuthUser(request);

        String app = reqVo.getApp();
        if (StringUtil.isBlank(app)) {
            return Result.ofFail(-1, "app can't be null or empty");
        }

        authUser.authTarget(app, AuthService.PrivilegeType.WRITE_RULE);

        Long id = reqVo.getId();
        if (id == null) {
            return Result.ofFail(-1, "id can't be null");
        }

        ApiDefinitionEntity entity = repository.findById(id);
        if (entity == null) {
            return Result.ofFail(-1, "api does not exist, id=" + id);
        }

        // 匹配规则列表
        List<ApiPredicateItemVo> predicateItems = reqVo.getPredicateItems();
        if (CollectionUtils.isEmpty(predicateItems)) {
            return Result.ofFail(-1, "predicateItems can't empty");
        }

        List<ApiPredicateItemEntity> predicateItemEntities = new ArrayList<>();
        for (ApiPredicateItemVo predicateItem : predicateItems) {
            ApiPredicateItemEntity predicateItemEntity = new ApiPredicateItemEntity();

            // 匹配模式
            int matchStrategy = predicateItem.getMatchStrategy();
            if (!Arrays.asList(URL_MATCH_STRATEGY_EXACT, URL_MATCH_STRATEGY_PREFIX, URL_MATCH_STRATEGY_REGEX).contains(matchStrategy)) {
                return Result.ofFail(-1, "Invalid matchStrategy: " + matchStrategy);
            }
            predicateItemEntity.setMatchStrategy(matchStrategy);

            // 匹配串
            String pattern = predicateItem.getPattern();
            if (StringUtil.isBlank(pattern)) {
                return Result.ofFail(-1, "pattern can't be null or empty");
            }
            predicateItemEntity.setPattern(pattern);

            predicateItemEntities.add(predicateItemEntity);
        }
        entity.setPredicateItems(new LinkedHashSet<>(predicateItemEntities));

        Date date = new Date();
        entity.setGmtModified(date);

        try {
            entity = repository.save(entity);
        } catch (Throwable throwable) {
            logger.error("update gateway api error:", throwable);
            return Result.ofThrowable(-1, throwable);
        }

        if (!publishApis(app, entity.getIp(), entity.getPort())) {
            logger.warn("publish gateway apis fail after update");
        }

        return Result.ofSuccess(entity);
    }

这里可以看到就是针对不同属性的处理,然后将配置的自定义分组推送到客户端

private boolean publishApis(String app, String ip, Integer port) {
    List<ApiDefinitionEntity> apis = repository.findAllByMachine(MachineInfo.of(app, ip, port));
    return sentinelApiClient.modifyApis(app, ip, port, apis);
}

 public boolean modifyApis(String app, String ip, int port, List<ApiDefinitionEntity> apis) {
    if (apis == null) {
        return true;
    }

    try {
        AssertUtil.notEmpty(app, "Bad app name");
        AssertUtil.notEmpty(ip, "Bad machine IP");
        AssertUtil.isTrue(port > 0, "Bad machine port");
        String data = JSON.toJSONString(
                apis.stream().map(r -> r.toApiDefinition()).collect(Collectors.toList()));
        Map<String, String> params = new HashMap<>(2);
        params.put("data", data);
        String result = executeCommand(app, ip, port, MODIFY_GATEWAY_API_PATH, params, true).get();
        logger.info("Modify gateway apis: {}", result);
        return true;
    } catch (Exception e) {
        logger.warn("Error when modifying gateway apis", e);
        return false;
    }
}

发送gateway/updateApiDefinitions请求,客户端会处理逻辑在UpdateGatewayApiDefinitionGroupCommandHandler

@Override
public CommandResponse<String> handle(CommandRequest request) {
    String data = request.getParam("data");
    if (StringUtil.isBlank(data)) {
        return CommandResponse.ofFailure(new IllegalArgumentException("Bad data"));
    }
    try {
        data = URLDecoder.decode(data, "utf-8");
    } catch (Exception e) {
        RecordLog.info("Decode gateway API definition data error", e);
        return CommandResponse.ofFailure(e, "decode gateway API definition data error");
    }

    RecordLog.info("[API Server] Receiving data change (type: gateway API definition): {0}", data);

    String result = SUCCESS_MSG;

    Set<ApiDefinition> apiDefinitions = parseJson(data);
    GatewayApiDefinitionManager.loadApiDefinitions(apiDefinitions);

    return CommandResponse.ofSuccess(result);
}

这里解析页面下发的自定义分组配置然调用loadApiDefinitions,创建自定义分组然后保存到API_MAP中
在这里插入图片描述
同样页面在配置网关流控规则时会被客户端UpdateGatewayRuleCommandHandler类处理

@Override
public CommandResponse<String> handle(CommandRequest request) {
    String data = request.getParam("data");
    if (StringUtil.isBlank(data)) {
        return CommandResponse.ofFailure(new IllegalArgumentException("Bad data"));
    }
    try {
        data = URLDecoder.decode(data, "utf-8");
    } catch (Exception e) {
        RecordLog.info("Decode gateway rule data error", e);
        return CommandResponse.ofFailure(e, "decode gateway rule data error");
    }

    RecordLog.info(String.format("[API Server] Receiving rule change (type: gateway rule): %s", data));

    String result = SUCCESS_MSG;
    List<GatewayFlowRule> flowRules = JSONArray.parseArray(data, GatewayFlowRule.class);
    //设置网关流控规则
    GatewayRuleManager.loadRules(new HashSet<>(flowRules));
    return CommandResponse.ofSuccess(result);
}

GatewayRuleManager负责管理网关流控规则,通过loadRules保存网关流控规则,触发GatewayRulePropertyListener回调方法

@Override
public void configUpdate(Set<GatewayFlowRule> conf) {
    applyGatewayRuleInternal(conf);
    RecordLog.info("[GatewayRuleManager] Gateway flow rules received: " + GATEWAY_RULE_MAP);
}

@Override
public void configLoad(Set<GatewayFlowRule> conf) {
    applyGatewayRuleInternal(conf);
    RecordLog.info("[GatewayRuleManager] Gateway flow rules loaded: " + GATEWAY_RULE_MAP);
}

将网关流控规则转换为热点参数规则

private synchronized void applyGatewayRuleInternal(Set<GatewayFlowRule> conf) {
    //删除规则
    if (conf == null || conf.isEmpty()) {
        applyToConvertedParamMap(new HashSet<ParamFlowRule>());
        GATEWAY_RULE_MAP.clear();
        return;
    }
    //网关流控集合
    Map<String, Set<GatewayFlowRule>> gatewayRuleMap = new ConcurrentHashMap<>();
    //有参规则时候设置的下标
    Map<String, Integer> idxMap = new HashMap<>();
    //热点参数规则集合
    Set<ParamFlowRule> paramFlowRules = new HashSet<>();
    //存放没有设置热点参数的规则
    Map<String, List<GatewayFlowRule>> noParamMap = new HashMap<>();

    for (GatewayFlowRule rule : conf) {
        //校验规则失败直接跳过
        if (!isValidRule(rule)) {
            RecordLog.warn("[GatewayRuleManager] Ignoring invalid rule when loading new rules: " + rule);
            continue;
        }
        //资源名称
        String resourceName = rule.getResource();
        //没有热点参数
        if (rule.getParamItem() == null) {
            // Cache the rules with no parameter config, then skip.
            List<GatewayFlowRule> noParamList = noParamMap.get(resourceName);
            if (noParamList == null) {
                noParamList = new ArrayList<>();
                noParamMap.put(resourceName, noParamList);
            }
            noParamList.add(rule);
        } else {
            //每种资源对于热点参数索引值
            int idx = getIdxInternal(idxMap, resourceName);
            // Convert to parameter flow rule.
            //将gatewayRule转化为paramRule,并添加到热点参数规则集合中
            if (paramFlowRules.add(GatewayRuleConverter.applyToParamRule(rule, idx))) {
                //更新热点参数索引值
                idxMap.put(rule.getResource(), idx + 1);
            }
            cacheRegexPattern(rule.getParamItem());
        }
        // Apply to the gateway rule map.
        //保存所有的规则
        Set<GatewayFlowRule> ruleSet = gatewayRuleMap.get(resourceName);
        if (ruleSet == null) {
            ruleSet = new HashSet<>();
            gatewayRuleMap.put(resourceName, ruleSet);
        }
        ruleSet.add(rule);
    }
    // Handle non-param mode rules.
    //处理没有设置热点参数的规则
    for (Map.Entry<String, List<GatewayFlowRule>> e : noParamMap.entrySet()) {
        List<GatewayFlowRule> rules = e.getValue();
        if (rules == null || rules.isEmpty()) {
            continue;
        }
        for (GatewayFlowRule rule : rules) {
            int idx = getIdxInternal(idxMap, e.getKey());
            // Always use the same index (the last position).
            //同样也会转换为paramFlowRule,但是参数索引值相同
            paramFlowRules.add(GatewayRuleConverter.applyNonParamToParamRule(rule, idx));
        }
    }

    applyToConvertedParamMap(paramFlowRules);

    GATEWAY_RULE_MAP.clear();
    GATEWAY_RULE_MAP.putAll(gatewayRuleMap);
}

private void applyToConvertedParamMap(Set<ParamFlowRule> paramFlowRules) {
    Map<String, List<ParamFlowRule>> newRuleMap = ParamFlowRuleUtil.buildParamRuleMap(
        new ArrayList<>(paramFlowRules));
    if (newRuleMap == null || newRuleMap.isEmpty()) {
        // No parameter flow rules, so clear all the metrics.
        //清空所有统计信息
        for (String resource : CONVERTED_PARAM_RULE_MAP.keySet()) {
            ParameterMetricStorage.clearParamMetricForResource(resource);
        }
        RecordLog.info("[GatewayRuleManager] No gateway rules, clearing parameter metrics of previous rules");
        CONVERTED_PARAM_RULE_MAP.clear();
        return;
    }

    // Clear unused parameter metrics.
    //删除无用的参数统计信息
    Set<String> previousResources = CONVERTED_PARAM_RULE_MAP.keySet();
    for (String resource : previousResources) {
        if (!newRuleMap.containsKey(resource)) {
            ParameterMetricStorage.clearParamMetricForResource(resource);
        }
    }

    // Apply to converted rule map.
    CONVERTED_PARAM_RULE_MAP.clear();
    CONVERTED_PARAM_RULE_MAP.putAll(newRuleMap);

    RecordLog.info("[GatewayRuleManager] Converted internal param rules: " + CONVERTED_PARAM_RULE_MAP);
}

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

将有参网关限流规则转换为热点参数限流规则,如果pattern为空则针对所有热点参数都做流控,如果不为空则只有匹配的参数才做流控,否则直接放行

static ParamFlowRule applyToParamRule(/*@Valid*/ GatewayFlowRule gatewayRule, int idx) {
    ParamFlowRule paramRule = new ParamFlowRule(gatewayRule.getResource())
        .setCount(gatewayRule.getCount())
        .setGrade(gatewayRule.getGrade())
        .setDurationInSec(gatewayRule.getIntervalSec())
        .setBurstCount(gatewayRule.getBurst())
        .setControlBehavior(gatewayRule.getControlBehavior())
        .setMaxQueueingTimeMs(gatewayRule.getMaxQueueingTimeoutMs())
        .setParamIdx(idx);
    //参数限流配置
    GatewayParamFlowItem gatewayItem = gatewayRule.getParamItem();
    // Apply the current idx to gateway rule item.
    //设置index下标,index自增,用户热点参数
    gatewayItem.setIndex(idx);
    // Apply for pattern-based parameters.
    // 针对有GatewayParamFlowItem.pattern
    String valuePattern = gatewayItem.getPattern();
    //设置例外
    if (valuePattern != null) {
        paramRule.getParamFlowItemList().add(generateNonMatchPassParamItem());
    }
    return paramRule;
}

将无参网关流控规则,转换为热点参数规则

static ParamFlowRule applyNonParamToParamRule(/*@Valid*/ GatewayFlowRule gatewayRule, int idx) {
    return new ParamFlowRule(gatewayRule.getResource())
        .setCount(gatewayRule.getCount())
        .setGrade(gatewayRule.getGrade())
        .setDurationInSec(gatewayRule.getIntervalSec())
        .setBurstCount(gatewayRule.getBurst())
        .setControlBehavior(gatewayRule.getControlBehavior())
        .setMaxQueueingTimeMs(gatewayRule.getMaxQueueingTimeoutMs())
        .setParamIdx(idx);
}

这里仅仅是属性的copy,无参网关流控规则最主要特点是,转换后热点规则的idx参数下标始终保持不变,是参数列表的最后一个元素。

网关接入限流规则通过SentinelGatewayFilter

@Override
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) {
        //路由id
        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)))
        );
    }
    //匹配用户自定义的限流Api分组
    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;
}

解析获取热点参数

public Object[] parseParameterFor(String resource, T request, Predicate<GatewayFlowRule> rulePredicate) {
    if (StringUtil.isEmpty(resource) || request == null || rulePredicate == null) {
        return new Object[0];
    }
    Set<GatewayFlowRule> gatewayRules = new HashSet<>();
    Set<Boolean> predSet = new HashSet<>();
    boolean hasNonParamRule = false;
    for (GatewayFlowRule rule : GatewayRuleManager.getRulesForResource(resource)) {
        //设置了热点数据限流
        if (rule.getParamItem() != null) {
            gatewayRules.add(rule);
            predSet.add(rulePredicate.test(rule));
        } else {
            hasNonParamRule = true;
        }
    }
    if (!hasNonParamRule && gatewayRules.isEmpty()) {
        return new Object[0];
    }
    //route:规则中设置了针对Api分组
    if (predSet.size() > 1 || predSet.contains(false)) {
        return new Object[0];
    }
    int size = hasNonParamRule ? gatewayRules.size() + 1 : gatewayRules.size();
    //热点参数数组
    Object[] arr = new Object[size];
    for (GatewayFlowRule rule : gatewayRules) {
        GatewayParamFlowItem paramItem = rule.getParamItem();
        int idx = paramItem.getIndex();
        //解析获取参数值
        String param = parseInternal(paramItem, request);
        arr[idx] = param;
    }
    //最后一个设置默认值
    if (hasNonParamRule) {
        arr[size - 1] = SentinelGatewayConstants.GATEWAY_DEFAULT_PARAM;
    }
    return arr;
}

这里首先根据资源名称配合的route或者资源资源分组名称获取所有的网关流控规则,然后判断这些规则是否包含没有参数的规则,然后设置返回参数数组长度。解析设置的参数放入参数数组,如果有没有参数的规则,则将参数数组的最后一位设置成默认参数$D,为了进行资源的所有无参数例外项的热点规则流控校验

private String parseInternal(GatewayParamFlowItem item, T request) {
    switch (item.getParseStrategy()) {
        //请求源ip
        case SentinelGatewayConstants.PARAM_PARSE_STRATEGY_CLIENT_IP:
            return parseClientIp(item, request);
            //host
        case SentinelGatewayConstants.PARAM_PARSE_STRATEGY_HOST:
            return parseHost(item, request);
            //请求header中取值
        case SentinelGatewayConstants.PARAM_PARSE_STRATEGY_HEADER:
            return parseHeader(item, request);
            //请求url中取值
        case SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM:
            return parseUrlParameter(item, request);
            //cookie中取值
        case SentinelGatewayConstants.PARAM_PARSE_STRATEGY_COOKIE:
            return parseCookie(item, request);
        default:
            return null;
    }
}
private String parseClientIp(/*@Valid*/ GatewayParamFlowItem item, T request) {
    String clientIp = requestItemParser.getRemoteAddress(request);
    String pattern = item.getPattern();
    if (StringUtil.isEmpty(pattern)) {
        return clientIp;
    }
    return parseWithMatchStrategyInternal(item.getMatchStrategy(), clientIp, pattern);
}

private String parseWithMatchStrategyInternal(int matchStrategy, String value, String pattern) {
    if (value == null) {
        return null;
    }
    switch (matchStrategy) {
        //精确匹配
        case SentinelGatewayConstants.PARAM_MATCH_STRATEGY_EXACT:
            return value.equals(pattern) ? value : SentinelGatewayConstants.GATEWAY_NOT_MATCH_PARAM;
            //子串
        case SentinelGatewayConstants.PARAM_MATCH_STRATEGY_CONTAINS:
            return value.contains(pattern) ? value : SentinelGatewayConstants.GATEWAY_NOT_MATCH_PARAM;
            //正则
        case SentinelGatewayConstants.PARAM_MATCH_STRATEGY_REGEX:
            Pattern regex = GatewayRegexCache.getRegexPattern(pattern);
            if (regex == null) {
                return value;
            }
            return regex.matcher(value).matches() ? value : SentinelGatewayConstants.GATEWAY_NOT_MATCH_PARAM;
        default:
            return value;
    }
}

解析参数支持请求源ip、host、header中取值、url中取值、cookie中取值。如果没有设置pattern则直接返回取值结果,如果设置pattern则可能是针对某类参数生效,支持3种模式精确匹配、子串匹配和正则匹配,如果匹配成功则返回参数值否则返回默认值$NM,它会匹配热点参数规则的参数例外项,但是例外项的阈值是100万,等同于直接放行

asyncResult = asyncResult.transform(
                new SentinelReactorTransformer<>(new EntryConfig(routeId, EntryType.IN,
                    1, params, new ContextConfig(contextName(routeId), origin)))
            );

@Override
    public Publisher<T> apply(Publisher<T> publisher) {
        if (publisher instanceof Mono) {
            return new MonoSentinelOperator<>((Mono<T>) publisher, entryConfig);
        }
        if (publisher instanceof Flux) {
            return new FluxSentinelOperator<>((Flux<T>) publisher, entryConfig);
        }

        throw new IllegalStateException("Publisher type is not supported: " + publisher.getClass().getCanonicalName());
    }

@Override
    public void subscribe(CoreSubscriber<? super T> actual) {
        source.subscribe(new SentinelReactorSubscriber<>(entryConfig, actual, true));
    }

最终都会走到SentinelReactorSubscriber拦截

@Override
protected void hookOnSubscribe(Subscription subscription) {
    doWithContextOrCurrent(() -> currentContext().getOrEmpty(SentinelReactorConstants.SENTINEL_CONTEXT_KEY),
        this::entryWhenSubscribed);
}

 private void entryWhenSubscribed() {
    //定义的配置
    ContextConfig sentinelContextConfig = entryConfig.getContextConfig();
    if (sentinelContextConfig != null) {
        // If current we're already in a context, the context config won't work.
        //初始化context
        ContextUtil.enter(sentinelContextConfig.getContextName(), sentinelContextConfig.getOrigin());
    }
    try {
        //执行限流逻辑
        AsyncEntry entry = SphU.asyncEntry(entryConfig.getResourceName(), entryConfig.getEntryType(),
            entryConfig.getAcquireCount(), entryConfig.getArgs());
        this.currentEntry = entry;
        actual.onSubscribe(this);
    } catch (BlockException ex) {
        // Mark as completed (exited) explicitly.
        entryExited.set(true);
        // Signal cancel and propagate the {@code BlockException}.
        cancel();
        actual.onSubscribe(this);
        actual.onError(ex);
    } finally {
        if (sentinelContextConfig != null) {
            ContextUtil.exit();
        }
    }
}

到这里就会进入sentinel的限流处理逻辑,进入GatewayFlowSlot处理

public class GatewayFlowSlot extends AbstractLinkedProcessorSlot<DefaultNode> {

    @Override
    public void entry(Context context, ResourceWrapper resource, DefaultNode node, int count,
                      boolean prioritized, Object... args) throws Throwable {
        checkGatewayParamFlow(resource, count, args);

        fireEntry(context, resource, node, count, prioritized, args);
    }

    private void checkGatewayParamFlow(ResourceWrapper resourceWrapper, int count, Object... args)
        throws BlockException {
        if (args == null) {
            return;
        }
        //获取流控规则
        List<ParamFlowRule> rules = GatewayRuleManager.getConvertedParamRules(resourceWrapper.getName());
        if (rules == null || rules.isEmpty()) {
            return;
        }
		//和热点参数限流处理逻辑一样
        for (ParamFlowRule rule : rules) {
            // Initialize the parameter metrics.
            ParameterMetricStorage.initParamMetricsFor(resourceWrapper, rule);

            if (!ParamFlowChecker.passCheck(resourceWrapper, rule, count, args)) {
                String triggeredParam = "";
                if (args.length > rule.getParamIdx()) {
                    Object value = args[rule.getParamIdx()];
                    triggeredParam = String.valueOf(value);
                }
                throw new ParamFlowException(resourceWrapper.getName(), triggeredParam, rule);
            }
        }
    }

    @Override
    public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
        fireExit(context, resourceWrapper, count, args);
    }
}

这里可以看到底层还是使用ParamFlowChecker.passCheck做热点参数规则校验。

  • 网关流控维度支持route或自定义分组,也就是资源名称配置支持路由或自定义分组名称
  • 无论是否针对请求属性进行限流,Sentinel 底层都会将网关流控规则转化为热点参数规则(ParamFlowRule),存储在 GatewayRuleManager 中
  • GatewayFlowRule中可以设置GatewayParamFlowItem,如果GatewayParamFlowItem为空则和普通流控规则相同,如果不为空比如设置了clientIp,再判断是否设置了匹配模式,如果设置了匹配模式则会根据匹配的clientIp进行统计限流,否则针对所有的clientIp进行统计限流。
sentinel集成网关流控 简介 使用网关代理服务请求是微服务架构下常见的做法,相比于对被网关代理的各个服务做限流服务,在网关进行统一的限流操作似乎是更好的做法,sentinel对java应用常用的两款网关框架spring-cloud-gateway和zuul1都做了适配。本文将以zuul为例,说明zuul网关接入senrtinel流控能力的步骤并对源码做简单解读 网关限流模块分析 sentinel中对应网关限流规则的实体... 阅读详情

相关推荐

限流及自定义API维度限流

第一步:添加依赖,在原有spring-cloud-starter-gateway依赖的基础上再添加如下两个依赖 第二步:添加sentinel及路由规则 第三步:启动网关项目,检测sentinel控制台的网关菜单。 启动时,添加sentinel的jvm参数,通过此菜单可以让网关服务在sentinel控制台显示不一样的菜单 在idea中,进行如下配置: Sentinel 控制台启动以后,界面如图所示: 说明,假如没有发现请求链路,API管理,关闭网关项目,关闭sentinel,然后重启sentin..

weixin_62525764的博客 910

sentinel 网关限流

sentinel 网关限流

weixin_43931625的博客 3707

【云原生&微服务>SCG网关篇十二】Spring Cloud Gateway集成Sentinel API实现多种限流方式

1、Spring Cloud Gateway集成Sentinel实现多种限流方式; 2、sentinel提供了两种资源维度的限流:route维度、自定义API维度; 3、GatewayFlowRule、ApiDefinition、GatewayRuleManager介绍

健身变秃,coding变强 2282

Sentinel源码剖析之执行流程

上述讲了SCG Sentinel执行流程,这里给一个SpringBoot集成Sentinel的执行流程,两则本质上执行流程一致StatisticSlot计数,底层通过LongAdder实现,why?1、LongAdder底层通过cas保证高并发情况下的线程安全2、LongAdder底层通过add,decrement,sum这些现成的API可以使用熔断器在哪计数?下篇写为什么要用滑动窗口算法?原理是什么?怎么实现?下篇写。

白鸽 1230

ROS2 Nav2实战:解决rviz2显示no map received的3种实用方法(附详细步骤)

本文针对ROS2 Nav2导航栈中rviz2显示“no map received”的常见问题,提供了三种核心解决方案。深入剖析了问题根源在于ROS2生命周期节点管理、QoS策略不匹配及坐标系设置错误,并分别通过手动配置流程、集成生命周期管理器以及调优QoS策略与固化rviz2配置,给出了详细的实战步骤,确保地图数据可靠显示。

j0k1l2m3n的博客 421

Vue+ECharts-GL 3D地图实战:5层叠加打造炫酷中国地图(附完整代码)

本文详细介绍了如何利用Vue和ECharts-GL构建具有强烈立体感的3D中国地图。通过5层图层的巧妙叠加与样式调校,实现了从背景阴影到交互高光的完整视觉层次,并整合了动态飞线与散点标记。文章提供了完整的代码实现、核心配置解析以及性能优化与视觉调优的实战技巧,帮助开发者快速打造专业级的交互式地图可视化应用。

d6e7f8g9h的博客 649

sentinel api分组规则以及限流规则持久化到nacos部分源码介绍及源码修改

sentinel 控制台 和 sentinel 和 zuul整合的部分源码介绍, 以及修改sentinel dashboard源码使其能够进行持久化

m0_49979570的博客 2523

从末流211到东南软院:一个普通CS学生的保研逆袭全记录(含GPA、竞赛、面试避坑点)

本文详细记录了一位来自末流211院校的CS学生如何通过合理规划和持续努力,成功保研东南大学软件学院的完整历程。文章分享了GPA提升、竞赛策略、夏令营申请和预推免准备等关键环节的实战经验,特别强调了竞赛在弥补科研短板中的重要作用,为普通背景学生提供了可复制的保研逆袭方案。

weixin_33417488的博客 391

Sentinel配置Gateway 实现限流

由于sentinel starter依赖默认情况下就会为当前服务的所有的HTTP服务提供限流埋点,所以只要项目引入了sentinel starter 依赖,那么当前项目中的所有可以通过HTTP方式访问到的资源都将被sentinel保护起来。但在 Gateway网关和Sentinel的整合中, Sentinel 网关流控默认的粒度是 route 维度以及自定义 API 分组维度,默认。Sentinel 的数据源配置项是一个Map,由于Key键是String类型,所以数据源的名称是可以随便取的,例如我上面的。

python15397的博客 1409

《揭秘 BME688人工智能压力湿度温度气体传感器:气体检测的多面能手》:此文为AI自动生成

BME688 是博世推出的一款具有人工智能和集成高线性度高精度压力、湿度、温度传感器的气体传感器,专为移动和联网应用开发,尺寸紧凑且低功耗。BME688 作为一款先进的传感器,在多个领域展现出卓越的性能。它采用了坚固而紧凑的 3.0 x 3.0 x 0.9 mm³ 封装,这种小巧的尺寸使其能够轻松应用于对空间要求严格的移动设备和联网应用中。同时,低功耗的特性也使得它在能源有限的环境下能够长时间稳定运行。

zheng_ruiguo的专栏 1925

AtmelStudio 7 ASF库学习笔记一:新建工程、配置时钟、下载程序和查看官方例程

由于需要学习一下atmel的atsaml 系列,使用AtmelStudio 7开发,之前主要用STM32,现在看到Atmel的库函数,其实是有些类相似的。 ASF3的库对应ST的标准库。ASF4对应ST的HAL库,Ateml Start对应STM32CubeMX。但在这里还是用ASF3来开发,因为Ateml Start还不支持我的这个芯片,而且和STM32的HAL库类似,ASF4为了好移植都是底...

yxy244的博客 8653

Sentinel Gateway 网关流控实现原理和源码处理流程

网关流控实现原理 Sentinel 1.6.0 引入了 Sentinel API Gateway Adapter Common 模块,此模块中包含网关限流的规则和自定义 API 的实体和管理逻辑: GatewayFlowRule:网关限流规则,针对 API Gateway 的场景定制的限流规则,可以针对不同 route 或自定义的 API 分组进行限流,支持针对请求中的参数、Header、来源 IP 等进行定制化的限流。 ApiDefinition:用户自定义的 API 定义分组,可以看做是一些 URL 匹

qq_33210338的博客 1415

直流并联电压源的下垂控制与模拟光伏板特性的方法与仿真

并联直流电源下垂控制方法,下垂特性可以让并联直流电源输出功率/电流自动均衡,直流电源输出特性模拟光伏板的输出特性,具有自动限制输出最大功率的功能

1197

Sentine 源码分析之--AuthoritySlot、SystemSlot、GatewayFlowSlot

上一篇我对 Sentinel 中统计数据部分的 NodeSelectorSlot、ClusterBuilderSlot、StatisticSlot 的相关源码进行了分析,本篇我们开始分析规则相关的源码。

weixin_42118323的博客 1309

YOLOv8-Pose NCNN Android部署优化实践

本文探讨了在Android设备上高效部署YOLOv8-Pose姿态检测模型的优化实践。通过修改Pose头部导出逻辑,分离后处理到C++端实现,并替换SiLU激活函数为ReLU,显著提升了计算效率。具体步骤包括模型结构优化、ONNX导出、NCNN转换与算子融合,最终在Android端实现了30FPS的稳定推理性能。项目提供了完整的优化思路和实现方案,对移动端AI应用开发具有参考价值。

FJN110的博客 322

《网络摄像机实战11:flutter_webrtc支持 AWS Kinesis Video Streams信令服务器》

本文介绍了如何在Flutter项目中集成WebRTC实现网络摄像头的实时视频和音频对讲功能。通过使用flutter_webrtc插件,开发者可以快速构建跨平台的视频通信应用。文章详细说明了集成步骤:1)安装flutter_webrtc插件;2)配置信令服务器和KVS服务;3)初始化本地/远程视频渲染器;4)建立WebRTC连接并进行设备呼叫。最后展示了实际应用效果图,证明该方案能有效实现移动端与摄像头的实时音视频交互。该方案为开发智能家居监控等应用提供了技术参考。

lvj_158的专栏 356

Sentinel核心架构源码剖析(经典)

sentinel

gb4215287的博客 287

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

Spring Cloud Gateway 整合 sentinel 实现流控熔断

在微服务架构中,网关层可以屏蔽外部服务直接对内部服务进行调用,对内部服务起到隔离保护的作用,网关限流,顾名思义,就是通过网关层对服务进行限流,从而达到保护后端服务的作用。如果已经在网关层面做了限流,那么躲在身后的服务是否就安全了呢?答案是否定的,因此最常见的解决方案是:网关层集群限流+内部服务的单机限流兜底,这样才能保证不被流量冲垮

张维鹏的博客 2万+

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

Sentinel-Dashboard 与 apollo 规则的相互同步

那什么需要对 Sentinel 进行改造呢?主要是因为所有规则都只能通过 Nacos 或 Apollo 控制台来完成修改才能得到持久化存储,而在 Sentinel Dashboard 中修改限流规则虽然可以生效,但是不会被持久化到配置中心。生产环境下推荐采用 push 模式的数据源,push 推送的操作应该由 Sentinel dashboard 和 Config Center Dashboard 控制台统一进行管理和推送,Sentinel Client 仅负责获取配置中心推送的配置并更新到本地。

张维鹏的博客 1万+

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

Sentinel使用Nacos存储规则——Gateway网关服务

Sentinel使用Nacos存储规则——Gateway网关服务

yy1209357299的博客 652

sentinel持久化方案

sentinel持久化

weixin_42181142的博客 1384

Sentinel全流程分析和网关规则持久化到Nacos

我们目前只用到了Sentinel网关限流中热点参数限流,所以只对网关规则进行了持久化改造。如果有其它需求,大致的修改逻辑应该是差不多的。虽然改造后的Sentinel控制台还存在很多问题,但通过上面的流程分析,控制台只是作为规则可视化配置的工具,对性能要求不高,我们也没有监控持久化的需求,所以暂时就不进行其它改造了。

ajiong的博客 1424

【微服务】Sentinel 适配网关进行流量控制

外部请求进入 API Gateway 时会经过 Sentinel 实现的 filter,其中会依次进行路由/API 分组匹配、请求属性解析和参数组装。Sentinel 会根据配置的网关流控规则来解析请求属性,并依照参数索引顺序组装参数数组,最终传入SphU.entry(res, args)中。Sentinel API Gateway Adapter Common 模块向 Slot Chain 中添加了一个GatewayFlowSlot,专门用来做网关规则的检查。......

qq_57756904的博客 3288

阿里Sentinel支持Spring Cloud Gateway啦

1. 前言 4月25号,Sentinel 1.6.0 正式发布,带来 Spring Cloud Gateway 支持、控制台登录功能、改进的热点限流和注解 fallback 等多项新特性,该出手时就出手,紧跟时代潮流,昨天刚发布,今天我就要给大家分享下如何使用! 2. 介绍(本段来自Sentinel文档) Sentinel 1.6.0 引入了 Sentinel API Gateway Adapte...

u010889990的专栏 3676

Sentinel 之 网关限流

网关限流 该文档来之Sentinel官方文档 Sentinel 支持对 Spring Cloud Gateway、Zuul 等主流的 API Gateway 进行限流。 Sentinel 1.6.0 引入了 Sentinel API Gateway Adapter Common 模块,此模块中包含网关限流的规则和自定义 API 的实体和管理逻辑: GatewayFlowRule:网关限流规则,针对 API Gateway 的场景定制的限流规则,可以针对不同 route 或自定义的 API 分组进行限流

qq_26462567的博客 2361

Spring Cloud Sentinel 详解

摘要: Sentinel作为流量治理框架,核心围绕资源、规则及责任链机制实现精准控制。其通过SlotChain处理链(如流量统计、熔断检查)和滑动窗口算法动态监控QPS、异常等指标,支持流量控制、熔断降级、热点参数限流等规则。在Spring Cloud Gateway中,通过全局过滤器集成,结合Nacos/Dashboard实现动态规则同步。生产实践中需优化统计性能(如LongAdder)、结合Prometheus监控,并利用灰度发布策略。Sentinel的优势在于高扩展性设计与微服务生态深度整合,适用于高

csdn_tom_168的博客 1217
上一篇: sentinel源码分析-06热点参数限流
下一篇: sentinel源码分析-08流量数据统计
胖柯G
博客等级 码龄10年 453粉丝 57原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值