Browse Source

Update parameter flow rule to adapt to cluster mode and extract rule util class

- Update ParamFlowRule to support cluster mode
- Add `ParamFlowClusterConfig` to provide cluster mode items for the rule
- Update ParamFlowChecker to support cluster flow mode
- Extract ParamFlowRuleUtil class
- Change type of `flowId` from Integer to Long

Signed-off-by: Eric Zhao <sczyh16@gmail.com>
master
Eric Zhao 6 years ago
parent
commit
1043648fbc
10 changed files with 419 additions and 166 deletions
  1. +2
    -2
      sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenService.java
  2. +6
    -6
      sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/ClusterFlowConfig.java
  3. +1
    -1
      sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/FlowRuleUtil.java
  4. +74
    -5
      sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowChecker.java
  5. +94
    -0
      sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowClusterConfig.java
  6. +31
    -4
      sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRule.java
  7. +2
    -66
      sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRuleManager.java
  8. +114
    -0
      sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRuleUtil.java
  9. +0
    -82
      sentinel-extension/sentinel-parameter-flow-control/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRuleManagerTest.java
  10. +95
    -0
      sentinel-extension/sentinel-parameter-flow-control/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRuleUtilTest.java

+ 2
- 2
sentinel-core/src/main/java/com/alibaba/csp/sentinel/cluster/TokenService.java View File

@@ -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);
}

+ 6
- 6
sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/ClusterFlowConfig.java View File

@@ -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;
}


+ 1
- 1
sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/FlowRuleUtil.java View File

@@ -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;
}



+ 74
- 5
sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowChecker.java View File

@@ -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() {}
}

+ 94
- 0
sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowClusterConfig.java View File

@@ -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 +
'}';
}
}

+ 31
- 4
sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRule.java View File

@@ -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 +
'}';
}
}

+ 2
- 66
sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRuleManager.java View File

@@ -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() {}
}


+ 114
- 0
sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRuleUtil.java View File

@@ -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() {}
}

+ 0
- 82
sentinel-extension/sentinel-parameter-flow-control/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRuleManagerTest.java View File

@@ -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));
}
}

+ 95
- 0
sentinel-extension/sentinel-parameter-flow-control/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRuleUtilTest.java View File

@@ -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));
}
}

Loading…
Cancel
Save