Refactor API gateway common module to separate converted rules from other rule managers
- Separate converted parameter rules from ParamFlowManager. Now the converted rules will be kept in GatewayRuleManager directly. - Add a GatewayFlowSlot to do separate flow checking for generated rules. - Refactor rule converting mechanism: now gateway rules in normal mode (without parameter) will also be converted to a parameter flow rule. The index will be the last (the last position). In GatewayParamParser we put a constant value to the last position. Signed-off-by: Eric Zhao <sczyh16@gmail.com>
This commit is contained in:
parent
77dec5f845
commit
ae0b8a5c74
|
|
@ -37,5 +37,15 @@
|
|||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
@ -40,6 +40,7 @@ public final class SentinelGatewayConstants {
|
|||
public static final String GATEWAY_CONTEXT_ROUTE_PREFIX = "sentinel_gateway_context$$route$$";
|
||||
|
||||
public static final String GATEWAY_NOT_MATCH_PARAM = "$$not_match";
|
||||
public static final String GATEWAY_DEFAULT_PARAM = "$D";
|
||||
|
||||
private SentinelGatewayConstants() {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,10 +53,13 @@ public class GatewayParamParser<T> {
|
|||
}
|
||||
Set<GatewayFlowRule> gatewayRules = new HashSet<>();
|
||||
Set<Boolean> predSet = new HashSet<>();
|
||||
boolean hasNonParamRule = false;
|
||||
for (GatewayFlowRule rule : GatewayRuleManager.getRulesForResource(resource)) {
|
||||
if (rule.getParamItem() != null) {
|
||||
gatewayRules.add(rule);
|
||||
predSet.add(rulePredicate.test(rule));
|
||||
} else {
|
||||
hasNonParamRule = true;
|
||||
}
|
||||
}
|
||||
if (gatewayRules.isEmpty()) {
|
||||
|
|
@ -65,13 +68,17 @@ public class GatewayParamParser<T> {
|
|||
if (predSet.size() != 1 || predSet.contains(false)) {
|
||||
return new Object[0];
|
||||
}
|
||||
Object[] arr = new Object[gatewayRules.size()];
|
||||
int size = hasNonParamRule ? gatewayRules.size() + 1 : gatewayRules.size();
|
||||
Object[] arr = new Object[size];
|
||||
for (GatewayFlowRule rule : gatewayRules) {
|
||||
GatewayParamFlowItem paramItem = rule.getParamItem();
|
||||
int idx = paramItem.getIndex();
|
||||
String param = parseInternal(paramItem, request);
|
||||
arr[idx] = param;
|
||||
}
|
||||
if (hasNonParamRule) {
|
||||
arr[size - 1] = SentinelGatewayConstants.GATEWAY_DEFAULT_PARAM;
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -32,6 +32,17 @@ final class GatewayRuleConverter {
|
|||
.setMaxQueueingTimeMs(rule.getMaxQueueingTimeoutMs());
|
||||
}
|
||||
|
||||
static ParamFlowRule applyNonParamToParamRule(/*@Valid*/ GatewayFlowRule gatewayRule, int idx) {
|
||||
return new ParamFlowRule(gatewayRule.getResource())
|
||||
.setCount(gatewayRule.getCount())
|
||||
.setGrade(gatewayRule.getGrade())
|
||||
.setDurationInSec(gatewayRule.getIntervalSec())
|
||||
.setBurstCount(gatewayRule.getBurst())
|
||||
.setControlBehavior(gatewayRule.getControlBehavior())
|
||||
.setMaxQueueingTimeMs(gatewayRule.getMaxQueueingTimeoutMs())
|
||||
.setParamIdx(idx);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a gateway rule to parameter flow rule, then apply the generated
|
||||
* parameter index to {@link GatewayParamFlowItem} of the rule.
|
||||
|
|
|
|||
|
|
@ -29,10 +29,9 @@ import com.alibaba.csp.sentinel.property.DynamicSentinelProperty;
|
|||
import com.alibaba.csp.sentinel.property.PropertyListener;
|
||||
import com.alibaba.csp.sentinel.property.SentinelProperty;
|
||||
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleUtil;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParameterMetricStorage;
|
||||
import com.alibaba.csp.sentinel.util.AssertUtil;
|
||||
import com.alibaba.csp.sentinel.util.StringUtil;
|
||||
|
||||
|
|
@ -42,7 +41,12 @@ import com.alibaba.csp.sentinel.util.StringUtil;
|
|||
*/
|
||||
public final class GatewayRuleManager {
|
||||
|
||||
private static final Map<String, Set<GatewayFlowRule>> RULE_MAP = new ConcurrentHashMap<>();
|
||||
/**
|
||||
* Gateway flow rule map: (resource, [rules...])
|
||||
*/
|
||||
private static final Map<String, Set<GatewayFlowRule>> GATEWAY_RULE_MAP = new ConcurrentHashMap<>();
|
||||
|
||||
private static final Map<String, List<ParamFlowRule>> CONVERTED_PARAM_RULE_MAP = new ConcurrentHashMap<>();
|
||||
|
||||
private static final GatewayRulePropertyListener LISTENER = new GatewayRulePropertyListener();
|
||||
private static SentinelProperty<Set<GatewayFlowRule>> currentProperty = new DynamicSentinelProperty<>();
|
||||
|
|
@ -74,46 +78,69 @@ public final class GatewayRuleManager {
|
|||
|
||||
public static Set<GatewayFlowRule> getRules() {
|
||||
Set<GatewayFlowRule> rules = new HashSet<>();
|
||||
for (Set<GatewayFlowRule> ruleSet : RULE_MAP.values()) {
|
||||
for (Set<GatewayFlowRule> ruleSet : GATEWAY_RULE_MAP.values()) {
|
||||
rules.addAll(ruleSet);
|
||||
}
|
||||
return rules;
|
||||
}
|
||||
|
||||
public static Set<GatewayFlowRule> getRulesForResource(String resourceName) {
|
||||
AssertUtil.assertNotBlank(resourceName, "resourceName cannot be blank");
|
||||
Set<GatewayFlowRule> set = RULE_MAP.get(resourceName);
|
||||
if (StringUtil.isBlank(resourceName)) {
|
||||
return new HashSet<>();
|
||||
}
|
||||
Set<GatewayFlowRule> set = GATEWAY_RULE_MAP.get(resourceName);
|
||||
if (set == null) {
|
||||
return new HashSet<>();
|
||||
}
|
||||
return new HashSet<>(set);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Get all converted parameter rules.</p>
|
||||
* <p>Note: caller SHOULD NOT modify the list and rules.</p>
|
||||
*
|
||||
* @param resourceName valid resource name
|
||||
* @return converted parameter rules
|
||||
*/
|
||||
public static List<ParamFlowRule> getConvertedParamRules(String resourceName) {
|
||||
if (StringUtil.isBlank(resourceName)) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return CONVERTED_PARAM_RULE_MAP.get(resourceName);
|
||||
}
|
||||
|
||||
private static final class GatewayRulePropertyListener implements PropertyListener<Set<GatewayFlowRule>> {
|
||||
|
||||
@Override
|
||||
public void configUpdate(Set<GatewayFlowRule> conf) {
|
||||
applyGatewayRuleInternal(conf);
|
||||
RecordLog.info("[GatewayRuleManager] Gateway flow rules received: " + RULE_MAP);
|
||||
RecordLog.info("[GatewayRuleManager] Gateway flow rules received: " + GATEWAY_RULE_MAP);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configLoad(Set<GatewayFlowRule> conf) {
|
||||
applyGatewayRuleInternal(conf);
|
||||
RecordLog.info("[GatewayRuleManager] Gateway flow rules loaded: " + RULE_MAP);
|
||||
RecordLog.info("[GatewayRuleManager] Gateway flow rules loaded: " + GATEWAY_RULE_MAP);
|
||||
}
|
||||
|
||||
private int getIdxInternal(Map<String, Integer> idxMap, String resourceName) {
|
||||
// Prepare index map.
|
||||
if (!idxMap.containsKey(resourceName)) {
|
||||
idxMap.put(resourceName, 0);
|
||||
}
|
||||
return idxMap.get(resourceName);
|
||||
}
|
||||
|
||||
private synchronized void applyGatewayRuleInternal(Set<GatewayFlowRule> conf) {
|
||||
if (conf == null || conf.isEmpty()) {
|
||||
FlowRuleManager.loadRules(new ArrayList<FlowRule>());
|
||||
ParamFlowRuleManager.loadRules(new ArrayList<ParamFlowRule>());
|
||||
RULE_MAP.clear();
|
||||
applyToConvertedParamMap(new HashSet<ParamFlowRule>());
|
||||
GATEWAY_RULE_MAP.clear();
|
||||
return;
|
||||
}
|
||||
Map<String, Set<GatewayFlowRule>> gatewayRuleMap = new ConcurrentHashMap<>();
|
||||
Map<String, Integer> idxMap = new HashMap<>();
|
||||
List<FlowRule> flowRules = new ArrayList<>();
|
||||
Set<ParamFlowRule> paramFlowRules = new HashSet<>();
|
||||
Map<String, List<GatewayFlowRule>> noParamMap = new HashMap<>();
|
||||
|
||||
for (GatewayFlowRule rule : conf) {
|
||||
if (!isValidRule(rule)) {
|
||||
|
|
@ -122,14 +149,15 @@ public final class GatewayRuleManager {
|
|||
}
|
||||
String resourceName = rule.getResource();
|
||||
if (rule.getParamItem() == null) {
|
||||
// If param item is absent, it will be converted to normal flow rule.
|
||||
flowRules.add(GatewayRuleConverter.toFlowRule(rule));
|
||||
} else {
|
||||
// Prepare index map.
|
||||
if (!idxMap.containsKey(resourceName)) {
|
||||
idxMap.put(resourceName, 0);
|
||||
// Cache the rules with no parameter config, then skip.
|
||||
List<GatewayFlowRule> noParamList = noParamMap.get(resourceName);
|
||||
if (noParamList == null) {
|
||||
noParamList = new ArrayList<>();
|
||||
noParamMap.put(resourceName, noParamList);
|
||||
}
|
||||
int idx = idxMap.get(resourceName);
|
||||
noParamList.add(rule);
|
||||
} else {
|
||||
int idx = getIdxInternal(idxMap, resourceName);
|
||||
// Convert to parameter flow rule.
|
||||
if (paramFlowRules.add(GatewayRuleConverter.applyToParamRule(rule, idx))) {
|
||||
idxMap.put(rule.getResource(), idx + 1);
|
||||
|
|
@ -143,11 +171,51 @@ public final class GatewayRuleManager {
|
|||
}
|
||||
ruleSet.add(rule);
|
||||
}
|
||||
FlowRuleManager.loadRules(flowRules);
|
||||
ParamFlowRuleManager.loadRules(new ArrayList<>(paramFlowRules));
|
||||
// Handle non-param mode rules.
|
||||
for (Map.Entry<String, List<GatewayFlowRule>> e : noParamMap.entrySet()) {
|
||||
List<GatewayFlowRule> rules = e.getValue();
|
||||
if (rules == null || rules.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
for (GatewayFlowRule rule : rules) {
|
||||
int idx = getIdxInternal(idxMap, e.getKey());
|
||||
// Always use the same index (the last position).
|
||||
paramFlowRules.add(GatewayRuleConverter.applyNonParamToParamRule(rule, idx));
|
||||
}
|
||||
}
|
||||
|
||||
RULE_MAP.clear();
|
||||
RULE_MAP.putAll(gatewayRuleMap);
|
||||
applyToConvertedParamMap(paramFlowRules);
|
||||
|
||||
GATEWAY_RULE_MAP.clear();
|
||||
GATEWAY_RULE_MAP.putAll(gatewayRuleMap);
|
||||
}
|
||||
|
||||
private void applyToConvertedParamMap(Set<ParamFlowRule> paramFlowRules) {
|
||||
Map<String, List<ParamFlowRule>> newRuleMap = ParamFlowRuleUtil.buildParamRuleMap(
|
||||
new ArrayList<>(paramFlowRules));
|
||||
if (newRuleMap == null || newRuleMap.isEmpty()) {
|
||||
// No parameter flow rules, so clear all the metrics.
|
||||
for (String resource : CONVERTED_PARAM_RULE_MAP.keySet()) {
|
||||
ParameterMetricStorage.clearParamMetricForResource(resource);
|
||||
}
|
||||
RecordLog.info("[GatewayRuleManager] No gateway rules, clearing parameter metrics of previous rules");
|
||||
CONVERTED_PARAM_RULE_MAP.clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear unused parameter metrics.
|
||||
Set<String> previousResources = CONVERTED_PARAM_RULE_MAP.keySet();
|
||||
for (String resource : previousResources) {
|
||||
if (!newRuleMap.containsKey(resource)) {
|
||||
ParameterMetricStorage.clearParamMetricForResource(resource);
|
||||
}
|
||||
}
|
||||
|
||||
// Apply to converted rule map.
|
||||
CONVERTED_PARAM_RULE_MAP.clear();
|
||||
CONVERTED_PARAM_RULE_MAP.putAll(newRuleMap);
|
||||
|
||||
RecordLog.info("[GatewayRuleManager] Converted internal param rules: " + CONVERTED_PARAM_RULE_MAP);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright 1999-2019 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
|
||||
*
|
||||
* https://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.adapter.gateway.common.slot;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
|
||||
import com.alibaba.csp.sentinel.context.Context;
|
||||
import com.alibaba.csp.sentinel.node.DefaultNode;
|
||||
import com.alibaba.csp.sentinel.slotchain.AbstractLinkedProcessorSlot;
|
||||
import com.alibaba.csp.sentinel.slotchain.ResourceWrapper;
|
||||
import com.alibaba.csp.sentinel.slots.block.BlockException;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowChecker;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParameterMetricStorage;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
* @since 1.6.1
|
||||
*/
|
||||
public class GatewayFlowSlot extends AbstractLinkedProcessorSlot<DefaultNode> {
|
||||
|
||||
@Override
|
||||
public void entry(Context context, ResourceWrapper resource, DefaultNode node, int count,
|
||||
boolean prioritized, Object... args) throws Throwable {
|
||||
checkGatewayParamFlow(resource, count, args);
|
||||
|
||||
fireEntry(context, resource, node, count, prioritized, args);
|
||||
}
|
||||
|
||||
private void checkGatewayParamFlow(ResourceWrapper resourceWrapper, int count, Object... args)
|
||||
throws BlockException {
|
||||
if (args == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<ParamFlowRule> rules = GatewayRuleManager.getConvertedParamRules(resourceWrapper.getName());
|
||||
if (rules == null || rules.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (ParamFlowRule rule : rules) {
|
||||
// Initialize the parameter metrics.
|
||||
ParameterMetricStorage.initParamMetricsFor(resourceWrapper, rule);
|
||||
|
||||
if (!ParamFlowChecker.passCheck(resourceWrapper, rule, count, args)) {
|
||||
String triggeredParam = "";
|
||||
if (args.length > rule.getParamIdx()) {
|
||||
Object value = args[rule.getParamIdx()];
|
||||
triggeredParam = String.valueOf(value);
|
||||
}
|
||||
throw new ParamFlowException(resourceWrapper.getName(), triggeredParam, rule);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {
|
||||
fireExit(context, resourceWrapper, count, args);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Copyright 1999-2019 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
|
||||
*
|
||||
* https://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.adapter.gateway.common.slot;
|
||||
|
||||
import com.alibaba.csp.sentinel.slotchain.DefaultProcessorSlotChain;
|
||||
import com.alibaba.csp.sentinel.slotchain.ProcessorSlotChain;
|
||||
import com.alibaba.csp.sentinel.slotchain.SlotChainBuilder;
|
||||
import com.alibaba.csp.sentinel.slots.block.authority.AuthoritySlot;
|
||||
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeSlot;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowSlot;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowSlot;
|
||||
import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot;
|
||||
import com.alibaba.csp.sentinel.slots.logger.LogSlot;
|
||||
import com.alibaba.csp.sentinel.slots.nodeselector.NodeSelectorSlot;
|
||||
import com.alibaba.csp.sentinel.slots.statistic.StatisticSlot;
|
||||
import com.alibaba.csp.sentinel.slots.system.SystemSlot;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
* @since 1.6.1
|
||||
*/
|
||||
public class GatewaySlotChainBuilder implements SlotChainBuilder {
|
||||
|
||||
@Override
|
||||
public ProcessorSlotChain build() {
|
||||
ProcessorSlotChain chain = new DefaultProcessorSlotChain();
|
||||
// Prepare slot
|
||||
chain.addLast(new NodeSelectorSlot());
|
||||
chain.addLast(new ClusterBuilderSlot());
|
||||
// Stat slot
|
||||
chain.addLast(new LogSlot());
|
||||
chain.addLast(new StatisticSlot());
|
||||
// Rule checking slot
|
||||
chain.addLast(new AuthoritySlot());
|
||||
chain.addLast(new SystemSlot());
|
||||
chain.addLast(new GatewayFlowSlot());
|
||||
|
||||
chain.addLast(new ParamFlowSlot());
|
||||
chain.addLast(new FlowSlot());
|
||||
chain.addLast(new DegradeSlot());
|
||||
|
||||
return chain;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
com.alibaba.csp.sentinel.adapter.gateway.common.slot.GatewaySlotChainBuilder
|
||||
|
|
@ -0,0 +1,205 @@
|
|||
/*
|
||||
* Copyright 1999-2019 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.adapter.gateway.common.param;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
|
||||
import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition;
|
||||
import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager;
|
||||
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
|
||||
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayParamFlowItem;
|
||||
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
|
||||
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
|
||||
import com.alibaba.csp.sentinel.util.function.Predicate;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* @author Eric Zhao
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public class GatewayParamParserTest {
|
||||
|
||||
private final Predicate<GatewayFlowRule> routeIdPredicate = new Predicate<GatewayFlowRule>() {
|
||||
@Override
|
||||
public boolean test(GatewayFlowRule e) {
|
||||
return e.getResourceMode() == SentinelGatewayConstants.RESOURCE_MODE_ROUTE_ID;
|
||||
}
|
||||
};
|
||||
private final Predicate<GatewayFlowRule> apiNamePredicate = new Predicate<GatewayFlowRule>() {
|
||||
@Override
|
||||
public boolean test(GatewayFlowRule e) {
|
||||
return e.getResourceMode() == SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME;
|
||||
}
|
||||
};
|
||||
|
||||
@Test
|
||||
public void testParseParametersNoParamItem() {
|
||||
RequestItemParser<Object> itemParser = mock(RequestItemParser.class);
|
||||
GatewayParamParser<Object> parser = new GatewayParamParser<>(itemParser);
|
||||
// Create a fake request.
|
||||
Object request = new Object();
|
||||
// Prepare gateway rules.
|
||||
Set<GatewayFlowRule> rules = new HashSet<>();
|
||||
String routeId1 = "my_test_route_A";
|
||||
rules.add(new GatewayFlowRule(routeId1)
|
||||
.setCount(5)
|
||||
.setIntervalSec(1)
|
||||
);
|
||||
GatewayRuleManager.loadRules(rules);
|
||||
|
||||
Object[] params = parser.parseParameterFor(routeId1, request, routeIdPredicate);
|
||||
assertThat(params.length).isZero();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseParametersWithItems() {
|
||||
RequestItemParser<Object> itemParser = mock(RequestItemParser.class);
|
||||
GatewayParamParser<Object> paramParser = new GatewayParamParser<>(itemParser);
|
||||
// Create a fake request.
|
||||
Object request = new Object();
|
||||
|
||||
// Prepare gateway rules.
|
||||
Set<GatewayFlowRule> rules = new HashSet<>();
|
||||
final String routeId1 = "my_test_route_A";
|
||||
final String api1 = "my_test_route_B";
|
||||
final String headerName = "X-Sentinel-Flag";
|
||||
final String paramName = "p";
|
||||
GatewayFlowRule routeRuleNoParam = new GatewayFlowRule(routeId1)
|
||||
.setCount(10)
|
||||
.setIntervalSec(10);
|
||||
GatewayFlowRule routeRule1 = new GatewayFlowRule(routeId1)
|
||||
.setCount(2)
|
||||
.setIntervalSec(2)
|
||||
.setBurst(2)
|
||||
.setParamItem(new GatewayParamFlowItem()
|
||||
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_CLIENT_IP)
|
||||
);
|
||||
GatewayFlowRule routeRule2 = new GatewayFlowRule(routeId1)
|
||||
.setCount(10)
|
||||
.setIntervalSec(1)
|
||||
.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)
|
||||
.setMaxQueueingTimeoutMs(600)
|
||||
.setParamItem(new GatewayParamFlowItem()
|
||||
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_HEADER)
|
||||
.setFieldName(headerName)
|
||||
);
|
||||
GatewayFlowRule routeRule3 = new GatewayFlowRule(routeId1)
|
||||
.setCount(20)
|
||||
.setIntervalSec(1)
|
||||
.setBurst(5)
|
||||
.setParamItem(new GatewayParamFlowItem()
|
||||
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM)
|
||||
.setFieldName(paramName)
|
||||
);
|
||||
GatewayFlowRule routeRule4 = new GatewayFlowRule(routeId1)
|
||||
.setCount(120)
|
||||
.setIntervalSec(10)
|
||||
.setBurst(30)
|
||||
.setParamItem(new GatewayParamFlowItem()
|
||||
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_HOST)
|
||||
);
|
||||
GatewayFlowRule apiRule1 = new GatewayFlowRule(api1)
|
||||
.setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME)
|
||||
.setCount(5)
|
||||
.setIntervalSec(1)
|
||||
.setParamItem(new GatewayParamFlowItem()
|
||||
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM)
|
||||
.setFieldName(paramName)
|
||||
);
|
||||
rules.add(routeRule1);
|
||||
rules.add(routeRule2);
|
||||
rules.add(routeRule3);
|
||||
rules.add(routeRule4);
|
||||
rules.add(routeRuleNoParam);
|
||||
rules.add(apiRule1);
|
||||
GatewayRuleManager.loadRules(rules);
|
||||
|
||||
final String expectedHost = "hello.test.sentinel";
|
||||
final String expectedAddress = "66.77.88.99";
|
||||
final String expectedHeaderValue1 = "Sentinel";
|
||||
final String expectedUrlParamValue1 = "17";
|
||||
mockClientHostAddress(itemParser, expectedAddress);
|
||||
Map<String, String> expectedHeaders = new HashMap<String, String>() {{
|
||||
put(headerName, expectedHeaderValue1); put("Host", expectedHost);
|
||||
}};
|
||||
mockHeaders(itemParser, expectedHeaders);
|
||||
mockSingleUrlParam(itemParser, paramName, expectedUrlParamValue1);
|
||||
Object[] params = paramParser.parseParameterFor(routeId1, request, routeIdPredicate);
|
||||
// Param length should be 5 (4 with parameters, 1 normal flow with generated constant)
|
||||
assertThat(params.length).isEqualTo(5);
|
||||
assertThat(params[routeRule1.getParamItem().getIndex()]).isEqualTo(expectedAddress);
|
||||
assertThat(params[routeRule2.getParamItem().getIndex()]).isEqualTo(expectedHeaderValue1);
|
||||
assertThat(params[routeRule3.getParamItem().getIndex()]).isEqualTo(expectedUrlParamValue1);
|
||||
assertThat(params[routeRule4.getParamItem().getIndex()]).isEqualTo(expectedHost);
|
||||
assertThat(params[params.length - 1]).isEqualTo(SentinelGatewayConstants.GATEWAY_DEFAULT_PARAM);
|
||||
|
||||
assertThat(paramParser.parseParameterFor(api1, request, routeIdPredicate).length).isZero();
|
||||
|
||||
String expectedUrlParamValue2 = "fs";
|
||||
mockSingleUrlParam(itemParser, paramName, expectedUrlParamValue2);
|
||||
params = paramParser.parseParameterFor(api1, request, apiNamePredicate);
|
||||
assertThat(params.length).isEqualTo(1);
|
||||
assertThat(params[apiRule1.getParamItem().getIndex()]).isEqualTo(expectedUrlParamValue2);
|
||||
}
|
||||
|
||||
private void mockClientHostAddress(/*@Mock*/ RequestItemParser parser, String address) {
|
||||
when(parser.getRemoteAddress(any())).thenReturn(address);
|
||||
}
|
||||
|
||||
private void mockHeaders(/*@Mock*/ RequestItemParser parser, Map<String, String> headerMap) {
|
||||
for (Map.Entry<String, String> e : headerMap.entrySet()) {
|
||||
when(parser.getHeader(any(), eq(e.getKey()))).thenReturn(e.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
private void mockUrlParams(/*@Mock*/ RequestItemParser parser, Map<String, String> paramMap) {
|
||||
for (Map.Entry<String, String> e : paramMap.entrySet()) {
|
||||
when(parser.getUrlParam(any(), eq(e.getKey()))).thenReturn(e.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
private void mockSingleUrlParam(/*@Mock*/ RequestItemParser parser, String key, String value) {
|
||||
when(parser.getUrlParam(any(), eq(key))).thenReturn(value);
|
||||
}
|
||||
|
||||
private void mockSingleHeader(/*@Mock*/ RequestItemParser parser, String key, String value) {
|
||||
when(parser.getHeader(any(), eq(key))).thenReturn(value);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
GatewayApiDefinitionManager.loadApiDefinitions(new HashSet<ApiDefinition>());
|
||||
GatewayRuleManager.loadRules(new HashSet<GatewayFlowRule>());
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
GatewayApiDefinitionManager.loadApiDefinitions(new HashSet<ApiDefinition>());
|
||||
GatewayRuleManager.loadRules(new HashSet<GatewayFlowRule>());
|
||||
}
|
||||
}
|
||||
|
|
@ -16,12 +16,12 @@
|
|||
package com.alibaba.csp.sentinel.adapter.gateway.common.rule;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
|
||||
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager;
|
||||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
|
|
@ -62,8 +62,8 @@ public class GatewayRuleManagerTest {
|
|||
rules.add(rule3);
|
||||
GatewayRuleManager.loadRules(rules);
|
||||
|
||||
assertTrue(FlowRuleManager.hasConfig(ahasRoute));
|
||||
assertTrue(ParamFlowRuleManager.hasRules(ahasRoute));
|
||||
List<ParamFlowRule> convertedRules = GatewayRuleManager.getConvertedParamRules(ahasRoute);
|
||||
assertNotNull(convertedRules);
|
||||
assertEquals(0, (int)rule2.getParamItem().getIndex());
|
||||
assertEquals(0, (int)rule3.getParamItem().getIndex());
|
||||
assertTrue(GatewayRuleManager.getRulesForResource(ahasRoute).contains(rule1));
|
||||
|
|
|
|||
Loading…
Reference in New Issue