- 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 <sczyh16@gmail.com>master
@@ -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<Object> params); | |||
TokenResult requestParamToken(Long ruleId, int acquireCount, Collection<Object> params); | |||
} |
@@ -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; | |||
} | |||
@@ -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; | |||
} | |||
@@ -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<Object> toCollection(Object value) { | |||
if (value instanceof Collection) { | |||
return (Collection<Object>)value; | |||
} else if (value.getClass().isArray()) { | |||
List<Object> params = new ArrayList<Object>(); | |||
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<Object> 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() {} | |||
} |
@@ -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 + | |||
'}'; | |||
} | |||
} |
@@ -65,6 +65,9 @@ public class ParamFlowRule extends AbstractRule { | |||
*/ | |||
private Map<Object, Integer> hotItems = new HashMap<Object, Integer>(); | |||
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 + | |||
'}'; | |||
} | |||
} |
@@ -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<List<ParamFlowRule>> { | |||
@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<ParamFlowItem>()); | |||
} | |||
Map<Object, Integer> itemMap = parseHotItems(rule.getParamFlowItemList()); | |||
rule.setParsedHotItems(itemMap); | |||
ParamFlowRuleUtil.fillExceptionFlowItems(rule); | |||
String resourceName = rule.getResource(); | |||
List<ParamFlowRule> ruleList = newRuleMap.get(resourceName); | |||
@@ -200,34 +164,6 @@ public final class ParamFlowRuleManager { | |||
} | |||
} | |||
static Map<Object, Integer> parseHotItems(List<ParamFlowItem> items) { | |||
Map<Object, Integer> itemMap = new HashMap<Object, Integer>(); | |||
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() {} | |||
} | |||
@@ -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<ParamFlowItem>()); | |||
} | |||
Map<Object, Integer> itemMap = parseHotItems(rule.getParamFlowItemList()); | |||
rule.setParsedHotItems(itemMap); | |||
} | |||
} | |||
static Map<Object, Integer> parseHotItems(List<ParamFlowItem> items) { | |||
Map<Object, Integer> itemMap = new HashMap<Object, Integer>(); | |||
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() {} | |||
} |
@@ -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<ParamFlowItem> badItems = Arrays.asList(itemA, itemB, itemC, itemD, itemE); | |||
Map<Object, Integer> 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<ParamFlowItem>()).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<ParamFlowItem> items = Arrays.asList(itemA, itemB, itemC, itemD); | |||
Map<Object, Integer> 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)); | |||
} | |||
} |
@@ -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<ParamFlowItem> badItems = Arrays.asList(itemA, itemB, itemC, itemD, itemE); | |||
Map<Object, Integer> 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<ParamFlowItem>()).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<ParamFlowItem> items = Arrays.asList(itemA, itemB, itemC, itemD); | |||
Map<Object, Integer> 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)); | |||
} | |||
} |