From d6237bee0ae1b83bf0813534bf939a882b6642f9 Mon Sep 17 00:00:00 2001
From: Eric Zhao
Date: Wed, 21 Nov 2018 17:55:27 +0800
Subject: [PATCH 01/10] Add basic interface and entity for Sentinel cluster
flow control
- Add a universal `TokenService` SPI interface for both local flow control and distributed flow control
- Add TokenResult entity to represents result of acquiring token
- Add `ClusterTokenClient` as the SPI interface for client of Sentinel cluster flow control
Signed-off-by: Eric Zhao
---
.../sentinel/cluster/ClusterTokenClient.java | 32 +++++++
.../sentinel/cluster/TokenClientProvider.java | 61 +++++++++++++
.../csp/sentinel/cluster/TokenResult.java | 86 +++++++++++++++++++
.../sentinel/cluster/TokenResultStatus.java | 60 +++++++++++++
.../cluster/TokenServerDescriptor.java | 72 ++++++++++++++++
.../csp/sentinel/cluster/TokenService.java | 47 ++++++++++
.../cluster/log/ClusterStatLogUtil.java | 59 +++++++++++++
.../slots/block/flow/FlowSlotTest.java | 16 ++--
8 files changed, 425 insertions(+), 8 deletions(-)
create mode 100644 sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/ClusterTokenClient.java
create mode 100644 sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenClientProvider.java
create mode 100644 sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenResult.java
create mode 100644 sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenResultStatus.java
create mode 100644 sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenServerDescriptor.java
create mode 100644 sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenService.java
create mode 100644 sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/log/ClusterStatLogUtil.java
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/ClusterTokenClient.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/ClusterTokenClient.java
new file mode 100644
index 00000000..6a230c07
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/ClusterTokenClient.java
@@ -0,0 +1,32 @@
+/*
+ * 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;
+
+/**
+ * Token client interface for distributed flow control.
+ *
+ * @author Eric Zhao
+ * @since 1.4.0
+ */
+public interface ClusterTokenClient extends TokenService {
+
+ /**
+ * Get descriptor of current token server.
+ *
+ * @return current token server
+ */
+ TokenServerDescriptor currentServer();
+}
\ No newline at end of file
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenClientProvider.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenClientProvider.java
new file mode 100644
index 00000000..592046c9
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenClientProvider.java
@@ -0,0 +1,61 @@
+/*
+ * 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;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.ServiceLoader;
+
+import com.alibaba.csp.sentinel.log.RecordLog;
+
+/**
+ * Provider for a universal {@link ClusterTokenClient} instance.
+ *
+ * @author Eric Zhao
+ * @since 1.4.0
+ */
+public final class TokenClientProvider {
+
+ private static ClusterTokenClient client = null;
+
+ private static final ServiceLoader LOADER = ServiceLoader.load(ClusterTokenClient.class);
+
+ static {
+ // Not strictly thread-safe, but it's OK since it will be resolved only once.
+ resolveTokenClientInstance();
+ }
+
+ public static ClusterTokenClient getClient() {
+ return client;
+ }
+
+ private static void resolveTokenClientInstance() {
+ List clients = new ArrayList();
+ for (ClusterTokenClient client : LOADER) {
+ clients.add(client);
+ }
+
+ if (!clients.isEmpty()) {
+ // Get first.
+ client = clients.get(0);
+ RecordLog.info("[TokenClientProvider] Token client resolved: " + client.getClass().getCanonicalName());
+ } else {
+ RecordLog.warn("[TokenClientProvider] No existing token client, resolve failed");
+ }
+ }
+
+ private TokenClientProvider() {}
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenResult.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenResult.java
new file mode 100644
index 00000000..29fa064a
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenResult.java
@@ -0,0 +1,86 @@
+/*
+ * 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;
+
+import java.util.Map;
+
+/**
+ * Result entity of acquiring cluster flow token.
+ *
+ * @author Eric Zhao
+ * @since 1.4.0
+ */
+public class TokenResult {
+
+ private Integer status;
+
+ private int remaining;
+ private int waitInMs;
+
+ private Map attachments;
+
+ public TokenResult() {}
+
+ public TokenResult(Integer status) {
+ this.status = status;
+ }
+
+ public Integer getStatus() {
+ return status;
+ }
+
+ public TokenResult setStatus(Integer status) {
+ this.status = status;
+ return this;
+ }
+
+ public int getRemaining() {
+ return remaining;
+ }
+
+ public TokenResult setRemaining(int remaining) {
+ this.remaining = remaining;
+ return this;
+ }
+
+ public int getWaitInMs() {
+ return waitInMs;
+ }
+
+ public TokenResult setWaitInMs(int waitInMs) {
+ this.waitInMs = waitInMs;
+ return this;
+ }
+
+ public Map getAttachments() {
+ return attachments;
+ }
+
+ public TokenResult setAttachments(Map attachments) {
+ this.attachments = attachments;
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return "TokenResult{" +
+ "status=" + status +
+ ", remaining=" + remaining +
+ ", waitInMs=" + waitInMs +
+ ", attachments=" + attachments +
+ '}';
+ }
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenResultStatus.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenResultStatus.java
new file mode 100644
index 00000000..275d761e
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenResultStatus.java
@@ -0,0 +1,60 @@
+/*
+ * 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;
+
+/**
+ * @author Eric Zhao
+ * @since 1.4.0
+ */
+public final class TokenResultStatus {
+
+ /**
+ * Bad client request.
+ */
+ public static final int BAD_REQUEST = -4;
+ /**
+ * Server or client unexpected failure (due to transport or serialization failure).
+ */
+ public static final int FAIL = -1;
+
+ /**
+ * Token acquired.
+ */
+ public static final int OK = 0;
+
+ /**
+ * Token acquire failed (blocked).
+ */
+ public static final int BLOCKED = 1;
+ /**
+ * Should wait for next buckets.
+ */
+ public static final int SHOULD_WAIT = 2;
+ /**
+ * Token acquire failed (no rule exists).
+ */
+ public static final int NO_RULE_EXISTS = 3;
+ /**
+ * Token acquire failed (reference resource is not available).
+ */
+ public static final int NO_REF_RULE_EXISTS = 4;
+ /**
+ * Token acquire failed (strategy not available).
+ */
+ public static final int NOT_AVAILABLE = 5;
+
+ private TokenResultStatus() {}
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenServerDescriptor.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenServerDescriptor.java
new file mode 100644
index 00000000..98739179
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenServerDescriptor.java
@@ -0,0 +1,72 @@
+/*
+ * 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;
+
+/**
+ * A simple descriptor for Sentinel token server.
+ *
+ * @author Eric Zhao
+ * @since 1.4.0
+ */
+public class TokenServerDescriptor {
+
+ private String host;
+ private int port;
+ private String type;
+
+ public TokenServerDescriptor() {}
+
+ public TokenServerDescriptor(String host, int port) {
+ this.host = host;
+ this.port = port;
+ }
+
+ public String getHost() {
+ return host;
+ }
+
+ public TokenServerDescriptor setHost(String host) {
+ this.host = host;
+ return this;
+ }
+
+ public int getPort() {
+ return port;
+ }
+
+ public TokenServerDescriptor setPort(int port) {
+ this.port = port;
+ return this;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public TokenServerDescriptor setType(String type) {
+ this.type = type;
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ return "TokenServerDescriptor{" +
+ "host='" + host + '\'' +
+ ", port=" + port +
+ ", type='" + type + '\'' +
+ '}';
+ }
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenService.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenService.java
new file mode 100644
index 00000000..d02c562f
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenService.java
@@ -0,0 +1,47 @@
+/*
+ * 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;
+
+import java.util.Collection;
+
+/**
+ * Service interface of flow control.
+ *
+ * @author Eric Zhao
+ * @since 1.4.0
+ */
+public interface TokenService {
+
+ /**
+ * Request tokens from remote token server.
+ *
+ * @param ruleId the unique rule ID
+ * @param acquireCount token count to acquire
+ * @param prioritized whether the request is prioritized
+ * @return result of the token request
+ */
+ TokenResult requestToken(Integer ruleId, int acquireCount, boolean prioritized);
+
+ /**
+ * Request tokens for a specific parameter from remote token server.
+ *
+ * @param ruleId the unique rule ID
+ * @param acquireCount token count to acquire
+ * @param params parameter list
+ * @return result of the token request
+ */
+ TokenResult requestParamToken(Integer ruleId, int acquireCount, Collection
*
* @author jialiang.linjl
+ * @author Eric Zhao
*/
public class FlowRuleManager {
private static final Map> flowRules = new ConcurrentHashMap>();
- private final static ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1,
- new NamedThreadFactory("sentinel-metrics-record-task", true));
- private final static FlowPropertyListener listener = new FlowPropertyListener();
+
+ private static final FlowPropertyListener LISTENER = new FlowPropertyListener();
private static SentinelProperty> currentProperty = new DynamicSentinelProperty>();
+ private static final ScheduledExecutorService SCHEDULER = Executors.newScheduledThreadPool(1,
+ new NamedThreadFactory("sentinel-metrics-record-task", true));
+
static {
- currentProperty.addListener(listener);
- scheduler.scheduleAtFixedRate(new MetricTimerListener(), 0, 1, TimeUnit.SECONDS);
+ currentProperty.addListener(LISTENER);
+ SCHEDULER.scheduleAtFixedRate(new MetricTimerListener(), 0, 1, TimeUnit.SECONDS);
}
/**
@@ -70,10 +65,10 @@ public class FlowRuleManager {
* @param property the property to listen.
*/
public static void register2Property(SentinelProperty> property) {
- synchronized (listener) {
+ synchronized (LISTENER) {
RecordLog.info("[FlowRuleManager] Registering new property to flow rule manager");
- currentProperty.removeListener(listener);
- property.addListener(listener);
+ currentProperty.removeListener(LISTENER);
+ property.addListener(LISTENER);
currentProperty = property;
}
}
@@ -100,65 +95,7 @@ public class FlowRuleManager {
currentProperty.updateValue(rules);
}
- private static Map> loadFlowConf(List list) {
- Map> newRuleMap = new ConcurrentHashMap>();
-
- if (list == null || list.isEmpty()) {
- return newRuleMap;
- }
-
- for (FlowRule rule : list) {
- if (!isValidRule(rule)) {
- RecordLog.warn("[FlowRuleManager] Ignoring invalid flow rule when loading new flow rules: " + rule);
- continue;
- }
- if (StringUtil.isBlank(rule.getLimitApp())) {
- rule.setLimitApp(RuleConstant.LIMIT_APP_DEFAULT);
- }
-
- TrafficShapingController rater = new DefaultController(rule.getCount(), rule.getGrade());
- if (rule.getGrade() == RuleConstant.FLOW_GRADE_QPS
- && rule.getControlBehavior() == RuleConstant.CONTROL_BEHAVIOR_WARM_UP
- && rule.getWarmUpPeriodSec() > 0) {
- rater = new WarmUpController(rule.getCount(), rule.getWarmUpPeriodSec(), ColdFactorProperty.coldFactor);
-
- } else if (rule.getGrade() == RuleConstant.FLOW_GRADE_QPS
- && rule.getControlBehavior() == RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER
- && rule.getMaxQueueingTimeMs() > 0) {
- rater = new RateLimiterController(rule.getMaxQueueingTimeMs(), rule.getCount());
- } else if (rule.getGrade() == RuleConstant.FLOW_GRADE_QPS
- && rule.getControlBehavior() == RuleConstant.CONTROL_BEHAVIOR_WARM_UP_RATE_LIMITER
- && rule.getMaxQueueingTimeMs() > 0 && rule.getWarmUpPeriodSec() > 0) {
- rater = new WarmUpRateLimiterController(rule.getCount(), rule.getWarmUpPeriodSec(),
- rule.getMaxQueueingTimeMs(), ColdFactorProperty.coldFactor);
- }
-
- rule.setRater(rater);
-
- String identity = rule.getResource();
- List ruleM = newRuleMap.get(identity);
-
- if (ruleM == null) {
- ruleM = new ArrayList();
- newRuleMap.put(identity, ruleM);
- }
-
- ruleM.add(rule);
-
- }
-
- if (!newRuleMap.isEmpty()) {
- Comparator comparator = new FlowRuleComparator();
- // Sort the rules.
- for (List rules : newRuleMap.values()) {
- Collections.sort(rules, comparator);
- }
- }
-
- return newRuleMap;
- }
-
- static Map> getFlowRules() {
+ static Map> getFlowRuleMap() {
return flowRules;
}
@@ -188,7 +125,7 @@ public class FlowRuleManager {
@Override
public void configUpdate(List value) {
- Map> rules = loadFlowConf(value);
+ Map> rules = FlowRuleUtil.buildFlowRuleMap(value);
if (rules != null) {
flowRules.clear();
flowRules.putAll(rules);
@@ -198,43 +135,13 @@ public class FlowRuleManager {
@Override
public void configLoad(List conf) {
- Map> rules = loadFlowConf(conf);
+ Map> rules = FlowRuleUtil.buildFlowRuleMap(conf);
if (rules != null) {
flowRules.clear();
flowRules.putAll(rules);
}
RecordLog.info("[FlowRuleManager] Flow rules loaded: " + flowRules);
}
-
}
- public static boolean isValidRule(FlowRule rule) {
- boolean baseValid = rule != null && !StringUtil.isBlank(rule.getResource()) && rule.getCount() >= 0
- && rule.getGrade() >= 0 && rule.getStrategy() >= 0 && rule.getControlBehavior() >= 0;
- if (!baseValid) {
- return false;
- }
- // Check strategy and control (shaping) behavior.
- return checkStrategyField(rule) && checkControlBehaviorField(rule);
- }
-
- private static boolean checkStrategyField(/*@NonNull*/ FlowRule rule) {
- if (rule.getStrategy() == RuleConstant.STRATEGY_RELATE || rule.getStrategy() == RuleConstant.STRATEGY_CHAIN) {
- return StringUtil.isNotBlank(rule.getRefResource());
- }
- return true;
- }
-
- private static boolean checkControlBehaviorField(/*@NonNull*/ FlowRule rule) {
- switch (rule.getControlBehavior()) {
- case RuleConstant.CONTROL_BEHAVIOR_WARM_UP:
- return rule.getWarmUpPeriodSec() > 0;
- case RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER:
- return rule.getMaxQueueingTimeMs() > 0;
- case RuleConstant.CONTROL_BEHAVIOR_WARM_UP_RATE_LIMITER:
- return rule.getWarmUpPeriodSec() > 0 && rule.getMaxQueueingTimeMs() > 0;
- default:
- return true;
- }
- }
}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/FlowRuleUtil.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/FlowRuleUtil.java
new file mode 100644
index 00000000..aa5a58c6
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/FlowRuleUtil.java
@@ -0,0 +1,227 @@
+/*
+ * 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.slots.block.flow;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import com.alibaba.csp.sentinel.log.RecordLog;
+import com.alibaba.csp.sentinel.slots.block.ClusterRuleConstant;
+import com.alibaba.csp.sentinel.slots.block.RuleConstant;
+import com.alibaba.csp.sentinel.slots.block.flow.controller.DefaultController;
+import com.alibaba.csp.sentinel.slots.block.flow.controller.RateLimiterController;
+import com.alibaba.csp.sentinel.slots.block.flow.controller.WarmUpController;
+import com.alibaba.csp.sentinel.slots.block.flow.controller.WarmUpRateLimiterController;
+import com.alibaba.csp.sentinel.util.StringUtil;
+import com.alibaba.csp.sentinel.util.function.Function;
+import com.alibaba.csp.sentinel.util.function.Predicate;
+
+/**
+ * @author Eric Zhao
+ * @since 1.4.0
+ */
+public final class FlowRuleUtil {
+
+ /**
+ * Build the flow rule map from raw list of flow rules, grouping by resource name.
+ *
+ * @param list raw list of flow rules
+ * @return constructed new flow rule map; empty map if list is null or empty, or no valid rules
+ */
+ public static Map> buildFlowRuleMap(List list) {
+ return buildFlowRuleMap(list, null);
+ }
+
+ /**
+ * Build the flow rule map from raw list of flow rules, grouping by resource name.
+ *
+ * @param list raw list of flow rules
+ * @param filter rule filter
+ * @return constructed new flow rule map; empty map if list is null or empty, or no wanted rules
+ */
+ public static Map> buildFlowRuleMap(List list, Predicate filter) {
+ return buildFlowRuleMap(list, filter, true);
+ }
+
+ /**
+ * Build the flow rule map from raw list of flow rules, grouping by resource name.
+ *
+ * @param list raw list of flow rules
+ * @param filter rule filter
+ * @param shouldSort whether the rules should be sorted
+ * @return constructed new flow rule map; empty map if list is null or empty, or no wanted rules
+ */
+ public static Map> buildFlowRuleMap(List list, Predicate filter,
+ boolean shouldSort) {
+ return buildFlowRuleMap(list, extractResource, filter, shouldSort);
+ }
+
+ /**
+ * Build the flow rule map from raw list of flow rules, grouping by provided group function.
+ *
+ * @param list raw list of flow rules
+ * @param groupFunction grouping function of the map (by key)
+ * @param filter rule filter
+ * @param shouldSort whether the rules should be sorted
+ * @param type of key
+ * @return constructed new flow rule map; empty map if list is null or empty, or no wanted rules
+ */
+ public static Map> buildFlowRuleMap(List list, Function groupFunction,
+ Predicate filter, boolean shouldSort) {
+ Map> newRuleMap = new ConcurrentHashMap>();
+ if (list == null || list.isEmpty()) {
+ return newRuleMap;
+ }
+
+ for (FlowRule rule : list) {
+ if (!isValidRule(rule)) {
+ RecordLog.warn("[FlowRuleManager] Ignoring invalid flow rule when loading new flow rules: " + rule);
+ continue;
+ }
+ if (filter != null && !filter.test(rule)) {
+ continue;
+ }
+ if (StringUtil.isBlank(rule.getLimitApp())) {
+ rule.setLimitApp(RuleConstant.LIMIT_APP_DEFAULT);
+ }
+
+ TrafficShapingController rater = generateRater(rule);
+ rule.setRater(rater);
+
+ K key = groupFunction.apply(rule);
+ if (key == null) {
+ continue;
+ }
+ List flowRules = newRuleMap.get(key);
+
+ if (flowRules == null) {
+ flowRules = new ArrayList();
+ newRuleMap.put(key, flowRules);
+ }
+
+ flowRules.add(rule);
+ }
+
+ if (shouldSort && !newRuleMap.isEmpty()) {
+ Comparator comparator = new FlowRuleComparator();
+ // Sort the rules.
+ for (List rules : newRuleMap.values()) {
+ Collections.sort(rules, comparator);
+ }
+ }
+
+ return newRuleMap;
+ }
+
+ private static TrafficShapingController generateRater(/*@Valid*/ FlowRule rule) {
+ if (rule.getGrade() == RuleConstant.FLOW_GRADE_QPS) {
+ switch (rule.getControlBehavior()) {
+ case RuleConstant.CONTROL_BEHAVIOR_WARM_UP:
+ return new WarmUpController(rule.getCount(), rule.getWarmUpPeriodSec(),
+ ColdFactorProperty.coldFactor);
+ case RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER:
+ return new RateLimiterController(rule.getMaxQueueingTimeMs(), rule.getCount());
+ case RuleConstant.CONTROL_BEHAVIOR_WARM_UP_RATE_LIMITER:
+ return new WarmUpRateLimiterController(rule.getCount(), rule.getWarmUpPeriodSec(),
+ rule.getMaxQueueingTimeMs(), ColdFactorProperty.coldFactor);
+ case RuleConstant.CONTROL_BEHAVIOR_DEFAULT:
+ default:
+ // Default mode or unknown mode: default traffic shaping controller (fast-reject).
+ }
+ }
+ return new DefaultController(rule.getCount(), rule.getGrade());
+ }
+
+ /**
+ * Check whether provided ID can be a valid cluster flow ID.
+ *
+ * @param id flow ID to check
+ * @return true if valid, otherwise false
+ */
+ public static boolean validClusterRuleId(Integer id) {
+ return id != null && id > 0;
+ }
+
+ /**
+ * Check whether provided flow rule is valid.
+ *
+ * @param rule flow rule to check
+ * @return true if valid, otherwise false
+ */
+ public static boolean isValidRule(FlowRule rule) {
+ boolean baseValid = rule != null && !StringUtil.isBlank(rule.getResource()) && rule.getCount() >= 0
+ && rule.getGrade() >= 0 && rule.getStrategy() >= 0 && rule.getControlBehavior() >= 0;
+ if (!baseValid) {
+ return false;
+ }
+ // Check strategy and control (shaping) behavior.
+ return checkClusterField(rule) && checkStrategyField(rule) && checkControlBehaviorField(rule);
+ }
+
+ private static boolean checkClusterField(/*@NonNull*/ FlowRule rule) {
+ if (!rule.isClusterMode()) {
+ return true;
+ }
+ ClusterFlowConfig clusterConfig = rule.getClusterConfig();
+ if (clusterConfig == null) {
+ return false;
+ }
+ if (!validClusterRuleId(clusterConfig.getFlowId())) {
+ return false;
+ }
+ switch (rule.getStrategy()) {
+ case ClusterRuleConstant.FLOW_CLUSTER_STRATEGY_NORMAL:
+ return true;
+ case ClusterRuleConstant.FLOW_CLUSTER_STRATEGY_REF:
+ return validClusterRuleId(clusterConfig.getRefFlowId());
+ default:
+ return true;
+ }
+ }
+
+ private static boolean checkStrategyField(/*@NonNull*/ FlowRule rule) {
+ if (rule.getStrategy() == RuleConstant.STRATEGY_RELATE || rule.getStrategy() == RuleConstant.STRATEGY_CHAIN) {
+ return StringUtil.isNotBlank(rule.getRefResource());
+ }
+ return true;
+ }
+
+ private static boolean checkControlBehaviorField(/*@NonNull*/ FlowRule rule) {
+ switch (rule.getControlBehavior()) {
+ case RuleConstant.CONTROL_BEHAVIOR_WARM_UP:
+ return rule.getWarmUpPeriodSec() > 0;
+ case RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER:
+ return rule.getMaxQueueingTimeMs() > 0;
+ case RuleConstant.CONTROL_BEHAVIOR_WARM_UP_RATE_LIMITER:
+ return rule.getWarmUpPeriodSec() > 0 && rule.getMaxQueueingTimeMs() > 0;
+ default:
+ return true;
+ }
+ }
+
+ private static final Function extractResource = new Function() {
+ @Override
+ public String apply(FlowRule rule) {
+ return rule.getResource();
+ }
+ };
+
+ private FlowRuleUtil() {}
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/FlowSlot.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/FlowSlot.java
index 4bd8ca5d..3f392db1 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/FlowSlot.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/FlowSlot.java
@@ -138,27 +138,27 @@ public class FlowSlot extends AbstractLinkedProcessorSlot {
@Override
public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count,
boolean prioritized, Object... args) throws Throwable {
- checkFlow(resourceWrapper, context, node, count);
+ checkFlow(resourceWrapper, context, node, count, prioritized);
fireEntry(context, resourceWrapper, node, count, prioritized, args);
}
- void checkFlow(ResourceWrapper resource, Context context, DefaultNode node, int count) throws BlockException {
+ void checkFlow(ResourceWrapper resource, Context context, DefaultNode node, int count, boolean prioritized) throws BlockException {
// Flow rule map cannot be null.
- Map> flowRules = FlowRuleManager.getFlowRules();
+ Map> flowRules = FlowRuleManager.getFlowRuleMap();
List rules = flowRules.get(resource.getName());
if (rules != null) {
for (FlowRule rule : rules) {
- if (!canPassCheck(rule, context, node, count)) {
+ if (!canPassCheck(rule, context, node, count, prioritized)) {
throw new FlowException(rule.getLimitApp());
}
}
}
}
- boolean canPassCheck(FlowRule rule, Context context, DefaultNode node, int count) {
- return FlowRuleChecker.passCheck(rule, context, node, count);
+ boolean canPassCheck(FlowRule rule, Context context, DefaultNode node, int count, boolean prioritized) {
+ return FlowRuleChecker.passCheck(rule, context, node, count, prioritized);
}
@Override
From f539b5b8274b833355731119915ed8b6eb803f9a Mon Sep 17 00:00:00 2001
From: Eric Zhao
Date: Wed, 21 Nov 2018 19:02:21 +0800
Subject: [PATCH 03/10] Update parameter flow rule to adapt to cluster mode and
extract rule util class
- Update ParamFlowRule to support cluster mode
- Add `ParamFlowClusterConfig` to provide cluster mode items for the rule
- Update ParamFlowChecker to support cluster flow mode
- Extract ParamFlowRuleUtil class
- Change type of `flowId` from Integer to Long
Signed-off-by: Eric Zhao
---
.../csp/sentinel/cluster/TokenService.java | 4 +-
.../slots/block/flow/ClusterFlowConfig.java | 12 +-
.../slots/block/flow/FlowRuleUtil.java | 2 +-
.../block/flow/param/ParamFlowChecker.java | 79 +++++++++++-
.../flow/param/ParamFlowClusterConfig.java | 94 +++++++++++++++
.../slots/block/flow/param/ParamFlowRule.java | 35 +++++-
.../flow/param/ParamFlowRuleManager.java | 68 +----------
.../block/flow/param/ParamFlowRuleUtil.java | 114 ++++++++++++++++++
.../flow/param/ParamFlowRuleManagerTest.java | 82 -------------
.../flow/param/ParamFlowRuleUtilTest.java | 95 +++++++++++++++
10 files changed, 419 insertions(+), 166 deletions(-)
create mode 100644 sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowClusterConfig.java
create mode 100644 sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRuleUtil.java
create mode 100644 sentinel-extension/sentinel-parameter-flow-control/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRuleUtilTest.java
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenService.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenService.java
index d02c562f..f1d06292 100644
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenService.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenService.java
@@ -33,7 +33,7 @@ public interface TokenService {
* @param prioritized whether the request is prioritized
* @return result of the token request
*/
- TokenResult requestToken(Integer ruleId, int acquireCount, boolean prioritized);
+ TokenResult requestToken(Long ruleId, int acquireCount, boolean prioritized);
/**
* Request tokens for a specific parameter from remote token server.
@@ -43,5 +43,5 @@ public interface TokenService {
* @param params parameter list
* @return result of the token request
*/
- TokenResult requestParamToken(Integer ruleId, int acquireCount, Collection