From 88a02623acbc73c3eca468ef6bb0484a6217f0b2 Mon Sep 17 00:00:00 2001
From: Eric Zhao
Date: Thu, 27 Sep 2018 14:15:47 +0800
Subject: [PATCH] Add flow control by frequent (hot spot) parameters (#156)
- Add callback registry for statistic slot for extensions.
- Add a new module `sentinel-parameter-flow-control` under `sentinel-extension`.
- Add a `CacheMap` interface to provide abstraction for cache. We use ConcurrentLinkedHashMap as the default implementation (LRU strategy)..
- Add a `ParameterMetric` class as frequent parameter metrics for a specific resource. The metric map is located in `ParamFlowSlot` rather than `ClusterNode`.
- Implement `ParameterLeapArray` as statistic data structure for frequent parameters in a period of time window.
- Add `ParamFlowSlot` as the checker slot; Add `ParamFlowChecker` to do rule checking; Add `ParamFlowRuleManager` to do rule managing.
- The statistic metrics for frequent parameters is enabled only if the related resource has configured parameter flow rule; Parameter metrics for removed rules will be cleared automatically.
- Leverage extensible `SlotChainBuilder` to provide a `HotParamSlotChainBuilder`.
- Add command handlers for hot param rules.
- Add test cases and demo.
---
pom.xml | 10 +
.../csp/sentinel/slotchain/ProcessorSlot.java | 6 +-
.../slotchain/ProcessorSlotEntryCallback.java | 32 +++
.../slotchain/ProcessorSlotExitCallback.java | 29 +++
.../slots/statistic/StatisticSlot.java | 22 +-
.../StatisticSlotCallbackRegistry.java | 85 +++++++
sentinel-demo/pom.xml | 1 +
.../pom.xml | 30 +++
.../demo/flow/param/ParamFlowQpsDemo.java | 69 ++++++
.../demo/flow/param/ParamFlowQpsRunner.java | 167 +++++++++++++
sentinel-extension/pom.xml | 1 +
.../sentinel-parameter-flow-control/README.md | 61 +++++
.../sentinel-parameter-flow-control/pom.xml | 43 ++++
.../handler/FetchTopParamsCommandHandler.java | 64 +++++
.../GetParamFlowRulesCommandHandler.java | 36 +++
.../ModifyParamFlowRulesCommandHandler.java | 95 +++++++
.../ParamFlowStatisticSlotCallbackInit.java | 35 +++
.../slots/HotParamSlotChainBuilder.java | 52 ++++
.../block/flow/param/ParamFlowChecker.java | 100 ++++++++
.../block/flow/param/ParamFlowException.java | 48 ++++
.../slots/block/flow/param/ParamFlowItem.java | 101 ++++++++
.../slots/block/flow/param/ParamFlowRule.java | 156 ++++++++++++
.../flow/param/ParamFlowRuleManager.java | 233 ++++++++++++++++++
.../slots/block/flow/param/ParamFlowSlot.java | 158 ++++++++++++
.../block/flow/param/ParameterMetric.java | 147 +++++++++++
.../block/flow/param/RollingParamEvent.java | 31 +++
.../ParamFlowStatisticEntryCallback.java | 49 ++++
.../slots/statistic/cache/CacheMap.java | 45 ++++
.../cache/ConcurrentLinkedHashMapWrapper.java | 92 +++++++
.../slots/statistic/data/ParamMapBucket.java | 67 +++++
.../metric/HotParameterLeapArray.java | 127 ++++++++++
...libaba.csp.sentinel.command.CommandHandler | 3 +
.../com.alibaba.csp.sentinel.init.InitFunc | 1 +
...ba.csp.sentinel.slotchain.SlotChainBuilder | 1 +
.../flow/param/ParamFlowCheckerTest.java | 193 +++++++++++++++
.../flow/param/ParamFlowRuleManagerTest.java | 192 +++++++++++++++
.../block/flow/param/ParamFlowSlotTest.java | 118 +++++++++
.../block/flow/param/ParameterMetricTest.java | 75 ++++++
.../statistic/data/ParamMapBucketTest.java | 77 ++++++
.../metric/HotParameterLeapArrayTest.java | 132 ++++++++++
40 files changed, 2977 insertions(+), 7 deletions(-)
create mode 100644 sentinel-core/src/main/java/com/alibaba/csp/sentinel/slotchain/ProcessorSlotEntryCallback.java
create mode 100644 sentinel-core/src/main/java/com/alibaba/csp/sentinel/slotchain/ProcessorSlotExitCallback.java
create mode 100644 sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/StatisticSlotCallbackRegistry.java
create mode 100644 sentinel-demo/sentinel-demo-parameter-flow-control/pom.xml
create mode 100644 sentinel-demo/sentinel-demo-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/demo/flow/param/ParamFlowQpsDemo.java
create mode 100644 sentinel-demo/sentinel-demo-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/demo/flow/param/ParamFlowQpsRunner.java
create mode 100644 sentinel-extension/sentinel-parameter-flow-control/README.md
create mode 100644 sentinel-extension/sentinel-parameter-flow-control/pom.xml
create mode 100644 sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/command/handler/FetchTopParamsCommandHandler.java
create mode 100644 sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/command/handler/GetParamFlowRulesCommandHandler.java
create mode 100644 sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/command/handler/ModifyParamFlowRulesCommandHandler.java
create mode 100644 sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/init/ParamFlowStatisticSlotCallbackInit.java
create mode 100644 sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/HotParamSlotChainBuilder.java
create mode 100644 sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowChecker.java
create mode 100644 sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowException.java
create mode 100644 sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowItem.java
create mode 100644 sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRule.java
create mode 100644 sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRuleManager.java
create mode 100644 sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowSlot.java
create mode 100644 sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParameterMetric.java
create mode 100644 sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/RollingParamEvent.java
create mode 100644 sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/statistic/ParamFlowStatisticEntryCallback.java
create mode 100644 sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/statistic/cache/CacheMap.java
create mode 100644 sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/statistic/cache/ConcurrentLinkedHashMapWrapper.java
create mode 100644 sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/statistic/data/ParamMapBucket.java
create mode 100644 sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/statistic/metric/HotParameterLeapArray.java
create mode 100755 sentinel-extension/sentinel-parameter-flow-control/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.command.CommandHandler
create mode 100755 sentinel-extension/sentinel-parameter-flow-control/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.init.InitFunc
create mode 100644 sentinel-extension/sentinel-parameter-flow-control/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.slotchain.SlotChainBuilder
create mode 100644 sentinel-extension/sentinel-parameter-flow-control/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowCheckerTest.java
create mode 100644 sentinel-extension/sentinel-parameter-flow-control/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRuleManagerTest.java
create mode 100644 sentinel-extension/sentinel-parameter-flow-control/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowSlotTest.java
create mode 100644 sentinel-extension/sentinel-parameter-flow-control/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParameterMetricTest.java
create mode 100644 sentinel-extension/sentinel-parameter-flow-control/src/test/java/com/alibaba/csp/sentinel/slots/statistic/data/ParamMapBucketTest.java
create mode 100644 sentinel-extension/sentinel-parameter-flow-control/src/test/java/com/alibaba/csp/sentinel/slots/statistic/metric/HotParameterLeapArrayTest.java
diff --git a/pom.xml b/pom.xml
index d4463d49..a3922ec1 100755
--- a/pom.xml
+++ b/pom.xml
@@ -87,6 +87,11 @@
sentinel-annotation-aspectj
${project.version}
+
+ com.alibaba.csp
+ sentinel-parameter-flow-control
+ ${project.version}
+
com.alibaba.csp
sentinel-datasource-extension
@@ -112,6 +117,11 @@
sentinel-transport-simple-http
${project.version}
+
+ com.alibaba.csp
+ sentinel-transport-common
+ ${project.version}
+
com.alibaba.csp
sentinel-adapter
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slotchain/ProcessorSlot.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slotchain/ProcessorSlot.java
index d4f0af5b..dfe5449d 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slotchain/ProcessorSlot.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slotchain/ProcessorSlot.java
@@ -34,7 +34,7 @@ public interface ProcessorSlot {
* @param param Generics parameter, usually is a {@link com.alibaba.csp.sentinel.node.Node}
* @param count tokens needed
* @param args parameters of the original call
- * @throws Throwable
+ * @throws Throwable blocked exception or unexpected error
*/
void entry(Context context, ResourceWrapper resourceWrapper, T param, int count, Object... args)
throws Throwable;
@@ -44,10 +44,10 @@ public interface ProcessorSlot {
*
* @param context current {@link Context}
* @param resourceWrapper current resource
- * @param obj
+ * @param obj relevant object (e.g. Node)
* @param count tokens needed
* @param args parameters of the original call
- * @throws Throwable
+ * @throws Throwable blocked exception or unexpected error
*/
void fireEntry(Context context, ResourceWrapper resourceWrapper, Object obj, int count, Object... args)
throws Throwable;
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slotchain/ProcessorSlotEntryCallback.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slotchain/ProcessorSlotEntryCallback.java
new file mode 100644
index 00000000..a29be37a
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slotchain/ProcessorSlotEntryCallback.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.slotchain;
+
+import com.alibaba.csp.sentinel.context.Context;
+import com.alibaba.csp.sentinel.slots.block.BlockException;
+
+/**
+ * Callback for entering {@link com.alibaba.csp.sentinel.slots.statistic.StatisticSlot} (passed and blocked).
+ *
+ * @author Eric Zhao
+ * @since 0.2.0
+ */
+public interface ProcessorSlotEntryCallback {
+
+ void onPass(Context context, ResourceWrapper resourceWrapper, T param, int count, Object... args) throws Exception;
+
+ void onBlocked(BlockException ex, Context context, ResourceWrapper resourceWrapper, T param, int count, Object... args);
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slotchain/ProcessorSlotExitCallback.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slotchain/ProcessorSlotExitCallback.java
new file mode 100644
index 00000000..b8349dd4
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slotchain/ProcessorSlotExitCallback.java
@@ -0,0 +1,29 @@
+/*
+ * 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.slotchain;
+
+import com.alibaba.csp.sentinel.context.Context;
+
+/**
+ * Callback for exiting {@link com.alibaba.csp.sentinel.slots.statistic.StatisticSlot} (passed and blocked).
+ *
+ * @author Eric Zhao
+ * @since 0.2.0
+ */
+public interface ProcessorSlotExitCallback {
+
+ void onExit(Context context, ResourceWrapper resourceWrapper, int count, Object... args);
+}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/StatisticSlot.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/StatisticSlot.java
index c4e4d8f6..093adbe3 100755
--- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/StatisticSlot.java
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/StatisticSlot.java
@@ -15,6 +15,10 @@
*/
package com.alibaba.csp.sentinel.slots.statistic;
+import java.util.Collection;
+
+import com.alibaba.csp.sentinel.slotchain.ProcessorSlotEntryCallback;
+import com.alibaba.csp.sentinel.slotchain.ProcessorSlotExitCallback;
import com.alibaba.csp.sentinel.util.TimeUtil;
import com.alibaba.csp.sentinel.Constants;
import com.alibaba.csp.sentinel.EntryType;
@@ -39,13 +43,13 @@ import com.alibaba.csp.sentinel.slots.block.BlockException;
*
*
* @author jialiang.linjl
+ * @author Eric Zhao
*/
public class StatisticSlot extends AbstractLinkedProcessorSlot {
@Override
public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, Object... args)
throws Throwable {
-
try {
fireEntry(context, resourceWrapper, node, count, args);
node.increaseThreadNum();
@@ -61,6 +65,9 @@ public class StatisticSlot extends AbstractLinkedProcessorSlot {
Constants.ENTRY_NODE.addPassRequest();
}
+ for (ProcessorSlotEntryCallback handler : StatisticSlotCallbackRegistry.getEntryCallbacks()) {
+ handler.onPass(context, resourceWrapper, node, count, args);
+ }
} catch (BlockException e) {
context.getCurEntry().setError(e);
@@ -74,6 +81,10 @@ public class StatisticSlot extends AbstractLinkedProcessorSlot {
Constants.ENTRY_NODE.increaseBlockedQps();
}
+ for (ProcessorSlotEntryCallback handler : StatisticSlotCallbackRegistry.getEntryCallbacks()) {
+ handler.onBlocked(e, context, resourceWrapper, node, count, args);
+ }
+
throw e;
} catch (Throwable e) {
context.getCurEntry().setError(e);
@@ -117,11 +128,14 @@ public class StatisticSlot extends AbstractLinkedProcessorSlot {
Constants.ENTRY_NODE.decreaseThreadNum();
}
} else {
- // error may happen
- // node.rt(-2);
+ // Error may happen.
+ }
+
+ Collection exitCallbacks = StatisticSlotCallbackRegistry.getExitCallbacks();
+ for (ProcessorSlotExitCallback handler : exitCallbacks) {
+ handler.onExit(context, resourceWrapper, count, args);
}
fireExit(context, resourceWrapper, count);
}
-
}
diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/StatisticSlotCallbackRegistry.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/StatisticSlotCallbackRegistry.java
new file mode 100644
index 00000000..a06fc984
--- /dev/null
+++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/StatisticSlotCallbackRegistry.java
@@ -0,0 +1,85 @@
+/*
+ * 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.statistic;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import com.alibaba.csp.sentinel.node.DefaultNode;
+import com.alibaba.csp.sentinel.slotchain.ProcessorSlotEntryCallback;
+import com.alibaba.csp.sentinel.slotchain.ProcessorSlotExitCallback;
+
+/**
+ *
+ * Callback registry for {@link StatisticSlot}. Now two kind of callbacks are supported:
+ *
+ * - {@link ProcessorSlotEntryCallback}: callback for entry (passed and blocked)
+ * - {@link ProcessorSlotExitCallback}: callback for exiting {@link StatisticSlot}
+ *
+ *
+ *
+ * @author Eric Zhao
+ * @since 0.2.0
+ */
+public final class StatisticSlotCallbackRegistry {
+
+ private static final Map> entryCallbackMap
+ = new ConcurrentHashMap>();
+
+ private static final Map exitCallbackMap
+ = new ConcurrentHashMap();
+
+ public static void clearEntryCallback() {
+ entryCallbackMap.clear();
+ }
+
+ public static void clearExitCallback() {
+ exitCallbackMap.clear();
+ }
+
+ public static void addEntryCallback(String key, ProcessorSlotEntryCallback callback) {
+ entryCallbackMap.put(key, callback);
+ }
+
+ public static void addExitCallback(String key, ProcessorSlotExitCallback callback) {
+ exitCallbackMap.put(key, callback);
+ }
+
+ public static ProcessorSlotEntryCallback removeEntryCallback(String key) {
+ if (key == null) {
+ return null;
+ }
+ return entryCallbackMap.remove(key);
+ }
+
+ public static ProcessorSlotExitCallback removeExitCallback(String key) {
+ if (key == null) {
+ return null;
+ }
+ return exitCallbackMap.remove(key);
+ }
+
+ public static Collection> getEntryCallbacks() {
+ return entryCallbackMap.values();
+ }
+
+ public static Collection getExitCallbacks() {
+ return exitCallbackMap.values();
+ }
+
+ private StatisticSlotCallbackRegistry() {}
+}
diff --git a/sentinel-demo/pom.xml b/sentinel-demo/pom.xml
index 1ca9b159..1dc8a6bb 100755
--- a/sentinel-demo/pom.xml
+++ b/sentinel-demo/pom.xml
@@ -26,6 +26,7 @@
sentinel-demo-zookeeper-datasource
sentinel-demo-apollo-datasource
sentinel-demo-annotation-spring-aop
+ sentinel-demo-parameter-flow-control
diff --git a/sentinel-demo/sentinel-demo-parameter-flow-control/pom.xml b/sentinel-demo/sentinel-demo-parameter-flow-control/pom.xml
new file mode 100644
index 00000000..b86e8aed
--- /dev/null
+++ b/sentinel-demo/sentinel-demo-parameter-flow-control/pom.xml
@@ -0,0 +1,30 @@
+
+
+
+ sentinel-demo
+ com.alibaba.csp
+ 0.2.0-SNAPSHOT
+
+ 4.0.0
+
+ sentinel-demo-parameter-flow-control
+
+
+ 1.8
+ 1.8
+
+
+
+
+ com.alibaba.csp
+ sentinel-parameter-flow-control
+
+
+
+ com.alibaba.csp
+ sentinel-transport-simple-http
+
+
+
\ No newline at end of file
diff --git a/sentinel-demo/sentinel-demo-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/demo/flow/param/ParamFlowQpsDemo.java b/sentinel-demo/sentinel-demo-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/demo/flow/param/ParamFlowQpsDemo.java
new file mode 100644
index 00000000..84cba516
--- /dev/null
+++ b/sentinel-demo/sentinel-demo-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/demo/flow/param/ParamFlowQpsDemo.java
@@ -0,0 +1,69 @@
+/*
+ * 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.demo.flow.param;
+
+import java.util.Collections;
+
+import com.alibaba.csp.sentinel.slots.block.RuleConstant;
+import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowItem;
+import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
+import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager;
+
+/**
+ * This demo demonstrates flow control by frequent ("hot spot") parameters.
+ *
+ * @author Eric Zhao
+ * @since 0.2.0
+ */
+public class ParamFlowQpsDemo {
+
+ private static final int PARAM_A = 1;
+ private static final int PARAM_B = 2;
+ private static final int PARAM_C = 3;
+ private static final int PARAM_D = 4;
+
+ /**
+ * Here we prepare different parameters to validate flow control by parameters.
+ */
+ private static final Integer[] PARAMS = new Integer[] {PARAM_A, PARAM_B, PARAM_C, PARAM_D};
+
+ private static final String RESOURCE_KEY = "resA";
+
+ public static void main(String[] args) {
+ initHotParamFlowRules();
+
+ final int threadCount = 8;
+ ParamFlowQpsRunner runner = new ParamFlowQpsRunner<>(PARAMS, RESOURCE_KEY, threadCount, 120);
+ runner.simulateTraffic();
+ runner.tick();
+ }
+
+ private static void initHotParamFlowRules() {
+ // QPS mode, threshold is 5 for every frequent "hot spot" parameter in index 0 (the first arg).
+ ParamFlowRule rule = new ParamFlowRule(RESOURCE_KEY)
+ .setParamIdx(0)
+ .setBlockGrade(RuleConstant.FLOW_GRADE_QPS)
+ .setCount(5);
+ // We can set threshold count for specific parameter value individually.
+ // Here we add an exception item. That means: QPS threshold of entries with parameter `PARAM_B` (type: int)
+ // in index 0 will be 10, rather than the global threshold (5).
+ ParamFlowItem item = new ParamFlowItem().setObject(String.valueOf(PARAM_B))
+ .setClassType(int.class.getName())
+ .setCount(10);
+ rule.setParamFlowItemList(Collections.singletonList(item));
+ ParamFlowRuleManager.loadRules(Collections.singletonList(rule));
+ }
+}
diff --git a/sentinel-demo/sentinel-demo-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/demo/flow/param/ParamFlowQpsRunner.java b/sentinel-demo/sentinel-demo-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/demo/flow/param/ParamFlowQpsRunner.java
new file mode 100644
index 00000000..c79c3d98
--- /dev/null
+++ b/sentinel-demo/sentinel-demo-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/demo/flow/param/ParamFlowQpsRunner.java
@@ -0,0 +1,167 @@
+/*
+ * 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.demo.flow.param;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
+import com.alibaba.csp.sentinel.Entry;
+import com.alibaba.csp.sentinel.EntryType;
+import com.alibaba.csp.sentinel.SphU;
+import com.alibaba.csp.sentinel.slots.block.BlockException;
+import com.alibaba.csp.sentinel.util.StringUtil;
+import com.alibaba.csp.sentinel.util.TimeUtil;
+
+/**
+ * A traffic runner to simulate flow for different parameters.
+ *
+ * @author Eric Zhao
+ * @since 0.2.0
+ */
+class ParamFlowQpsRunner {
+
+ private final T[] params;
+ private final String resourceName;
+ private int seconds;
+ private final int threadCount;
+
+ private final Map passCountMap = new ConcurrentHashMap<>();
+
+ private volatile boolean stop = false;
+
+ public ParamFlowQpsRunner(T[] params, String resourceName, int threadCount, int seconds) {
+ assertTrue(params != null && params.length > 0, "Parameter array should not be empty");
+ assertTrue(StringUtil.isNotBlank(resourceName), "Resource name cannot be empty");
+ assertTrue(seconds > 0, "Time period should be positive");
+ assertTrue(threadCount > 0 && threadCount <= 1000, "Invalid thread count");
+ this.params = params;
+ this.resourceName = resourceName;
+ this.seconds = seconds;
+ this.threadCount = threadCount;
+
+ for (T param : params) {
+ assertTrue(param != null, "Parameters should not be null");
+ passCountMap.putIfAbsent(param, new AtomicLong());
+ }
+ }
+
+ private void assertTrue(boolean b, String message) {
+ if (!b) {
+ throw new IllegalArgumentException(message);
+ }
+ }
+
+ /**
+ * Pick one of provided parameters randomly.
+ *
+ * @return picked parameter
+ */
+ private T generateParam() {
+ int i = ThreadLocalRandom.current().nextInt(0, params.length);
+ return params[i];
+ }
+
+ void simulateTraffic() {
+ for (int i = 0; i < threadCount; i++) {
+ Thread t = new Thread(new RunTask());
+ t.setName("sentinel-simulate-traffic-task-" + i);
+ t.start();
+ }
+ }
+
+ void tick() {
+ Thread timer = new Thread(new TimerTask());
+ timer.setName("sentinel-timer-task");
+ timer.start();
+ }
+
+ private void passFor(T param) {
+ passCountMap.get(param).incrementAndGet();
+ }
+
+ final class RunTask implements Runnable {
+ @Override
+ public void run() {
+ while (!stop) {
+ Entry entry = null;
+
+ try {
+ T param = generateParam();
+ entry = SphU.entry(resourceName, EntryType.IN, 1, param);
+ // Add pass for parameter.
+ passFor(param);
+ } catch (BlockException e1) {
+ // block.incrementAndGet();
+ } catch (Exception e2) {
+ // biz exception
+ } finally {
+ // total.incrementAndGet();
+ if (entry != null) {
+ entry.exit();
+ }
+ }
+
+ try {
+ TimeUnit.MILLISECONDS.sleep(ThreadLocalRandom.current().nextInt(0, 10));
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ }
+ }
+ }
+
+ final class TimerTask implements Runnable {
+ @Override
+ public void run() {
+ long start = System.currentTimeMillis();
+ System.out.println("Begin to run! Go go go!");
+ System.out.println("See corresponding metrics.log for accurate statistic data");
+
+ Map map = new HashMap<>(params.length);
+ for (T param : params) {
+ map.putIfAbsent(param, 0L);
+ }
+ while (!stop) {
+ try {
+ TimeUnit.SECONDS.sleep(1);
+ } catch (InterruptedException e) {
+ }
+ // There may be a mismatch for time window of internal sliding window.
+ // See corresponding `metrics.log` for accurate statistic log.
+ for (T param : params) {
+ long globalPass = passCountMap.get(param).get();
+ long oldPass = map.get(param);
+ long oneSecondPass = globalPass - oldPass;
+ map.put(param, globalPass);
+ System.out.println(String.format("[%d][%d] Hot param metrics for resource %s: "
+ + "pass count for param <%s> is %d",
+ seconds, TimeUtil.currentTimeMillis(), resourceName, param, oneSecondPass));
+ }
+ if (seconds-- <= 0) {
+ stop = true;
+ }
+ }
+
+ long cost = System.currentTimeMillis() - start;
+ System.out.println("Time cost: " + cost + " ms");
+ System.exit(0);
+ }
+ }
+}
diff --git a/sentinel-extension/pom.xml b/sentinel-extension/pom.xml
index 876121b7..a472bf62 100755
--- a/sentinel-extension/pom.xml
+++ b/sentinel-extension/pom.xml
@@ -18,6 +18,7 @@
sentinel-datasource-apollo
sentinel-datasource-redis
sentinel-annotation-aspectj
+ sentinel-parameter-flow-control
diff --git a/sentinel-extension/sentinel-parameter-flow-control/README.md b/sentinel-extension/sentinel-parameter-flow-control/README.md
new file mode 100644
index 00000000..5fe44e0d
--- /dev/null
+++ b/sentinel-extension/sentinel-parameter-flow-control/README.md
@@ -0,0 +1,61 @@
+# Sentinel Parameter Flow Control
+
+This component provides functionality of flow control by frequent ("hot spot") parameters.
+
+## Usage
+
+To use Sentinel Parameter Flow Control, you need to add the following dependency to `pom.xml`:
+
+```xml
+
+ com.alibaba.csp
+ sentinel-parameter-flow-control
+ x.y.z
+
+```
+
+First you need to pass parameters with the following `SphU.entry` overloaded methods:
+
+```java
+public static Entry entry(String name, EntryType type, int count, Object... args) throws BlockException
+
+public static Entry entry(Method method, EntryType type, int count, Object... args) throws BlockException
+```
+
+For example, if there are two parameters to provide, you can:
+
+```java
+// paramA in index 0, paramB in index 1.
+SphU.entry(resourceName, EntryType.IN, 1, paramA, paramB);
+```
+
+Then you can configure parameter flow control rules via `loadRules` method in `ParamFlowRuleManager`:
+
+```java
+// QPS mode, threshold is 5 for every frequent "hot spot" parameter in index 0 (the first arg).
+ParamFlowRule rule = new ParamFlowRule(RESOURCE_KEY)
+ .setParamIdx(0)
+ .setCount(5);
+// We can set threshold count for specific parameter value individually.
+// Here we add an exception item. That means: QPS threshold of entries with parameter `PARAM_B` (type: int)
+// in index 0 will be 10, rather than the global threshold (5).
+ParamFlowItem item = new ParamFlowItem().setObject(String.valueOf(PARAM_B))
+ .setClassType(int.class.getName())
+ .setCount(10);
+rule.setParamFlowItemList(Collections.singletonList(item));
+ParamFlowRuleManager.loadRules(Collections.singletonList(rule));
+```
+
+The description for fields of `ParamFlowRule`:
+
+| Field | Description | Default |
+| :----: | :----| :----|
+| resource| resource name (**required**) ||
+| count | flow control threshold (**required**) ||
+| blockGrade | flow control mode (only QPS mode is supported) | QPS mode |
+| paramIdx | the index of provided parameter in `SphU.entry(xxx, args)` (**required**) ||
+| paramFlowItemList | the exception items of parameter; you can set threshold to a specific parameter value ||
+
+
+Now the parameter flow control rules will take effect.
+
diff --git a/sentinel-extension/sentinel-parameter-flow-control/pom.xml b/sentinel-extension/sentinel-parameter-flow-control/pom.xml
new file mode 100644
index 00000000..9511a743
--- /dev/null
+++ b/sentinel-extension/sentinel-parameter-flow-control/pom.xml
@@ -0,0 +1,43 @@
+
+
+
+ sentinel-extension
+ com.alibaba.csp
+ 0.2.0-SNAPSHOT
+
+ 4.0.0
+
+ sentinel-parameter-flow-control
+ jar
+
+
+
+ com.alibaba.csp
+ sentinel-core
+
+
+ com.alibaba.csp
+ sentinel-transport-common
+ provided
+
+
+
+ com.googlecode.concurrentlinkedhashmap
+ concurrentlinkedhashmap-lru
+ 1.4.2
+
+
+
+ junit
+ junit
+ test
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+
\ No newline at end of file
diff --git a/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/command/handler/FetchTopParamsCommandHandler.java b/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/command/handler/FetchTopParamsCommandHandler.java
new file mode 100644
index 00000000..ddebeaf6
--- /dev/null
+++ b/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/command/handler/FetchTopParamsCommandHandler.java
@@ -0,0 +1,64 @@
+/*
+ * 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.command.handler;
+
+import java.util.Map;
+
+import com.alibaba.csp.sentinel.command.CommandHandler;
+import com.alibaba.csp.sentinel.command.CommandRequest;
+import com.alibaba.csp.sentinel.command.CommandResponse;
+import com.alibaba.csp.sentinel.command.annotation.CommandMapping;
+import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowSlot;
+import com.alibaba.csp.sentinel.slots.block.flow.param.ParameterMetric;
+import com.alibaba.csp.sentinel.util.StringUtil;
+import com.alibaba.fastjson.JSON;
+
+/**
+ * @author Eric Zhao
+ * @since 0.2.0
+ */
+@CommandMapping(name = "topParams")
+public class FetchTopParamsCommandHandler implements CommandHandler {
+
+ @Override
+ public CommandResponse handle(CommandRequest request) {
+ String resourceName = request.getParam("res");
+ if (StringUtil.isBlank(resourceName)) {
+ return CommandResponse.ofFailure(new IllegalArgumentException("Invalid parameter: res"));
+ }
+ String idx = request.getParam("idx");
+ int index;
+ try {
+ index = Integer.valueOf(idx);
+ } catch (Exception ex) {
+ return CommandResponse.ofFailure(ex, "Invalid parameter: idx");
+ }
+ String n = request.getParam("n");
+ int amount;
+ try {
+ amount = Integer.valueOf(n);
+ } catch (Exception ex) {
+ return CommandResponse.ofFailure(ex, "Invalid parameter: n");
+ }
+ ParameterMetric metric = ParamFlowSlot.getHotParamMetricForName(resourceName);
+ if (metric == null) {
+ return CommandResponse.ofSuccess("{}");
+ }
+ Map