All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.alibaba.csp.sentinel.cluster.flow.rule.ClusterFlowRuleManager Maven / Gradle / Ivy

/*
 * Copyright 1999-2018 Alibaba Group Holding Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.alibaba.csp.sentinel.cluster.flow.rule;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import com.alibaba.csp.sentinel.cluster.flow.statistic.ClusterMetricStatistics;
import com.alibaba.csp.sentinel.cluster.flow.statistic.metric.ClusterMetric;
import com.alibaba.csp.sentinel.cluster.server.ServerConstants;
import com.alibaba.csp.sentinel.cluster.server.config.ClusterServerConfigManager;
import com.alibaba.csp.sentinel.cluster.server.connection.ConnectionManager;
import com.alibaba.csp.sentinel.cluster.server.util.ClusterRuleUtil;
import com.alibaba.csp.sentinel.log.RecordLog;
import com.alibaba.csp.sentinel.property.DynamicSentinelProperty;
import com.alibaba.csp.sentinel.property.PropertyListener;
import com.alibaba.csp.sentinel.property.SentinelProperty;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.flow.ClusterFlowConfig;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleUtil;
import com.alibaba.csp.sentinel.util.AssertUtil;
import com.alibaba.csp.sentinel.util.StringUtil;
import com.alibaba.csp.sentinel.util.function.Function;
import com.alibaba.csp.sentinel.util.function.Predicate;

/**
 * Manager for cluster flow rules.
 *
 * @author Eric Zhao
 * @since 1.4.0
 */
public final class ClusterFlowRuleManager {

    /**
     * The default cluster flow rule property supplier that creates a new dynamic property
     * for a specific namespace to do rule management manually.
     */
    public static final Function>> DEFAULT_PROPERTY_SUPPLIER =
        new Function>>() {
            @Override
            public SentinelProperty> apply(String namespace) {
                return new DynamicSentinelProperty<>();
            }
        };

    /**
     * (flowId, clusterRule)
     */
    private static final Map FLOW_RULES = new ConcurrentHashMap<>();
    /**
     * (namespace, [flowId...])
     */
    private static final Map> NAMESPACE_FLOW_ID_MAP = new ConcurrentHashMap<>();
    /**
     * 

This map (flowId, namespace) is used for getting connected count * when checking a specific rule in {@code ruleId}:

* *
     * ruleId -> namespace -> connection group -> connected count
     * 
*/ private static final Map FLOW_NAMESPACE_MAP = new ConcurrentHashMap<>(); /** * (namespace, property-listener wrapper) */ private static final Map> PROPERTY_MAP = new ConcurrentHashMap<>(); /** * Cluster flow rule property supplier for a specific namespace. */ private static volatile Function>> propertySupplier = DEFAULT_PROPERTY_SUPPLIER; private static final Object UPDATE_LOCK = new Object(); static { initDefaultProperty(); } private static void initDefaultProperty() { // The server should always support default namespace, // so register a default property for default namespace. SentinelProperty> defaultProperty = new DynamicSentinelProperty<>(); String defaultNamespace = ServerConstants.DEFAULT_NAMESPACE; registerPropertyInternal(defaultNamespace, defaultProperty); } public static void setPropertySupplier(Function>> propertySupplier) { AssertUtil.notNull(propertySupplier, "flow rule property supplier cannot be null"); ClusterFlowRuleManager.propertySupplier = propertySupplier; } /** * Listen to the {@link SentinelProperty} for cluster {@link FlowRule}s. * The property is the source of cluster {@link FlowRule}s for a specific namespace. * * @param namespace namespace to register */ public static void register2Property(String namespace) { AssertUtil.notEmpty(namespace, "namespace cannot be empty"); if (propertySupplier == null) { RecordLog.warn( "[ClusterFlowRuleManager] Cluster flow property supplier is absent, cannot register property"); return; } SentinelProperty> property = propertySupplier.apply(namespace); if (property == null) { RecordLog.warn( "[ClusterFlowRuleManager] Wrong created property from cluster flow property supplier, ignoring"); return; } synchronized (UPDATE_LOCK) { RecordLog.info("[ClusterFlowRuleManager] Registering new property to cluster flow rule manager" + " for namespace <{0}>", namespace); registerPropertyInternal(namespace, property); } } /** * Listen to the {@link SentinelProperty} for cluster {@link FlowRule}s if current property for namespace is absent. * The property is the source of cluster {@link FlowRule}s for a specific namespace. * * @param namespace namespace to register */ public static void registerPropertyIfAbsent(String namespace) { AssertUtil.notEmpty(namespace, "namespace cannot be empty"); if (!PROPERTY_MAP.containsKey(namespace)) { synchronized (UPDATE_LOCK) { if (!PROPERTY_MAP.containsKey(namespace)) { register2Property(namespace); } } } } private static void registerPropertyInternal(/*@NonNull*/ String namespace, /*@Valid*/ SentinelProperty> property) { NamespaceFlowProperty oldProperty = PROPERTY_MAP.get(namespace); if (oldProperty != null) { oldProperty.getProperty().removeListener(oldProperty.getListener()); } PropertyListener> listener = new FlowRulePropertyListener(namespace); property.addListener(listener); PROPERTY_MAP.put(namespace, new NamespaceFlowProperty<>(namespace, property, listener)); Set flowIdSet = NAMESPACE_FLOW_ID_MAP.get(namespace); if (flowIdSet == null) { resetNamespaceFlowIdMapFor(namespace); } } /** * Remove cluster flow rule property for a specific namespace. * * @param namespace valid namespace */ public static void removeProperty(String namespace) { AssertUtil.notEmpty(namespace, "namespace cannot be empty"); synchronized (UPDATE_LOCK) { NamespaceFlowProperty property = PROPERTY_MAP.get(namespace); if (property != null) { property.getProperty().removeListener(property.getListener()); PROPERTY_MAP.remove(namespace); } RecordLog.info("[ClusterFlowRuleManager] Removing property from cluster flow rule manager" + " for namespace <{0}>", namespace); } } private static void removePropertyListeners() { for (NamespaceFlowProperty property : PROPERTY_MAP.values()) { property.getProperty().removeListener(property.getListener()); } } private static void restorePropertyListeners() { for (NamespaceFlowProperty p : PROPERTY_MAP.values()) { p.getProperty().removeListener(p.getListener()); p.getProperty().addListener(p.getListener()); } } /** * Get flow rule by rule ID. * * @param id rule ID * @return flow rule */ public static FlowRule getFlowRuleById(Long id) { if (!ClusterRuleUtil.validId(id)) { return null; } return FLOW_RULES.get(id); } public static Set getFlowIdSet(String namespace) { if (StringUtil.isEmpty(namespace)) { return new HashSet<>(); } Set set = NAMESPACE_FLOW_ID_MAP.get(namespace); if (set == null) { return new HashSet<>(); } return new HashSet<>(set); } public static List getAllFlowRules() { return new ArrayList<>(FLOW_RULES.values()); } /** * Get all cluster flow rules within a specific namespace. * * @param namespace valid namespace * @return cluster flow rules within the provided namespace */ public static List getFlowRules(String namespace) { if (StringUtil.isEmpty(namespace)) { return new ArrayList<>(); } List rules = new ArrayList<>(); Set flowIdSet = NAMESPACE_FLOW_ID_MAP.get(namespace); if (flowIdSet == null || flowIdSet.isEmpty()) { return rules; } for (Long flowId : flowIdSet) { FlowRule rule = FLOW_RULES.get(flowId); if (rule != null) { rules.add(rule); } } return rules; } /** * Load flow rules for a specific namespace. The former rules of the namespace will be replaced. * * @param namespace a valid namespace * @param rules rule list */ public static void loadRules(String namespace, List rules) { AssertUtil.notEmpty(namespace, "namespace cannot be empty"); NamespaceFlowProperty property = PROPERTY_MAP.get(namespace); if (property != null) { property.getProperty().updateValue(rules); } } private static void resetNamespaceFlowIdMapFor(/*@Valid*/ String namespace) { NAMESPACE_FLOW_ID_MAP.put(namespace, new HashSet()); } /** * Clear all rules of the provided namespace and reset map. * * @param namespace valid namespace */ private static void clearAndResetRulesFor(/*@Valid*/ String namespace) { Set flowIdSet = NAMESPACE_FLOW_ID_MAP.get(namespace); if (flowIdSet != null && !flowIdSet.isEmpty()) { for (Long flowId : flowIdSet) { FLOW_RULES.remove(flowId); FLOW_NAMESPACE_MAP.remove(flowId); } flowIdSet.clear(); } else { resetNamespaceFlowIdMapFor(namespace); } } private static void clearAndResetRulesConditional(/*@Valid*/ String namespace, Predicate predicate) { Set oldIdSet = NAMESPACE_FLOW_ID_MAP.get(namespace); if (oldIdSet != null && !oldIdSet.isEmpty()) { for (Long flowId : oldIdSet) { if (predicate.test(flowId)) { FLOW_RULES.remove(flowId); FLOW_NAMESPACE_MAP.remove(flowId); ClusterMetricStatistics.removeMetric(flowId); } } oldIdSet.clear(); } } /** * Get connected count for associated namespace of given {@code flowId}. * * @param flowId unique flow ID * @return connected count */ public static int getConnectedCount(long flowId) { if (flowId <= 0) { return 0; } String namespace = FLOW_NAMESPACE_MAP.get(flowId); if (namespace == null) { return 0; } return ConnectionManager.getConnectedCount(namespace); } public static String getNamespace(long flowId) { return FLOW_NAMESPACE_MAP.get(flowId); } private static void applyClusterFlowRule(List list, /*@Valid*/ String namespace) { if (list == null || list.isEmpty()) { clearAndResetRulesFor(namespace); return; } final ConcurrentHashMap ruleMap = new ConcurrentHashMap<>(); Set flowIdSet = new HashSet<>(); for (FlowRule rule : list) { if (!rule.isClusterMode()) { continue; } if (!FlowRuleUtil.isValidRule(rule)) { RecordLog.warn( "[ClusterFlowRuleManager] Ignoring invalid flow rule when loading new flow rules: " + rule); continue; } if (StringUtil.isBlank(rule.getLimitApp())) { rule.setLimitApp(RuleConstant.LIMIT_APP_DEFAULT); } // Flow id should not be null after filtered. ClusterFlowConfig clusterConfig = rule.getClusterConfig(); Long flowId = clusterConfig.getFlowId(); if (flowId == null) { continue; } ruleMap.put(flowId, rule); FLOW_NAMESPACE_MAP.put(flowId, namespace); flowIdSet.add(flowId); // Prepare cluster metric from valid flow ID. ClusterMetricStatistics.putMetricIfAbsent(flowId, new ClusterMetric(clusterConfig.getSampleCount(), clusterConfig.getWindowIntervalMs())); } // Cleanup unused cluster metrics. clearAndResetRulesConditional(namespace, new Predicate() { @Override public boolean test(Long flowId) { return !ruleMap.containsKey(flowId); } }); FLOW_RULES.putAll(ruleMap); NAMESPACE_FLOW_ID_MAP.put(namespace, flowIdSet); } private static final class FlowRulePropertyListener implements PropertyListener> { private final String namespace; public FlowRulePropertyListener(String namespace) { this.namespace = namespace; } @Override public synchronized void configUpdate(List conf) { applyClusterFlowRule(conf, namespace); RecordLog.info("[ClusterFlowRuleManager] Cluster flow rules received for namespace <{0}>: {1}", namespace, FLOW_RULES); } @Override public synchronized void configLoad(List conf) { applyClusterFlowRule(conf, namespace); RecordLog.info("[ClusterFlowRuleManager] Cluster flow rules loaded for namespace <{0}>: {1}", namespace, FLOW_RULES); } } private ClusterFlowRuleManager() {} }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy