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>
|
<artifactId>junit</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</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>
|
</dependencies>
|
||||||
</project>
|
</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_CONTEXT_ROUTE_PREFIX = "sentinel_gateway_context$$route$$";
|
||||||
|
|
||||||
public static final String GATEWAY_NOT_MATCH_PARAM = "$$not_match";
|
public static final String GATEWAY_NOT_MATCH_PARAM = "$$not_match";
|
||||||
|
public static final String GATEWAY_DEFAULT_PARAM = "$D";
|
||||||
|
|
||||||
private SentinelGatewayConstants() {}
|
private SentinelGatewayConstants() {}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -53,10 +53,13 @@ public class GatewayParamParser<T> {
|
||||||
}
|
}
|
||||||
Set<GatewayFlowRule> gatewayRules = new HashSet<>();
|
Set<GatewayFlowRule> gatewayRules = new HashSet<>();
|
||||||
Set<Boolean> predSet = new HashSet<>();
|
Set<Boolean> predSet = new HashSet<>();
|
||||||
|
boolean hasNonParamRule = false;
|
||||||
for (GatewayFlowRule rule : GatewayRuleManager.getRulesForResource(resource)) {
|
for (GatewayFlowRule rule : GatewayRuleManager.getRulesForResource(resource)) {
|
||||||
if (rule.getParamItem() != null) {
|
if (rule.getParamItem() != null) {
|
||||||
gatewayRules.add(rule);
|
gatewayRules.add(rule);
|
||||||
predSet.add(rulePredicate.test(rule));
|
predSet.add(rulePredicate.test(rule));
|
||||||
|
} else {
|
||||||
|
hasNonParamRule = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (gatewayRules.isEmpty()) {
|
if (gatewayRules.isEmpty()) {
|
||||||
|
|
@ -65,13 +68,17 @@ public class GatewayParamParser<T> {
|
||||||
if (predSet.size() != 1 || predSet.contains(false)) {
|
if (predSet.size() != 1 || predSet.contains(false)) {
|
||||||
return new Object[0];
|
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) {
|
for (GatewayFlowRule rule : gatewayRules) {
|
||||||
GatewayParamFlowItem paramItem = rule.getParamItem();
|
GatewayParamFlowItem paramItem = rule.getParamItem();
|
||||||
int idx = paramItem.getIndex();
|
int idx = paramItem.getIndex();
|
||||||
String param = parseInternal(paramItem, request);
|
String param = parseInternal(paramItem, request);
|
||||||
arr[idx] = param;
|
arr[idx] = param;
|
||||||
}
|
}
|
||||||
|
if (hasNonParamRule) {
|
||||||
|
arr[size - 1] = SentinelGatewayConstants.GATEWAY_DEFAULT_PARAM;
|
||||||
|
}
|
||||||
return arr;
|
return arr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,17 @@ final class GatewayRuleConverter {
|
||||||
.setMaxQueueingTimeMs(rule.getMaxQueueingTimeoutMs());
|
.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
|
* Convert a gateway rule to parameter flow rule, then apply the generated
|
||||||
* parameter index to {@link GatewayParamFlowItem} of the rule.
|
* 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.PropertyListener;
|
||||||
import com.alibaba.csp.sentinel.property.SentinelProperty;
|
import com.alibaba.csp.sentinel.property.SentinelProperty;
|
||||||
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
|
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.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.AssertUtil;
|
||||||
import com.alibaba.csp.sentinel.util.StringUtil;
|
import com.alibaba.csp.sentinel.util.StringUtil;
|
||||||
|
|
||||||
|
|
@ -42,7 +41,12 @@ import com.alibaba.csp.sentinel.util.StringUtil;
|
||||||
*/
|
*/
|
||||||
public final class GatewayRuleManager {
|
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 final GatewayRulePropertyListener LISTENER = new GatewayRulePropertyListener();
|
||||||
private static SentinelProperty<Set<GatewayFlowRule>> currentProperty = new DynamicSentinelProperty<>();
|
private static SentinelProperty<Set<GatewayFlowRule>> currentProperty = new DynamicSentinelProperty<>();
|
||||||
|
|
@ -74,46 +78,69 @@ public final class GatewayRuleManager {
|
||||||
|
|
||||||
public static Set<GatewayFlowRule> getRules() {
|
public static Set<GatewayFlowRule> getRules() {
|
||||||
Set<GatewayFlowRule> rules = new HashSet<>();
|
Set<GatewayFlowRule> rules = new HashSet<>();
|
||||||
for (Set<GatewayFlowRule> ruleSet : RULE_MAP.values()) {
|
for (Set<GatewayFlowRule> ruleSet : GATEWAY_RULE_MAP.values()) {
|
||||||
rules.addAll(ruleSet);
|
rules.addAll(ruleSet);
|
||||||
}
|
}
|
||||||
return rules;
|
return rules;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Set<GatewayFlowRule> getRulesForResource(String resourceName) {
|
public static Set<GatewayFlowRule> getRulesForResource(String resourceName) {
|
||||||
AssertUtil.assertNotBlank(resourceName, "resourceName cannot be blank");
|
if (StringUtil.isBlank(resourceName)) {
|
||||||
Set<GatewayFlowRule> set = RULE_MAP.get(resourceName);
|
return new HashSet<>();
|
||||||
|
}
|
||||||
|
Set<GatewayFlowRule> set = GATEWAY_RULE_MAP.get(resourceName);
|
||||||
if (set == null) {
|
if (set == null) {
|
||||||
return new HashSet<>();
|
return new HashSet<>();
|
||||||
}
|
}
|
||||||
return new HashSet<>(set);
|
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>> {
|
private static final class GatewayRulePropertyListener implements PropertyListener<Set<GatewayFlowRule>> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void configUpdate(Set<GatewayFlowRule> conf) {
|
public void configUpdate(Set<GatewayFlowRule> conf) {
|
||||||
applyGatewayRuleInternal(conf);
|
applyGatewayRuleInternal(conf);
|
||||||
RecordLog.info("[GatewayRuleManager] Gateway flow rules received: " + RULE_MAP);
|
RecordLog.info("[GatewayRuleManager] Gateway flow rules received: " + GATEWAY_RULE_MAP);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void configLoad(Set<GatewayFlowRule> conf) {
|
public void configLoad(Set<GatewayFlowRule> conf) {
|
||||||
applyGatewayRuleInternal(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) {
|
private synchronized void applyGatewayRuleInternal(Set<GatewayFlowRule> conf) {
|
||||||
if (conf == null || conf.isEmpty()) {
|
if (conf == null || conf.isEmpty()) {
|
||||||
FlowRuleManager.loadRules(new ArrayList<FlowRule>());
|
applyToConvertedParamMap(new HashSet<ParamFlowRule>());
|
||||||
ParamFlowRuleManager.loadRules(new ArrayList<ParamFlowRule>());
|
GATEWAY_RULE_MAP.clear();
|
||||||
RULE_MAP.clear();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Map<String, Set<GatewayFlowRule>> gatewayRuleMap = new ConcurrentHashMap<>();
|
Map<String, Set<GatewayFlowRule>> gatewayRuleMap = new ConcurrentHashMap<>();
|
||||||
Map<String, Integer> idxMap = new HashMap<>();
|
Map<String, Integer> idxMap = new HashMap<>();
|
||||||
List<FlowRule> flowRules = new ArrayList<>();
|
|
||||||
Set<ParamFlowRule> paramFlowRules = new HashSet<>();
|
Set<ParamFlowRule> paramFlowRules = new HashSet<>();
|
||||||
|
Map<String, List<GatewayFlowRule>> noParamMap = new HashMap<>();
|
||||||
|
|
||||||
for (GatewayFlowRule rule : conf) {
|
for (GatewayFlowRule rule : conf) {
|
||||||
if (!isValidRule(rule)) {
|
if (!isValidRule(rule)) {
|
||||||
|
|
@ -122,14 +149,15 @@ public final class GatewayRuleManager {
|
||||||
}
|
}
|
||||||
String resourceName = rule.getResource();
|
String resourceName = rule.getResource();
|
||||||
if (rule.getParamItem() == null) {
|
if (rule.getParamItem() == null) {
|
||||||
// If param item is absent, it will be converted to normal flow rule.
|
// Cache the rules with no parameter config, then skip.
|
||||||
flowRules.add(GatewayRuleConverter.toFlowRule(rule));
|
List<GatewayFlowRule> noParamList = noParamMap.get(resourceName);
|
||||||
} else {
|
if (noParamList == null) {
|
||||||
// Prepare index map.
|
noParamList = new ArrayList<>();
|
||||||
if (!idxMap.containsKey(resourceName)) {
|
noParamMap.put(resourceName, noParamList);
|
||||||
idxMap.put(resourceName, 0);
|
|
||||||
}
|
}
|
||||||
int idx = idxMap.get(resourceName);
|
noParamList.add(rule);
|
||||||
|
} else {
|
||||||
|
int idx = getIdxInternal(idxMap, resourceName);
|
||||||
// Convert to parameter flow rule.
|
// Convert to parameter flow rule.
|
||||||
if (paramFlowRules.add(GatewayRuleConverter.applyToParamRule(rule, idx))) {
|
if (paramFlowRules.add(GatewayRuleConverter.applyToParamRule(rule, idx))) {
|
||||||
idxMap.put(rule.getResource(), idx + 1);
|
idxMap.put(rule.getResource(), idx + 1);
|
||||||
|
|
@ -143,11 +171,51 @@ public final class GatewayRuleManager {
|
||||||
}
|
}
|
||||||
ruleSet.add(rule);
|
ruleSet.add(rule);
|
||||||
}
|
}
|
||||||
FlowRuleManager.loadRules(flowRules);
|
// Handle non-param mode rules.
|
||||||
ParamFlowRuleManager.loadRules(new ArrayList<>(paramFlowRules));
|
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();
|
applyToConvertedParamMap(paramFlowRules);
|
||||||
RULE_MAP.putAll(gatewayRuleMap);
|
|
||||||
|
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;
|
package com.alibaba.csp.sentinel.adapter.gateway.common.rule;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
|
import com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants;
|
||||||
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
|
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.ParamFlowRule;
|
||||||
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager;
|
|
||||||
|
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
|
|
@ -62,8 +62,8 @@ public class GatewayRuleManagerTest {
|
||||||
rules.add(rule3);
|
rules.add(rule3);
|
||||||
GatewayRuleManager.loadRules(rules);
|
GatewayRuleManager.loadRules(rules);
|
||||||
|
|
||||||
assertTrue(FlowRuleManager.hasConfig(ahasRoute));
|
List<ParamFlowRule> convertedRules = GatewayRuleManager.getConvertedParamRules(ahasRoute);
|
||||||
assertTrue(ParamFlowRuleManager.hasRules(ahasRoute));
|
assertNotNull(convertedRules);
|
||||||
assertEquals(0, (int)rule2.getParamItem().getIndex());
|
assertEquals(0, (int)rule2.getParamItem().getIndex());
|
||||||
assertEquals(0, (int)rule3.getParamItem().getIndex());
|
assertEquals(0, (int)rule3.getParamItem().getIndex());
|
||||||
assertTrue(GatewayRuleManager.getRulesForResource(ahasRoute).contains(rule1));
|
assertTrue(GatewayRuleManager.getRulesForResource(ahasRoute).contains(rule1));
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue