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 params); + TokenResult requestParamToken(Long ruleId, int acquireCount, Collection params); } diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/ClusterFlowConfig.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/ClusterFlowConfig.java index 506701e2..b12a0743 100644 --- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/ClusterFlowConfig.java +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/ClusterFlowConfig.java @@ -28,7 +28,7 @@ public class ClusterFlowConfig { /** * Global unique ID. */ - private Integer flowId; + private Long flowId; /** * Threshold type (average by local value or global value). @@ -41,15 +41,15 @@ public class ClusterFlowConfig { */ private int strategy = ClusterRuleConstant.FLOW_CLUSTER_STRATEGY_NORMAL; - private Integer refFlowId; + private Long refFlowId; private int refSampleCount = 10; private double refRatio = 1d; - public Integer getFlowId() { + public Long getFlowId() { return flowId; } - public ClusterFlowConfig setFlowId(Integer flowId) { + public ClusterFlowConfig setFlowId(Long flowId) { this.flowId = flowId; return this; } @@ -72,11 +72,11 @@ public class ClusterFlowConfig { return this; } - public Integer getRefFlowId() { + public Long getRefFlowId() { return refFlowId; } - public ClusterFlowConfig setRefFlowId(Integer refFlowId) { + public ClusterFlowConfig setRefFlowId(Long refFlowId) { this.refFlowId = refFlowId; return this; } 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 index aa5a58c6..bea269f1 100644 --- 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 @@ -155,7 +155,7 @@ public final class FlowRuleUtil { * @param id flow ID to check * @return true if valid, otherwise false */ - public static boolean validClusterRuleId(Integer id) { + public static boolean validClusterRuleId(Long id) { return id != null && id > 0; } diff --git a/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowChecker.java b/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowChecker.java index f98ef04d..2488643f 100644 --- a/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowChecker.java +++ b/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowChecker.java @@ -16,14 +16,23 @@ package com.alibaba.csp.sentinel.slots.block.flow.param; import java.lang.reflect.Array; +import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; +import java.util.List; import java.util.Set; +import com.alibaba.csp.sentinel.cluster.ClusterTokenClient; +import com.alibaba.csp.sentinel.cluster.TokenClientProvider; +import com.alibaba.csp.sentinel.cluster.TokenResult; +import com.alibaba.csp.sentinel.cluster.TokenResultStatus; import com.alibaba.csp.sentinel.log.RecordLog; import com.alibaba.csp.sentinel.slotchain.ResourceWrapper; import com.alibaba.csp.sentinel.slots.block.RuleConstant; /** + * Rule checker for parameter flow control. + * * @author Eric Zhao * @since 0.2.0 */ @@ -40,17 +49,72 @@ final class ParamFlowChecker { return true; } + // Get parameter value. If value is null, then pass. Object value = args[paramIdx]; + if (value == null) { + return true; + } + + if (rule.isClusterMode()) { + return passClusterCheck(resourceWrapper, rule, count, value); + } return passLocalCheck(resourceWrapper, rule, count, value); } - private static ParameterMetric getHotParameters(ResourceWrapper resourceWrapper) { - // Should not be null. - return ParamFlowSlot.getParamMetric(resourceWrapper); + @SuppressWarnings("unchecked") + private static Collection toCollection(Object value) { + if (value instanceof Collection) { + return (Collection)value; + } else if (value.getClass().isArray()) { + List params = new ArrayList(); + int length = Array.getLength(value); + for (int i = 0; i < length; i++) { + Object param = Array.get(value, i); + params.add(param); + } + return params; + } else { + return Collections.singletonList(value); + } } - private static boolean passLocalCheck(ResourceWrapper resourceWrapper, ParamFlowRule rule, int count, Object value) { + private static boolean passClusterCheck(ResourceWrapper resourceWrapper, ParamFlowRule rule, int count, + Object value) { + try { + ClusterTokenClient client = TokenClientProvider.getClient(); + if (client == null) { + return true; + } + Collection params = toCollection(value); + + TokenResult result = client.requestParamToken(rule.getClusterConfig().getFlowId(), count, params); + switch (result.getStatus()) { + case TokenResultStatus.OK: + return true; + case TokenResultStatus.BLOCKED: + return false; + default: + return fallbackToLocalOrPass(resourceWrapper, rule, count, params); + } + } catch (Throwable ex) { + RecordLog.warn("[ParamFlowChecker] Request cluster token for parameter unexpected failed", ex); + return fallbackToLocalOrPass(resourceWrapper, rule, count, value); + } + } + + private static boolean fallbackToLocalOrPass(ResourceWrapper resourceWrapper, ParamFlowRule rule, int count, + Object value) { + if (rule.getClusterConfig().isFallbackToLocalWhenFail()) { + return passLocalCheck(resourceWrapper, rule, count, value); + } else { + // The rule won't be activated, just pass. + return true; + } + } + + private static boolean passLocalCheck(ResourceWrapper resourceWrapper, ParamFlowRule rule, int count, + Object value) { try { if (Collection.class.isAssignableFrom(value.getClass())) { for (Object param : ((Collection)value)) { @@ -70,7 +134,7 @@ final class ParamFlowChecker { return passSingleValueCheck(resourceWrapper, rule, count, value); } } catch (Throwable e) { - RecordLog.info("[ParamFlowChecker] Unexpected error", e); + RecordLog.warn("[ParamFlowChecker] Unexpected error", e); } return true; @@ -96,5 +160,10 @@ final class ParamFlowChecker { return true; } + private static ParameterMetric getHotParameters(ResourceWrapper resourceWrapper) { + // Should not be null. + return ParamFlowSlot.getParamMetric(resourceWrapper); + } + private ParamFlowChecker() {} } diff --git a/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowClusterConfig.java b/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowClusterConfig.java new file mode 100644 index 00000000..2a532df6 --- /dev/null +++ b/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowClusterConfig.java @@ -0,0 +1,94 @@ +/* + * 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.param; + +import com.alibaba.csp.sentinel.slots.block.ClusterRuleConstant; + +/** + * Parameter flow rule config in cluster mode. + * + * @author Eric Zhao + * @since 1.4.0 + */ +public class ParamFlowClusterConfig { + + /** + * Global unique ID. + */ + private Long flowId; + + /** + * Threshold type (average by local value or global value). + */ + private int thresholdType = ClusterRuleConstant.FLOW_THRESHOLD_AVG_LOCAL; + private boolean fallbackToLocalWhenFail = false; + + public Long getFlowId() { + return flowId; + } + + public ParamFlowClusterConfig setFlowId(Long flowId) { + this.flowId = flowId; + return this; + } + + public int getThresholdType() { + return thresholdType; + } + + public ParamFlowClusterConfig setThresholdType(int thresholdType) { + this.thresholdType = thresholdType; + return this; + } + + public boolean isFallbackToLocalWhenFail() { + return fallbackToLocalWhenFail; + } + + public ParamFlowClusterConfig setFallbackToLocalWhenFail(boolean fallbackToLocalWhenFail) { + this.fallbackToLocalWhenFail = fallbackToLocalWhenFail; + return this; + } + + @Override + public boolean equals(Object o) { + if (this == o) { return true; } + if (o == null || getClass() != o.getClass()) { return false; } + + ParamFlowClusterConfig that = (ParamFlowClusterConfig)o; + + if (thresholdType != that.thresholdType) { return false; } + if (fallbackToLocalWhenFail != that.fallbackToLocalWhenFail) { return false; } + return flowId != null ? flowId.equals(that.flowId) : that.flowId == null; + } + + @Override + public int hashCode() { + int result = flowId != null ? flowId.hashCode() : 0; + result = 31 * result + thresholdType; + result = 31 * result + (fallbackToLocalWhenFail ? 1 : 0); + return result; + } + + @Override + public String toString() { + return "ParamFlowClusterConfig{" + + "flowId=" + flowId + + ", thresholdType=" + thresholdType + + ", fallbackToLocalWhenFail=" + fallbackToLocalWhenFail + + '}'; + } +} diff --git a/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRule.java b/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRule.java index 51f7b16a..18f6db16 100644 --- a/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRule.java +++ b/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRule.java @@ -65,6 +65,9 @@ public class ParamFlowRule extends AbstractRule { */ private Map hotItems = new HashMap(); + private boolean clusterMode = false; + private ParamFlowClusterConfig clusterConfig; + public int getGrade() { return grade; } @@ -110,6 +113,25 @@ public class ParamFlowRule extends AbstractRule { return this; } + public boolean isClusterMode() { + return clusterMode; + } + + public ParamFlowRule setClusterMode(boolean clusterMode) { + this.clusterMode = clusterMode; + return this; + } + + public ParamFlowClusterConfig getClusterConfig() { + return clusterConfig; + } + + public ParamFlowRule setClusterConfig( + ParamFlowClusterConfig clusterConfig) { + this.clusterConfig = clusterConfig; + return this; + } + @Override @Deprecated public boolean passCheck(Context context, DefaultNode node, int count, Object... args) { @@ -126,8 +148,11 @@ public class ParamFlowRule extends AbstractRule { if (grade != rule.grade) { return false; } if (Double.compare(rule.count, count) != 0) { return false; } + if (clusterMode != rule.clusterMode) { return false; } if (paramIdx != null ? !paramIdx.equals(rule.paramIdx) : rule.paramIdx != null) { return false; } - return paramFlowItemList != null ? paramFlowItemList.equals(rule.paramFlowItemList) : rule.paramFlowItemList == null; + if (paramFlowItemList != null ? !paramFlowItemList.equals(rule.paramFlowItemList) + : rule.paramFlowItemList != null) { return false; } + return clusterConfig != null ? clusterConfig.equals(rule.clusterConfig) : rule.clusterConfig == null; } @Override @@ -139,18 +164,20 @@ public class ParamFlowRule extends AbstractRule { temp = Double.doubleToLongBits(count); result = 31 * result + (int)(temp ^ (temp >>> 32)); result = 31 * result + (paramFlowItemList != null ? paramFlowItemList.hashCode() : 0); + result = 31 * result + (clusterMode ? 1 : 0); + result = 31 * result + (clusterConfig != null ? clusterConfig.hashCode() : 0); return result; } @Override public String toString() { return "ParamFlowRule{" + - "resource=" + getResource() + - ", limitApp=" + getLimitApp() + - ", grade=" + grade + + "grade=" + grade + ", paramIdx=" + paramIdx + ", count=" + count + ", paramFlowItemList=" + paramFlowItemList + + ", clusterMode=" + clusterMode + + ", clusterConfig=" + clusterConfig + '}'; } } diff --git a/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRuleManager.java b/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRuleManager.java index b10b260a..3f60b840 100644 --- a/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRuleManager.java +++ b/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRuleManager.java @@ -99,37 +99,6 @@ public final class ParamFlowRuleManager { return rules; } - private static Object parseValue(String value, String classType) { - if (value == null) { - throw new IllegalArgumentException("Null value"); - } - if (StringUtil.isBlank(classType)) { - // If the class type is not provided, then treat it as string. - return value; - } - // Handle primitive type. - if (int.class.toString().equals(classType) || Integer.class.getName().equals(classType)) { - return Integer.parseInt(value); - } else if (boolean.class.toString().equals(classType) || Boolean.class.getName().equals(classType)) { - return Boolean.parseBoolean(value); - } else if (long.class.toString().equals(classType) || Long.class.getName().equals(classType)) { - return Long.parseLong(value); - } else if (double.class.toString().equals(classType) || Double.class.getName().equals(classType)) { - return Double.parseDouble(value); - } else if (float.class.toString().equals(classType) || Float.class.getName().equals(classType)) { - return Float.parseFloat(value); - } else if (byte.class.toString().equals(classType) || Byte.class.getName().equals(classType)) { - return Byte.parseByte(value); - } else if (short.class.toString().equals(classType) || Short.class.getName().equals(classType)) { - return Short.parseShort(value); - } else if (char.class.toString().equals(classType)) { - char[] array = value.toCharArray(); - return array.length > 0 ? array[0] : null; - } - - return value; - } - static class RulePropertyListener implements PropertyListener> { @Override @@ -163,7 +132,7 @@ public final class ParamFlowRuleManager { } for (ParamFlowRule rule : list) { - if (!isValidRule(rule)) { + if (!ParamFlowRuleUtil.isValidRule(rule)) { RecordLog.warn("[ParamFlowRuleManager] Ignoring invalid rule when loading new rules: " + rule); continue; } @@ -172,12 +141,7 @@ public final class ParamFlowRuleManager { rule.setLimitApp(RuleConstant.LIMIT_APP_DEFAULT); } - if (rule.getParamFlowItemList() == null) { - rule.setParamFlowItemList(new ArrayList()); - } - - Map itemMap = parseHotItems(rule.getParamFlowItemList()); - rule.setParsedHotItems(itemMap); + ParamFlowRuleUtil.fillExceptionFlowItems(rule); String resourceName = rule.getResource(); List ruleList = newRuleMap.get(resourceName); @@ -200,34 +164,6 @@ public final class ParamFlowRuleManager { } } - static Map parseHotItems(List items) { - Map itemMap = new HashMap(); - if (items == null || items.isEmpty()) { - return itemMap; - } - for (ParamFlowItem item : items) { - // Value should not be null. - Object value; - try { - value = parseValue(item.getObject(), item.getClassType()); - } catch (Exception ex) { - RecordLog.warn("[ParamFlowRuleManager] Failed to parse value for item: " + item, ex); - continue; - } - if (item.getCount() == null || item.getCount() < 0 || value == null) { - RecordLog.warn("[ParamFlowRuleManager] Ignoring invalid exclusion parameter item: " + item); - continue; - } - itemMap.put(value, item.getCount()); - } - return itemMap; - } - - static boolean isValidRule(ParamFlowRule rule) { - return rule != null && !StringUtil.isBlank(rule.getResource()) && rule.getCount() >= 0 - && rule.getParamIdx() != null && rule.getParamIdx() >= 0; - } - private ParamFlowRuleManager() {} } diff --git a/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRuleUtil.java b/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRuleUtil.java new file mode 100644 index 00000000..ec97b19c --- /dev/null +++ b/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRuleUtil.java @@ -0,0 +1,114 @@ +/* + * 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.param; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import com.alibaba.csp.sentinel.log.RecordLog; +import com.alibaba.csp.sentinel.util.StringUtil; + +/** + * @author Eric Zhao + */ +public final class ParamFlowRuleUtil { + + public static boolean isValidRule(ParamFlowRule rule) { + return rule != null && !StringUtil.isBlank(rule.getResource()) && rule.getCount() >= 0 + && rule.getParamIdx() != null && rule.getParamIdx() >= 0 && checkCluster(rule); + } + + private static boolean checkCluster(/*@PreChecked*/ ParamFlowRule rule) { + if (!rule.isClusterMode()) { + return true; + } + ParamFlowClusterConfig clusterConfig = rule.getClusterConfig(); + return clusterConfig != null && validClusterRuleId(clusterConfig.getFlowId()); + } + + public static boolean validClusterRuleId(Long id) { + return id != null && id > 0; + } + + public static void fillExceptionFlowItems(ParamFlowRule rule) { + if (rule != null) { + if (rule.getParamFlowItemList() == null) { + rule.setParamFlowItemList(new ArrayList()); + } + + Map itemMap = parseHotItems(rule.getParamFlowItemList()); + rule.setParsedHotItems(itemMap); + } + } + + static Map parseHotItems(List items) { + Map itemMap = new HashMap(); + if (items == null || items.isEmpty()) { + return itemMap; + } + for (ParamFlowItem item : items) { + // Value should not be null. + Object value; + try { + value = parseItemValue(item.getObject(), item.getClassType()); + } catch (Exception ex) { + RecordLog.warn("[ParamFlowRuleUtil] Failed to parse value for item: " + item, ex); + continue; + } + if (item.getCount() == null || item.getCount() < 0 || value == null) { + RecordLog.warn("[ParamFlowRuleUtil] Ignoring invalid exclusion parameter item: " + item); + continue; + } + itemMap.put(value, item.getCount()); + } + return itemMap; + } + + static Object parseItemValue(String value, String classType) { + if (value == null) { + throw new IllegalArgumentException("Null value"); + } + if (StringUtil.isBlank(classType)) { + // If the class type is not provided, then treat it as string. + return value; + } + // Handle primitive type. + if (int.class.toString().equals(classType) || Integer.class.getName().equals(classType)) { + return Integer.parseInt(value); + } else if (boolean.class.toString().equals(classType) || Boolean.class.getName().equals(classType)) { + return Boolean.parseBoolean(value); + } else if (long.class.toString().equals(classType) || Long.class.getName().equals(classType)) { + return Long.parseLong(value); + } else if (double.class.toString().equals(classType) || Double.class.getName().equals(classType)) { + return Double.parseDouble(value); + } else if (float.class.toString().equals(classType) || Float.class.getName().equals(classType)) { + return Float.parseFloat(value); + } else if (byte.class.toString().equals(classType) || Byte.class.getName().equals(classType)) { + return Byte.parseByte(value); + } else if (short.class.toString().equals(classType) || Short.class.getName().equals(classType)) { + return Short.parseShort(value); + } else if (char.class.toString().equals(classType)) { + char[] array = value.toCharArray(); + return array.length > 0 ? array[0] : null; + } + + return value; + } + + private ParamFlowRuleUtil() {} +} diff --git a/sentinel-extension/sentinel-parameter-flow-control/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRuleManagerTest.java b/sentinel-extension/sentinel-parameter-flow-control/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRuleManagerTest.java index 6f27a934..a6ae69c0 100644 --- a/sentinel-extension/sentinel-parameter-flow-control/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRuleManagerTest.java +++ b/sentinel-extension/sentinel-parameter-flow-control/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRuleManagerTest.java @@ -15,11 +15,9 @@ */ package com.alibaba.csp.sentinel.slots.block.flow.param; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; -import java.util.Map; import com.alibaba.csp.sentinel.EntryType; import com.alibaba.csp.sentinel.slotchain.StringResourceWrapper; @@ -109,84 +107,4 @@ public class ParamFlowRuleManagerTest { assertTrue(allRules.contains(ruleC)); assertTrue(allRules.contains(ruleD)); } - - @Test - public void testParseHotParamExceptionItemsFailure() { - String valueB = "Sentinel"; - Integer valueC = 6; - char valueD = 6; - float valueE = 11.11f; - // Null object will not be parsed. - ParamFlowItem itemA = new ParamFlowItem(null, 1, double.class.getName()); - // Hot item with empty class type will be treated as string. - ParamFlowItem itemB = new ParamFlowItem(valueB, 3, null); - ParamFlowItem itemE = new ParamFlowItem(String.valueOf(valueE), 3, ""); - // Bad count will not be parsed. - ParamFlowItem itemC = ParamFlowItem.newItem(valueC, -5); - ParamFlowItem itemD = new ParamFlowItem(String.valueOf(valueD), null, char.class.getName()); - - List badItems = Arrays.asList(itemA, itemB, itemC, itemD, itemE); - Map parsedItems = ParamFlowRuleManager.parseHotItems(badItems); - - // Value B and E will be parsed, but ignoring the type. - assertEquals(2, parsedItems.size()); - assertEquals(itemB.getCount(), parsedItems.get(valueB)); - assertFalse(parsedItems.containsKey(valueE)); - assertEquals(itemE.getCount(), parsedItems.get(String.valueOf(valueE))); - } - - @Test - public void testParseHotParamExceptionItemsSuccess() { - // Test for empty list. - assertEquals(0, ParamFlowRuleManager.parseHotItems(null).size()); - assertEquals(0, ParamFlowRuleManager.parseHotItems(new ArrayList()).size()); - - // Test for boxing objects and primitive types. - Double valueA = 1.1d; - String valueB = "Sentinel"; - Integer valueC = 6; - char valueD = 'c'; - ParamFlowItem itemA = ParamFlowItem.newItem(valueA, 1); - ParamFlowItem itemB = ParamFlowItem.newItem(valueB, 3); - ParamFlowItem itemC = ParamFlowItem.newItem(valueC, 5); - ParamFlowItem itemD = new ParamFlowItem().setObject(String.valueOf(valueD)) - .setClassType(char.class.getName()) - .setCount(7); - List items = Arrays.asList(itemA, itemB, itemC, itemD); - Map parsedItems = ParamFlowRuleManager.parseHotItems(items); - assertEquals(itemA.getCount(), parsedItems.get(valueA)); - assertEquals(itemB.getCount(), parsedItems.get(valueB)); - assertEquals(itemC.getCount(), parsedItems.get(valueC)); - assertEquals(itemD.getCount(), parsedItems.get(valueD)); - } - - @Test - public void testCheckValidHotParamRule() { - // Null or empty resource; - ParamFlowRule rule1 = new ParamFlowRule(); - ParamFlowRule rule2 = new ParamFlowRule(""); - assertFalse(ParamFlowRuleManager.isValidRule(null)); - assertFalse(ParamFlowRuleManager.isValidRule(rule1)); - assertFalse(ParamFlowRuleManager.isValidRule(rule2)); - - // Invalid threshold count. - ParamFlowRule rule3 = new ParamFlowRule("abc") - .setCount(-1) - .setParamIdx(1); - assertFalse(ParamFlowRuleManager.isValidRule(rule3)); - - // Parameter index not set or invalid. - ParamFlowRule rule4 = new ParamFlowRule("abc") - .setCount(1); - ParamFlowRule rule5 = new ParamFlowRule("abc") - .setCount(1) - .setParamIdx(-1); - assertFalse(ParamFlowRuleManager.isValidRule(rule4)); - assertFalse(ParamFlowRuleManager.isValidRule(rule5)); - - ParamFlowRule goodRule = new ParamFlowRule("abc") - .setCount(10) - .setParamIdx(1); - assertTrue(ParamFlowRuleManager.isValidRule(goodRule)); - } } \ No newline at end of file diff --git a/sentinel-extension/sentinel-parameter-flow-control/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRuleUtilTest.java b/sentinel-extension/sentinel-parameter-flow-control/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRuleUtilTest.java new file mode 100644 index 00000000..af49f476 --- /dev/null +++ b/sentinel-extension/sentinel-parameter-flow-control/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRuleUtilTest.java @@ -0,0 +1,95 @@ +package com.alibaba.csp.sentinel.slots.block.flow.param; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * @author Eric Zhao + */ +public class ParamFlowRuleUtilTest { + @Test + public void testCheckValidHotParamRule() { + // Null or empty resource; + ParamFlowRule rule1 = new ParamFlowRule(); + ParamFlowRule rule2 = new ParamFlowRule(""); + assertFalse(ParamFlowRuleUtil.isValidRule(null)); + assertFalse(ParamFlowRuleUtil.isValidRule(rule1)); + assertFalse(ParamFlowRuleUtil.isValidRule(rule2)); + + // Invalid threshold count. + ParamFlowRule rule3 = new ParamFlowRule("abc") + .setCount(-1) + .setParamIdx(1); + assertFalse(ParamFlowRuleUtil.isValidRule(rule3)); + + // Parameter index not set or invalid. + ParamFlowRule rule4 = new ParamFlowRule("abc") + .setCount(1); + ParamFlowRule rule5 = new ParamFlowRule("abc") + .setCount(1) + .setParamIdx(-1); + assertFalse(ParamFlowRuleUtil.isValidRule(rule4)); + assertFalse(ParamFlowRuleUtil.isValidRule(rule5)); + + ParamFlowRule goodRule = new ParamFlowRule("abc") + .setCount(10) + .setParamIdx(1); + assertTrue(ParamFlowRuleUtil.isValidRule(goodRule)); + } + + @Test + public void testParseHotParamExceptionItemsFailure() { + String valueB = "Sentinel"; + Integer valueC = 6; + char valueD = 6; + float valueE = 11.11f; + // Null object will not be parsed. + ParamFlowItem itemA = new ParamFlowItem(null, 1, double.class.getName()); + // Hot item with empty class type will be treated as string. + ParamFlowItem itemB = new ParamFlowItem(valueB, 3, null); + ParamFlowItem itemE = new ParamFlowItem(String.valueOf(valueE), 3, ""); + // Bad count will not be parsed. + ParamFlowItem itemC = ParamFlowItem.newItem(valueC, -5); + ParamFlowItem itemD = new ParamFlowItem(String.valueOf(valueD), null, char.class.getName()); + + List badItems = Arrays.asList(itemA, itemB, itemC, itemD, itemE); + Map parsedItems = ParamFlowRuleUtil.parseHotItems(badItems); + + // Value B and E will be parsed, but ignoring the type. + assertEquals(2, parsedItems.size()); + assertEquals(itemB.getCount(), parsedItems.get(valueB)); + assertFalse(parsedItems.containsKey(valueE)); + assertEquals(itemE.getCount(), parsedItems.get(String.valueOf(valueE))); + } + + @Test + public void testParseHotParamExceptionItemsSuccess() { + // Test for empty list. + assertEquals(0, ParamFlowRuleUtil.parseHotItems(null).size()); + assertEquals(0, ParamFlowRuleUtil.parseHotItems(new ArrayList()).size()); + + // Test for boxing objects and primitive types. + Double valueA = 1.1d; + String valueB = "Sentinel"; + Integer valueC = 6; + char valueD = 'c'; + ParamFlowItem itemA = ParamFlowItem.newItem(valueA, 1); + ParamFlowItem itemB = ParamFlowItem.newItem(valueB, 3); + ParamFlowItem itemC = ParamFlowItem.newItem(valueC, 5); + ParamFlowItem itemD = new ParamFlowItem().setObject(String.valueOf(valueD)) + .setClassType(char.class.getName()) + .setCount(7); + List items = Arrays.asList(itemA, itemB, itemC, itemD); + Map parsedItems = ParamFlowRuleUtil.parseHotItems(items); + assertEquals(itemA.getCount(), parsedItems.get(valueA)); + assertEquals(itemB.getCount(), parsedItems.get(valueB)); + assertEquals(itemC.getCount(), parsedItems.get(valueC)); + assertEquals(itemD.getCount(), parsedItems.get(valueD)); + } +} \ No newline at end of file