diff --git a/README.md b/README.md index 889aea7788..dc3507f64a 100755 --- a/README.md +++ b/README.md @@ -55,7 +55,7 @@ If your application is build in Maven, just add the following dependency in `pom com.alibaba.csp sentinel-core - 1.5.1 + 1.6.1 ``` diff --git a/doc/awesome-sentinel.md b/doc/awesome-sentinel.md index 1b3736757b..db2715777d 100644 --- a/doc/awesome-sentinel.md +++ b/doc/awesome-sentinel.md @@ -25,6 +25,7 @@ You can also add to [sentinel-group/sentinel-awesome](https://github.com/sentine ## Extensions / Integrations - [sentinel-support](https://github.com/cdfive/sentinel-support): A support project for convenient Sentinel integration including properties file configuration, ActiveMQ integration and a JdbcDataSource implementation by [cdfive](https://github.com/cdfive) +- [sentinel-multiDataSource-adapter](https://github.com/finefuture/sentinel-dashboard-X): Sentinel-dashborad multi-data source adapter has integrated Apollo configuration center and Nacos configuration center for bidirectional modification persistence, implementation by [finefuture](https://github.com/finefuture) ## Blogs diff --git a/pom.xml b/pom.xml index 3cb56b4c7a..944bfc7a16 100755 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ com.alibaba.csp sentinel-parent - 1.6.0-SNAPSHOT + 1.7.0-SNAPSHOT pom ${project.artifactId} diff --git a/sentinel-adapter/pom.xml b/sentinel-adapter/pom.xml index 44531d929b..038652a4cf 100755 --- a/sentinel-adapter/pom.xml +++ b/sentinel-adapter/pom.xml @@ -7,7 +7,7 @@ com.alibaba.csp sentinel-parent - 1.6.0-SNAPSHOT + 1.7.0-SNAPSHOT sentinel-adapter pom diff --git a/sentinel-adapter/sentinel-apache-dubbo-adapter/pom.xml b/sentinel-adapter/sentinel-apache-dubbo-adapter/pom.xml index aae2f9fc51..d6a4bab392 100644 --- a/sentinel-adapter/sentinel-apache-dubbo-adapter/pom.xml +++ b/sentinel-adapter/sentinel-apache-dubbo-adapter/pom.xml @@ -5,7 +5,7 @@ sentinel-adapter com.alibaba.csp - 1.6.0-SNAPSHOT + 1.7.0-SNAPSHOT 4.0.0 diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/pom.xml b/sentinel-adapter/sentinel-api-gateway-adapter-common/pom.xml index acda0b1dba..5cc4a43f06 100644 --- a/sentinel-adapter/sentinel-api-gateway-adapter-common/pom.xml +++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/pom.xml @@ -5,7 +5,7 @@ sentinel-adapter com.alibaba.csp - 1.6.0-SNAPSHOT + 1.7.0-SNAPSHOT 4.0.0 @@ -26,11 +26,26 @@ com.alibaba.csp sentinel-parameter-flow-control + + com.alibaba.csp + sentinel-transport-common + provided + junit junit test + + org.assertj + assertj-core + test + + + org.mockito + mockito-core + test + \ No newline at end of file diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/SentinelGatewayConstants.java b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/SentinelGatewayConstants.java index 4a2c6ce320..2c56e6b7ce 100644 --- a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/SentinelGatewayConstants.java +++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/SentinelGatewayConstants.java @@ -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() {} } diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/api/ApiDefinition.java b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/api/ApiDefinition.java index 6f3a9782ea..4540eebe86 100644 --- a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/api/ApiDefinition.java +++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/api/ApiDefinition.java @@ -19,6 +19,8 @@ import java.util.Set; /** + * A group of HTTP API patterns. + * * @author Eric Zhao * @since 1.6.0 */ diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/command/GetGatewayApiDefinitionGroupCommandHandler.java b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/command/GetGatewayApiDefinitionGroupCommandHandler.java new file mode 100644 index 0000000000..b6403f086e --- /dev/null +++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/command/GetGatewayApiDefinitionGroupCommandHandler.java @@ -0,0 +1,36 @@ +/* + * 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.command; + +import com.alibaba.csp.sentinel.adapter.gateway.common.api.GatewayApiDefinitionManager; +import com.alibaba.csp.sentinel.command.CommandHandler; +import com.alibaba.csp.sentinel.command.CommandRequest; +import com.alibaba.csp.sentinel.command.CommandResponse; +import com.alibaba.csp.sentinel.command.annotation.CommandMapping; +import com.alibaba.fastjson.JSON; + +/** + * @author Eric Zhao + * @since 1.6.0 + */ +@CommandMapping(name = "gateway/getApiDefinitions", desc = "Fetch all customized gateway API groups") +public class GetGatewayApiDefinitionGroupCommandHandler implements CommandHandler { + + @Override + public CommandResponse handle(CommandRequest request) { + return CommandResponse.ofSuccess(JSON.toJSONString(GatewayApiDefinitionManager.getApiDefinitions())); + } +} \ No newline at end of file diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/command/GetGatewayRuleCommandHandler.java b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/command/GetGatewayRuleCommandHandler.java new file mode 100644 index 0000000000..e7b8e9c578 --- /dev/null +++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/command/GetGatewayRuleCommandHandler.java @@ -0,0 +1,36 @@ +/* + * 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.command; + +import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager; +import com.alibaba.csp.sentinel.command.CommandHandler; +import com.alibaba.csp.sentinel.command.CommandRequest; +import com.alibaba.csp.sentinel.command.CommandResponse; +import com.alibaba.csp.sentinel.command.annotation.CommandMapping; +import com.alibaba.fastjson.JSON; + +/** + * @author Eric Zhao + * @since 1.6.0 + */ +@CommandMapping(name = "gateway/getRules", desc = "Fetch all gateway rules") +public class GetGatewayRuleCommandHandler implements CommandHandler { + + @Override + public CommandResponse handle(CommandRequest request) { + return CommandResponse.ofSuccess(JSON.toJSONString(GatewayRuleManager.getRules())); + } +} diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/command/UpdateGatewayApiDefinitionGroupCommandHandler.java b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/command/UpdateGatewayApiDefinitionGroupCommandHandler.java new file mode 100644 index 0000000000..963561f82a --- /dev/null +++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/command/UpdateGatewayApiDefinitionGroupCommandHandler.java @@ -0,0 +1,61 @@ +/* + * 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.command; + +import java.net.URLDecoder; +import java.util.HashSet; +import java.util.List; + +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.command.CommandHandler; +import com.alibaba.csp.sentinel.command.CommandRequest; +import com.alibaba.csp.sentinel.command.CommandResponse; +import com.alibaba.csp.sentinel.command.annotation.CommandMapping; +import com.alibaba.csp.sentinel.log.RecordLog; +import com.alibaba.csp.sentinel.util.StringUtil; +import com.alibaba.fastjson.JSONArray; + +/** + * @author Eric Zhao + * @since 1.6.0 + */ +@CommandMapping(name = "gateway/updateApiDefinitions", desc = "") +public class UpdateGatewayApiDefinitionGroupCommandHandler implements CommandHandler { + + @Override + public CommandResponse handle(CommandRequest request) { + String data = request.getParam("data"); + if (StringUtil.isBlank(data)) { + return CommandResponse.ofFailure(new IllegalArgumentException("Bad data")); + } + try { + data = URLDecoder.decode(data, "utf-8"); + } catch (Exception e) { + RecordLog.info("Decode gateway API definition data error", e); + return CommandResponse.ofFailure(e, "decode gateway API definition data error"); + } + + RecordLog.info("[API Server] Receiving data change (type: gateway API definition): {0}", data); + + String result = SUCCESS_MSG; + List apiDefinitions = JSONArray.parseArray(data, ApiDefinition.class); + GatewayApiDefinitionManager.loadApiDefinitions(new HashSet<>(apiDefinitions)); + return CommandResponse.ofSuccess(result); + } + + private static final String SUCCESS_MSG = "success"; +} diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/command/UpdateGatewayRuleCommandHandler.java b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/command/UpdateGatewayRuleCommandHandler.java new file mode 100644 index 0000000000..46e14209b4 --- /dev/null +++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/command/UpdateGatewayRuleCommandHandler.java @@ -0,0 +1,61 @@ +/* + * 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.command; + +import java.net.URLDecoder; +import java.util.HashSet; +import java.util.List; + +import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule; +import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager; +import com.alibaba.csp.sentinel.command.CommandHandler; +import com.alibaba.csp.sentinel.command.CommandRequest; +import com.alibaba.csp.sentinel.command.CommandResponse; +import com.alibaba.csp.sentinel.command.annotation.CommandMapping; +import com.alibaba.csp.sentinel.log.RecordLog; +import com.alibaba.csp.sentinel.util.StringUtil; +import com.alibaba.fastjson.JSONArray; + +/** + * @author Eric Zhao + * @since 1.6.0 + */ +@CommandMapping(name = "gateway/updateRules", desc = "Update gateway rules") +public class UpdateGatewayRuleCommandHandler implements CommandHandler { + + @Override + public CommandResponse handle(CommandRequest request) { + String data = request.getParam("data"); + if (StringUtil.isBlank(data)) { + return CommandResponse.ofFailure(new IllegalArgumentException("Bad data")); + } + try { + data = URLDecoder.decode(data, "utf-8"); + } catch (Exception e) { + RecordLog.info("Decode gateway rule data error", e); + return CommandResponse.ofFailure(e, "decode gateway rule data error"); + } + + RecordLog.info(String.format("[API Server] Receiving rule change (type: gateway rule): %s", data)); + + String result = SUCCESS_MSG; + List flowRules = JSONArray.parseArray(data, GatewayFlowRule.class); + GatewayRuleManager.loadRules(new HashSet<>(flowRules)); + return CommandResponse.ofSuccess(result); + } + + private static final String SUCCESS_MSG = "success"; +} \ No newline at end of file diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/param/GatewayParamParser.java b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/param/GatewayParamParser.java index ee3dfaafc5..f326468b68 100644 --- a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/param/GatewayParamParser.java +++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/param/GatewayParamParser.java @@ -53,25 +53,32 @@ public Object[] parseParameterFor(String resource, T request, Predicate gatewayRules = new HashSet<>(); Set 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()) { + if (!hasNonParamRule && gatewayRules.isEmpty()) { return new Object[0]; } - if (predSet.size() != 1 || predSet.contains(false)) { + 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; } diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/rule/GatewayRuleConverter.java b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/rule/GatewayRuleConverter.java index e2b091ba39..f5531fa24f 100644 --- a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/rule/GatewayRuleConverter.java +++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/rule/GatewayRuleConverter.java @@ -32,6 +32,17 @@ static FlowRule toFlowRule(/*@Valid*/ GatewayFlowRule rule) { .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. diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/rule/GatewayRuleManager.java b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/rule/GatewayRuleManager.java index f0ca5fd76d..e4880bed2d 100644 --- a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/rule/GatewayRuleManager.java +++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/rule/GatewayRuleManager.java @@ -29,10 +29,9 @@ 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 @@ */ public final class GatewayRuleManager { - private static final Map> RULE_MAP = new ConcurrentHashMap<>(); + /** + * Gateway flow rule map: (resource, [rules...]) + */ + private static final Map> GATEWAY_RULE_MAP = new ConcurrentHashMap<>(); + + private static final Map> CONVERTED_PARAM_RULE_MAP = new ConcurrentHashMap<>(); private static final GatewayRulePropertyListener LISTENER = new GatewayRulePropertyListener(); private static SentinelProperty> currentProperty = new DynamicSentinelProperty<>(); @@ -74,46 +78,69 @@ public static boolean loadRules(Set rules) { public static Set getRules() { Set rules = new HashSet<>(); - for (Set ruleSet : RULE_MAP.values()) { + for (Set ruleSet : GATEWAY_RULE_MAP.values()) { rules.addAll(ruleSet); } return rules; } public static Set getRulesForResource(String resourceName) { - AssertUtil.assertNotBlank(resourceName, "resourceName cannot be blank"); - Set set = RULE_MAP.get(resourceName); + if (StringUtil.isBlank(resourceName)) { + return new HashSet<>(); + } + Set set = GATEWAY_RULE_MAP.get(resourceName); if (set == null) { return new HashSet<>(); } return new HashSet<>(set); } + /** + *

Get all converted parameter rules.

+ *

Note: caller SHOULD NOT modify the list and rules.

+ * + * @param resourceName valid resource name + * @return converted parameter rules + */ + public static List getConvertedParamRules(String resourceName) { + if (StringUtil.isBlank(resourceName)) { + return new ArrayList<>(); + } + return CONVERTED_PARAM_RULE_MAP.get(resourceName); + } + private static final class GatewayRulePropertyListener implements PropertyListener> { @Override public void configUpdate(Set 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 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 idxMap, String resourceName) { + // Prepare index map. + if (!idxMap.containsKey(resourceName)) { + idxMap.put(resourceName, 0); + } + return idxMap.get(resourceName); } private synchronized void applyGatewayRuleInternal(Set conf) { if (conf == null || conf.isEmpty()) { - FlowRuleManager.loadRules(new ArrayList()); - ParamFlowRuleManager.loadRules(new ArrayList()); - RULE_MAP.clear(); + applyToConvertedParamMap(new HashSet()); + GATEWAY_RULE_MAP.clear(); return; } Map> gatewayRuleMap = new ConcurrentHashMap<>(); Map idxMap = new HashMap<>(); - List flowRules = new ArrayList<>(); Set paramFlowRules = new HashSet<>(); + Map> noParamMap = new HashMap<>(); for (GatewayFlowRule rule : conf) { if (!isValidRule(rule)) { @@ -122,14 +149,15 @@ private synchronized void applyGatewayRuleInternal(Set conf) { } 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 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 @@ private synchronized void applyGatewayRuleInternal(Set conf) { } ruleSet.add(rule); } - FlowRuleManager.loadRules(flowRules); - ParamFlowRuleManager.loadRules(new ArrayList<>(paramFlowRules)); + // Handle non-param mode rules. + for (Map.Entry> e : noParamMap.entrySet()) { + List 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)); + } + } + + applyToConvertedParamMap(paramFlowRules); + + GATEWAY_RULE_MAP.clear(); + GATEWAY_RULE_MAP.putAll(gatewayRuleMap); + } + + private void applyToConvertedParamMap(Set paramFlowRules) { + Map> 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 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); - RULE_MAP.clear(); - RULE_MAP.putAll(gatewayRuleMap); + RecordLog.info("[GatewayRuleManager] Converted internal param rules: " + CONVERTED_PARAM_RULE_MAP); } } diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/slot/GatewayFlowSlot.java b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/slot/GatewayFlowSlot.java new file mode 100644 index 0000000000..15151729d6 --- /dev/null +++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/slot/GatewayFlowSlot.java @@ -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 { + + @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 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); + } +} diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/slot/GatewaySlotChainBuilder.java b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/slot/GatewaySlotChainBuilder.java new file mode 100644 index 0000000000..383e465326 --- /dev/null +++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/common/slot/GatewaySlotChainBuilder.java @@ -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; + } +} diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.command.CommandHandler b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.command.CommandHandler new file mode 100755 index 0000000000..824408905b --- /dev/null +++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.command.CommandHandler @@ -0,0 +1,4 @@ +com.alibaba.csp.sentinel.adapter.gateway.common.command.UpdateGatewayApiDefinitionGroupCommandHandler +com.alibaba.csp.sentinel.adapter.gateway.common.command.UpdateGatewayRuleCommandHandler +com.alibaba.csp.sentinel.adapter.gateway.common.command.GetGatewayApiDefinitionGroupCommandHandler +com.alibaba.csp.sentinel.adapter.gateway.common.command.GetGatewayRuleCommandHandler \ No newline at end of file diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.slotchain.SlotChainBuilder b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.slotchain.SlotChainBuilder new file mode 100644 index 0000000000..5f9fde567e --- /dev/null +++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.slotchain.SlotChainBuilder @@ -0,0 +1 @@ +com.alibaba.csp.sentinel.adapter.gateway.common.slot.GatewaySlotChainBuilder \ No newline at end of file diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/common/param/GatewayParamParserTest.java b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/common/param/GatewayParamParserTest.java new file mode 100644 index 0000000000..7edc7844ed --- /dev/null +++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/common/param/GatewayParamParserTest.java @@ -0,0 +1,210 @@ +/* + * 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 routeIdPredicate = new Predicate() { + @Override + public boolean test(GatewayFlowRule e) { + return e.getResourceMode() == SentinelGatewayConstants.RESOURCE_MODE_ROUTE_ID; + } + }; + private final Predicate apiNamePredicate = new Predicate() { + @Override + public boolean test(GatewayFlowRule e) { + return e.getResourceMode() == SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME; + } + }; + + @Test + public void testParseParametersNoParamItem() { + RequestItemParser itemParser = mock(RequestItemParser.class); + GatewayParamParser parser = new GatewayParamParser<>(itemParser); + // Create a fake request. + Object request = new Object(); + // Prepare gateway rules. + Set rules = new HashSet<>(); + String routeId1 = "my_test_route_A"; + rules.add(new GatewayFlowRule(routeId1) + .setCount(5) + .setIntervalSec(1) + ); + rules.add(new GatewayFlowRule(routeId1) + .setCount(10) + .setControlBehavior(2) + .setMaxQueueingTimeoutMs(1000) + ); + GatewayRuleManager.loadRules(rules); + + Object[] params = parser.parseParameterFor(routeId1, request, routeIdPredicate); + assertThat(params.length).isEqualTo(1); + } + + @Test + public void testParseParametersWithItems() { + RequestItemParser itemParser = mock(RequestItemParser.class); + GatewayParamParser paramParser = new GatewayParamParser<>(itemParser); + // Create a fake request. + Object request = new Object(); + + // Prepare gateway rules. + Set 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 expectedHeaders = new HashMap() {{ + 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 headerMap) { + for (Map.Entry e : headerMap.entrySet()) { + when(parser.getHeader(any(), eq(e.getKey()))).thenReturn(e.getValue()); + } + } + + private void mockUrlParams(/*@Mock*/ RequestItemParser parser, Map paramMap) { + for (Map.Entry 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()); + GatewayRuleManager.loadRules(new HashSet()); + } + + @After + public void tearDown() { + GatewayApiDefinitionManager.loadApiDefinitions(new HashSet()); + GatewayRuleManager.loadRules(new HashSet()); + } +} diff --git a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/common/rule/GatewayRuleManagerTest.java b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/common/rule/GatewayRuleManagerTest.java index ab1fcc159d..fb2d0e7625 100644 --- a/sentinel-adapter/sentinel-api-gateway-adapter-common/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/common/rule/GatewayRuleManagerTest.java +++ b/sentinel-adapter/sentinel-api-gateway-adapter-common/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/common/rule/GatewayRuleManagerTest.java @@ -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 void testLoadAndGetGatewayRules() { rules.add(rule3); GatewayRuleManager.loadRules(rules); - assertTrue(FlowRuleManager.hasConfig(ahasRoute)); - assertTrue(ParamFlowRuleManager.hasRules(ahasRoute)); + List convertedRules = GatewayRuleManager.getConvertedParamRules(ahasRoute); + assertNotNull(convertedRules); assertEquals(0, (int)rule2.getParamItem().getIndex()); assertEquals(0, (int)rule3.getParamItem().getIndex()); assertTrue(GatewayRuleManager.getRulesForResource(ahasRoute).contains(rule1)); diff --git a/sentinel-adapter/sentinel-dubbo-adapter/pom.xml b/sentinel-adapter/sentinel-dubbo-adapter/pom.xml index 4d53ba1ccf..6bbf6d6a1b 100755 --- a/sentinel-adapter/sentinel-dubbo-adapter/pom.xml +++ b/sentinel-adapter/sentinel-dubbo-adapter/pom.xml @@ -6,7 +6,7 @@ com.alibaba.csp sentinel-adapter - 1.6.0-SNAPSHOT + 1.7.0-SNAPSHOT 4.0.0 sentinel-dubbo-adapter diff --git a/sentinel-adapter/sentinel-grpc-adapter/pom.xml b/sentinel-adapter/sentinel-grpc-adapter/pom.xml index 777b19b5b8..034b541987 100755 --- a/sentinel-adapter/sentinel-grpc-adapter/pom.xml +++ b/sentinel-adapter/sentinel-grpc-adapter/pom.xml @@ -5,7 +5,7 @@ sentinel-adapter com.alibaba.csp - 1.6.0-SNAPSHOT + 1.7.0-SNAPSHOT 4.0.0 sentinel-grpc-adapter diff --git a/sentinel-adapter/sentinel-reactor-adapter/pom.xml b/sentinel-adapter/sentinel-reactor-adapter/pom.xml index c6f809c75c..2e483c8933 100644 --- a/sentinel-adapter/sentinel-reactor-adapter/pom.xml +++ b/sentinel-adapter/sentinel-reactor-adapter/pom.xml @@ -5,7 +5,7 @@ sentinel-adapter com.alibaba.csp - 1.6.0-SNAPSHOT + 1.7.0-SNAPSHOT 4.0.0 diff --git a/sentinel-adapter/sentinel-reactor-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/reactor/SentinelReactorSubscriber.java b/sentinel-adapter/sentinel-reactor-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/reactor/SentinelReactorSubscriber.java index 6d134a3679..3a3be77404 100644 --- a/sentinel-adapter/sentinel-reactor-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/reactor/SentinelReactorSubscriber.java +++ b/sentinel-adapter/sentinel-reactor-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/reactor/SentinelReactorSubscriber.java @@ -160,7 +160,7 @@ protected void hookOnCancel() { private boolean tryCompleteEntry() { if (currentEntry != null && entryExited.compareAndSet(false, true)) { - currentEntry.exit(); + currentEntry.exit(1, entryConfig.getArgs()); return true; } return false; diff --git a/sentinel-adapter/sentinel-spring-cloud-gateway-adapter/pom.xml b/sentinel-adapter/sentinel-spring-cloud-gateway-adapter/pom.xml index 73c06db87d..b221cc7b99 100644 --- a/sentinel-adapter/sentinel-spring-cloud-gateway-adapter/pom.xml +++ b/sentinel-adapter/sentinel-spring-cloud-gateway-adapter/pom.xml @@ -5,7 +5,7 @@ sentinel-adapter com.alibaba.csp - 1.6.0-SNAPSHOT + 1.7.0-SNAPSHOT 4.0.0 diff --git a/sentinel-adapter/sentinel-spring-cloud-gateway-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/sc/SpringCloudGatewayParamParserTest.java b/sentinel-adapter/sentinel-spring-cloud-gateway-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/sc/SpringCloudGatewayParamParserTest.java index 5b9ca980ee..a28240e0b2 100644 --- a/sentinel-adapter/sentinel-spring-cloud-gateway-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/sc/SpringCloudGatewayParamParserTest.java +++ b/sentinel-adapter/sentinel-spring-cloud-gateway-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/sc/SpringCloudGatewayParamParserTest.java @@ -67,7 +67,7 @@ public void testParseParametersNoParamItem() { Object[] params = paramParser.parseParameterFor(routeId1, exchange, e -> e.getResourceMode() == 0); - assertThat(params.length).isZero(); + assertThat(params.length).isEqualTo(1); } @Test diff --git a/sentinel-adapter/sentinel-spring-webflux-adapter/pom.xml b/sentinel-adapter/sentinel-spring-webflux-adapter/pom.xml index 9180773e40..3c8a8d12e0 100644 --- a/sentinel-adapter/sentinel-spring-webflux-adapter/pom.xml +++ b/sentinel-adapter/sentinel-spring-webflux-adapter/pom.xml @@ -5,7 +5,7 @@ sentinel-adapter com.alibaba.csp - 1.6.0-SNAPSHOT + 1.7.0-SNAPSHOT 4.0.0 diff --git a/sentinel-adapter/sentinel-web-servlet/pom.xml b/sentinel-adapter/sentinel-web-servlet/pom.xml index ee69bae50e..fc82802ed2 100755 --- a/sentinel-adapter/sentinel-web-servlet/pom.xml +++ b/sentinel-adapter/sentinel-web-servlet/pom.xml @@ -6,7 +6,7 @@ com.alibaba.csp sentinel-adapter - 1.6.0-SNAPSHOT + 1.7.0-SNAPSHOT sentinel-web-servlet diff --git a/sentinel-adapter/sentinel-zuul-adapter/README.md b/sentinel-adapter/sentinel-zuul-adapter/README.md index f86b440674..9814a948bc 100755 --- a/sentinel-adapter/sentinel-zuul-adapter/README.md +++ b/sentinel-adapter/sentinel-zuul-adapter/README.md @@ -1,6 +1,7 @@ # Sentinel Zuul Adapter -Sentinel Zuul Adapter provides **ServiceId level** and **API Path level** flow control for Zuul gateway service. +Sentinel Zuul Adapter provides **route level** and **customized API level** +flow control for Zuul API Gateway. > *Note*: this adapter only support Zuul 1.x. @@ -18,54 +19,68 @@ Sentinel Zuul Adapter provides **ServiceId level** and **API Path level** flow c 2. Register filters +For Spring Cloud Zuul users, we only need to inject the three filters in Spring configuration class like this: + +```java +@Configuration +public class ZuulConfig { + + @Bean + public ZuulFilter sentinelZuulPreFilter() { + // We can provider the filter order here. + return new SentinelZuulPreFilter(10000); + } + + @Bean + public ZuulFilter sentinelZuulPostFilter() { + return new SentinelZuulPostFilter(1000); + } + + @Bean + public ZuulFilter sentinelZuulErrorFilter() { + return new SentinelZuulErrorFilter(-1); + } +} +``` + +For original Zuul users: + ```java -// get registry +// Get filter registry final FilterRegistry r = FilterRegistry.instance(); -// this is property config. set filter enable -SentinelZuulProperties properties = new SentinelZuulProperties(); -properties.setEnabled(true); -// set url cleaner, here use default -DefaultUrlCleaner defaultUrlCleaner = new DefaultUrlCleaner(); -// set origin parser. here use default -DefaultRequestOriginParser defaultRequestOriginParser = new DefaultRequestOriginParser(); - -// register filters. you must register all three filters. -SentinelPreFilter sentinelPreFilter = new SentinelPreFilter(properties, defaultUrlCleaner, defaultRequestOriginParser); -r.put("sentinelPreFilter", sentinelPreFilter); -SentinelPostFilter postFilter = new SentinelPostFilter(properties); -r.put("sentinelPostFilter", postFilter); -SentinelErrorFilter errorFilter = new SentinelErrorFilter(properties); -r.put("sentinelErrorFilter", errorFilter); + +// We need to register all three filters. +SentinelZuulPreFilter sentinelPreFilter = new SentinelZuulPreFilter(); +r.put("sentinelZuulPreFilter", sentinelPreFilter); +SentinelZuulPostFilter postFilter = new SentinelZuulPostFilter(); +r.put("sentinelZuulPostFilter", postFilter); +SentinelZuulErrorFilter errorFilter = new SentinelZuulErrorFilter(); +r.put("sentinelZuulErrorFilter", errorFilter); ``` ## How it works -As Zuul run as per thread per connection block model, we add filters around `route Filter` to trace sentinel statistics. +As Zuul run as per thread per connection block model, we add filters around route filter to trace Sentinel statistics. -- `SentinelPreFilter`: Get an entry of resource, the first order is **ServiceId** (the key in RequestContext is `serviceId`, this can set in own custom filter), then **API Path**. -- `SentinelPostFilter`: When success response, exit entry. -- `SentinelPreFilter`: When an `Exception` caught, trace the exception and exit context. +- `SentinelZuulPreFilter`: This pre-filter will regard all proxy ID (`proxy` in `RequestContext`) and all customized API as resources. When a `BlockException` caught, the filter will try to find a fallback to execute. +- `SentinelZuulPostFilter`: When the response has no exception caught, the post filter will complete the entries. +- `SentinelZuulPreFilter`: When an exception is caught, the filter will trace the exception and complete the entries. -The order of filters can be changed in property. +The order of filters can be changed via the constructor. The invocation chain resembles this: ```bash -EntranceNode: machine-root(t:3 pq:0 bq:0 tq:0 rt:0 prq:0 1mp:0 1mb:0 1mt:0) --EntranceNode: coke(t:2 pq:0 bq:0 tq:0 rt:0 prq:0 1mp:0 1mb:0 1mt:0) ---coke(t:2 pq:0 bq:0 tq:0 rt:0 prq:0 1mp:0 1mb:0 1mt:0) ----/coke/coke(t:0 pq:0 bq:0 tq:0 rt:0 prq:0 1mp:0 1mb:0 1mt:0) --EntranceNode: sentinel_default_context(t:0 pq:0 bq:0 tq:0 rt:0 prq:0 1mp:0 1mb:0 1mt:0) --EntranceNode: book(t:1 pq:0 bq:0 tq:0 rt:0 prq:0 1mp:0 1mb:0 1mt:0) ---book(t:1 pq:0 bq:0 tq:0 rt:0 prq:0 1mp:0 1mb:0 1mt:0) ----/book/coke(t:0 pq:0 bq:0 tq:0 rt:0 prq:0 1mp:0 1mb:0 1mt:0) +-EntranceNode: sentinel_gateway_context$$route$$another-route-b(t:0 pq:0.0 bq:0.0 tq:0.0 rt:0.0 prq:0.0 1mp:8 1mb:1 1mt:9) +--another-route-b(t:0 pq:0.0 bq:0.0 tq:0.0 rt:0.0 prq:0.0 1mp:4 1mb:1 1mt:5) +--another_customized_api(t:0 pq:0.0 bq:0.0 tq:0.0 rt:0.0 prq:0.0 1mp:4 1mb:0 1mt:4) +-EntranceNode: sentinel_gateway_context$$route$$my-route-1(t:0 pq:0.0 bq:0.0 tq:0.0 rt:0.0 prq:0.0 1mp:6 1mb:0 1mt:6) +--my-route-1(t:0 pq:0.0 bq:0.0 tq:0.0 rt:0.0 prq:0.0 1mp:2 1mb:0 1mt:2) +--some_customized_api(t:0 pq:0.0 bq:0.0 tq:0.0 rt:0.0 prq:0.0 1mp:2 1mb:0 1mt:2) ``` -- `book` and `coke` are serviceId. -- `/book/coke` is api path, the real API path is `/coke`. - ## Integration with Sentinel Dashboard 1. Start [Sentinel Dashboard](https://github.com/alibaba/Sentinel/wiki/Dashboard). @@ -76,7 +91,7 @@ EntranceNode: machine-root(t:3 pq:0 bq:0 tq:0 rt:0 prq:0 1mp:0 1mb:0 1mt:0) You can implement `SentinelFallbackProvider` to define your own fallback provider when Sentinel `BlockException` is thrown. The default fallback provider is `DefaultBlockFallbackProvider`. -By default fallback route is `ServiveId + URI PATH`, example `/book/coke`, first `book` is serviceId, `/coke` is URI PATH, so that both can be needed. +By default fallback route is proxy ID (or customized API name). Here is an example: @@ -90,7 +105,7 @@ public class MyBlockFallbackProvider implements ZuulBlockFallbackProvider { // you can define root as service level @Override public String getRoute() { - return "/coke/coke"; + return "my-route"; } @Override diff --git a/sentinel-adapter/sentinel-zuul-adapter/pom.xml b/sentinel-adapter/sentinel-zuul-adapter/pom.xml index 41228d5274..a00190ff24 100755 --- a/sentinel-adapter/sentinel-zuul-adapter/pom.xml +++ b/sentinel-adapter/sentinel-zuul-adapter/pom.xml @@ -5,7 +5,7 @@ sentinel-adapter com.alibaba.csp - 1.6.0-SNAPSHOT + 1.7.0-SNAPSHOT 4.0.0 sentinel-zuul-adapter @@ -21,6 +21,11 @@ com.alibaba.csp sentinel-core + + com.alibaba.csp + sentinel-api-gateway-adapter-common + + javax.servlet javax.servlet-api @@ -39,6 +44,20 @@ + + + org.springframework + spring-core + 4.3.20.RELEASE + provided + + + + org.springframework.cloud + spring-cloud-starter-netflix-zuul + 1.4.6.RELEASE + test + junit diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/RequestContextItemParser.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/RequestContextItemParser.java new file mode 100644 index 0000000000..c1e081e8b1 --- /dev/null +++ b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/RequestContextItemParser.java @@ -0,0 +1,47 @@ +/* + * 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.zuul; + +import com.alibaba.csp.sentinel.adapter.gateway.common.param.RequestItemParser; + +import com.netflix.zuul.context.RequestContext; + +/** + * @author Eric Zhao + * @since 1.6.0 + */ +public class RequestContextItemParser implements RequestItemParser { + + @Override + public String getPath(RequestContext requestContext) { + return requestContext.getRequest().getServletPath(); + } + + @Override + public String getRemoteAddress(RequestContext requestContext) { + return requestContext.getRequest().getRemoteAddr(); + } + + @Override + public String getHeader(RequestContext requestContext, String headerKey) { + return requestContext.getRequest().getHeader(headerKey); + } + + @Override + public String getUrlParam(RequestContext requestContext, String paramName) { + return requestContext.getRequest().getParameter(paramName); + } +} diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/ZuulApiDefinitionChangeObserver.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/ZuulApiDefinitionChangeObserver.java new file mode 100644 index 0000000000..a53c9a4e0b --- /dev/null +++ b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/ZuulApiDefinitionChangeObserver.java @@ -0,0 +1,33 @@ +/* + * 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.zuul.api; + +import java.util.Set; + +import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition; +import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinitionChangeObserver; + +/** + * @author Eric Zhao + * @since 1.6.0 + */ +public class ZuulApiDefinitionChangeObserver implements ApiDefinitionChangeObserver { + + @Override + public void onChange(Set apiDefinitions) { + ZuulGatewayApiMatcherManager.loadApiDefinitions(apiDefinitions); + } +} diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/ZuulGatewayApiMatcherManager.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/ZuulGatewayApiMatcherManager.java new file mode 100644 index 0000000000..b9ba7bc595 --- /dev/null +++ b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/ZuulGatewayApiMatcherManager.java @@ -0,0 +1,69 @@ +/* + * 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.zuul.api; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinition; +import com.alibaba.csp.sentinel.adapter.gateway.zuul.api.matcher.RequestContextApiMatcher; + +/** + * @author Eric Zhao + * @since 1.6.0 + */ +public final class ZuulGatewayApiMatcherManager { + + private static final Map API_MATCHER_MAP = new ConcurrentHashMap<>(); + + public static Map getApiMatcherMap() { + return Collections.unmodifiableMap(API_MATCHER_MAP); + } + + public static RequestContextApiMatcher getMatcher(final String apiName) { + if (apiName == null) { + return null; + } + return API_MATCHER_MAP.get(apiName); + } + + public static Set getApiDefinitionSet() { + Set set = new HashSet<>(); + for (RequestContextApiMatcher matcher : API_MATCHER_MAP.values()) { + set.add(matcher.getApiDefinition()); + } + return set; + } + + static synchronized void loadApiDefinitions(/*@Valid*/ Set definitions) { + if (definitions == null || definitions.isEmpty()) { + API_MATCHER_MAP.clear(); + return; + } + for (ApiDefinition definition : definitions) { + addApiDefinition(definition); + } + } + + static void addApiDefinition(ApiDefinition definition) { + API_MATCHER_MAP.put(definition.getApiName(), new RequestContextApiMatcher(definition)); + } + + private ZuulGatewayApiMatcherManager() {} +} diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/matcher/RequestContextApiMatcher.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/matcher/RequestContextApiMatcher.java new file mode 100644 index 0000000000..90718db194 --- /dev/null +++ b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/matcher/RequestContextApiMatcher.java @@ -0,0 +1,72 @@ +/* + * 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.zuul.api.matcher; + +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.ApiPathPredicateItem; +import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPredicateItem; +import com.alibaba.csp.sentinel.adapter.gateway.common.api.matcher.AbstractApiMatcher; +import com.alibaba.csp.sentinel.adapter.gateway.zuul.api.route.ZuulRouteMatchers; +import com.alibaba.csp.sentinel.util.StringUtil; +import com.alibaba.csp.sentinel.util.function.Predicate; + +import com.netflix.zuul.context.RequestContext; + +/** + * @author Eric Zhao + * @since 1.6.0 + */ +public class RequestContextApiMatcher extends AbstractApiMatcher { + + public RequestContextApiMatcher(ApiDefinition apiDefinition) { + super(apiDefinition); + } + + @Override + protected void initializeMatchers() { + if (apiDefinition.getPredicateItems() != null) { + for (ApiPredicateItem item : apiDefinition.getPredicateItems()) { + Predicate predicate = fromApiPredicate(item); + if (predicate != null) { + matchers.add(predicate); + } + } + } + } + + private Predicate fromApiPredicate(/*@NonNull*/ ApiPredicateItem item) { + if (item instanceof ApiPathPredicateItem) { + return fromApiPathPredicate((ApiPathPredicateItem)item); + } + return null; + } + + private Predicate fromApiPathPredicate(/*@Valid*/ ApiPathPredicateItem item) { + String pattern = item.getPattern(); + if (StringUtil.isBlank(pattern)) { + return null; + } + switch (item.getMatchStrategy()) { + case SentinelGatewayConstants.PARAM_MATCH_STRATEGY_REGEX: + return ZuulRouteMatchers.regexPath(pattern); + case SentinelGatewayConstants.PARAM_MATCH_STRATEGY_PREFIX: + return ZuulRouteMatchers.antPath(pattern); + default: + return ZuulRouteMatchers.exactPath(pattern); + } + } +} diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/route/PrefixRoutePathMatcher.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/route/PrefixRoutePathMatcher.java new file mode 100644 index 0000000000..21e636e589 --- /dev/null +++ b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/route/PrefixRoutePathMatcher.java @@ -0,0 +1,55 @@ +/* + * 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.zuul.api.route; + +import com.alibaba.csp.sentinel.util.AssertUtil; +import com.alibaba.csp.sentinel.util.function.Predicate; + +import com.netflix.zuul.context.RequestContext; +import org.springframework.util.AntPathMatcher; +import org.springframework.util.PathMatcher; + +/** + * @author Eric Zhao + * @since 1.6.0 + */ +public class PrefixRoutePathMatcher implements Predicate { + + private final String pattern; + + private final PathMatcher pathMatcher; + private final boolean canMatch; + + public PrefixRoutePathMatcher(String pattern) { + AssertUtil.assertNotBlank(pattern, "pattern cannot be blank"); + this.pattern = pattern; + this.pathMatcher = new AntPathMatcher(); + this.canMatch = pathMatcher.isPattern(pattern); + } + + @Override + public boolean test(RequestContext context) { + String path = context.getRequest().getServletPath(); + if (canMatch) { + return pathMatcher.match(pattern, path); + } + return false; + } + + public String getPattern() { + return pattern; + } +} diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/route/RegexRoutePathMatcher.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/route/RegexRoutePathMatcher.java new file mode 100644 index 0000000000..daf1310ec3 --- /dev/null +++ b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/route/RegexRoutePathMatcher.java @@ -0,0 +1,49 @@ +/* + * 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.zuul.api.route; + +import java.util.regex.Pattern; + +import com.alibaba.csp.sentinel.util.AssertUtil; +import com.alibaba.csp.sentinel.util.function.Predicate; + +import com.netflix.zuul.context.RequestContext; + +/** + * @author Eric Zhao + * @since 1.6.0 + */ +public class RegexRoutePathMatcher implements Predicate { + + private final String pattern; + private final Pattern regex; + + public RegexRoutePathMatcher(String pattern) { + AssertUtil.assertNotBlank(pattern, "pattern cannot be blank"); + this.pattern = pattern; + this.regex = Pattern.compile(pattern); + } + + @Override + public boolean test(RequestContext context) { + String path = context.getRequest().getServletPath(); + return regex.matcher(path).matches(); + } + + public String getPattern() { + return pattern; + } +} \ No newline at end of file diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/route/ZuulRouteMatchers.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/route/ZuulRouteMatchers.java new file mode 100644 index 0000000000..970c30e1b2 --- /dev/null +++ b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/api/route/ZuulRouteMatchers.java @@ -0,0 +1,55 @@ +/* + * 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.zuul.api.route; + +import com.alibaba.csp.sentinel.util.function.Predicate; + +import com.netflix.zuul.context.RequestContext; + +/** + * @author Eric Zhao + * @since 1.6.0 + */ +public final class ZuulRouteMatchers { + + public static Predicate all() { + return new Predicate() { + @Override + public boolean test(RequestContext requestContext) { + return true; + } + }; + } + + public static Predicate antPath(String pathPattern) { + return new PrefixRoutePathMatcher(pathPattern); + } + + public static Predicate exactPath(final String path) { + return new Predicate() { + @Override + public boolean test(RequestContext exchange) { + return exchange.getRequest().getServletPath().equals(path); + } + }; + } + + public static Predicate regexPath(String pathPattern) { + return new RegexRoutePathMatcher(pathPattern); + } + + private ZuulRouteMatchers() {} +} diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/DefaultRequestOriginParser.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/callback/DefaultRequestOriginParser.java similarity index 79% rename from sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/DefaultRequestOriginParser.java rename to sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/callback/DefaultRequestOriginParser.java index 45dba4d705..e2d078436f 100644 --- a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/DefaultRequestOriginParser.java +++ b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/callback/DefaultRequestOriginParser.java @@ -1,4 +1,4 @@ -package com.alibaba.csp.sentinel.adapter.zuul.fallback; +package com.alibaba.csp.sentinel.adapter.gateway.zuul.callback; import javax.servlet.http.HttpServletRequest; @@ -6,6 +6,7 @@ * @author tiger */ public class DefaultRequestOriginParser implements RequestOriginParser { + @Override public String parseOrigin(HttpServletRequest request) { return ""; diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/RequestOriginParser.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/callback/RequestOriginParser.java similarity index 94% rename from sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/RequestOriginParser.java rename to sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/callback/RequestOriginParser.java index 20f6d744a2..caacd0a0ef 100644 --- a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/RequestOriginParser.java +++ b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/callback/RequestOriginParser.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package com.alibaba.csp.sentinel.adapter.zuul.fallback; +package com.alibaba.csp.sentinel.adapter.gateway.zuul.callback; import javax.servlet.http.HttpServletRequest; diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/callback/ZuulGatewayCallbackManager.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/callback/ZuulGatewayCallbackManager.java new file mode 100644 index 0000000000..03bfa246cd --- /dev/null +++ b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/callback/ZuulGatewayCallbackManager.java @@ -0,0 +1,38 @@ +/* + * 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.zuul.callback; + +import com.alibaba.csp.sentinel.util.AssertUtil; + +/** + * @author Eric Zhao + * @since 1.6.0 + */ +public final class ZuulGatewayCallbackManager { + + private static volatile RequestOriginParser originParser = new DefaultRequestOriginParser(); + + public static RequestOriginParser getOriginParser() { + return originParser; + } + + public static void setOriginParser(RequestOriginParser originParser) { + AssertUtil.notNull(originParser, "originParser cannot be null"); + ZuulGatewayCallbackManager.originParser = originParser; + } + + private ZuulGatewayCallbackManager() {} +} diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/constants/ZuulConstant.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/constants/ZuulConstant.java similarity index 80% rename from sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/constants/ZuulConstant.java rename to sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/constants/ZuulConstant.java index 244fbf4ae8..3042805321 100644 --- a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/constants/ZuulConstant.java +++ b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/constants/ZuulConstant.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.alibaba.csp.sentinel.adapter.zuul.constants; +package com.alibaba.csp.sentinel.adapter.gateway.zuul.constants; import com.netflix.zuul.ZuulFilter; @@ -27,6 +27,10 @@ public class ZuulConstant { * Zuul {@link com.netflix.zuul.context.RequestContext} key for use in load balancer. */ public static final String SERVICE_ID_KEY = "serviceId"; + /** + * Zuul {@link com.netflix.zuul.context.RequestContext} key for proxying (route ID). + */ + public static final String PROXY_ID_KEY = "proxy"; /** * {@link ZuulFilter#filterType()} error type. @@ -58,5 +62,12 @@ public class ZuulConstant { */ public static final String ZUUL_DEFAULT_CONTEXT = "zuul_default_context"; + /** + * Zuul context key for keeping Sentinel entries. + * + * @since 1.6.0 + */ + public static final String ZUUL_CTX_SENTINEL_ENTRIES_KEY = "_sentinel_entries"; + private ZuulConstant(){} } diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/BlockResponse.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/fallback/BlockResponse.java similarity index 93% rename from sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/BlockResponse.java rename to sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/fallback/BlockResponse.java index 6fd617fd10..435850fce6 100644 --- a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/BlockResponse.java +++ b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/fallback/BlockResponse.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.alibaba.csp.sentinel.adapter.zuul.fallback; +package com.alibaba.csp.sentinel.adapter.gateway.zuul.fallback; /** * Fall back response for {@link com.alibaba.csp.sentinel.slots.block.BlockException} @@ -22,7 +22,12 @@ * @author tiger */ public class BlockResponse { + + /** + * HTTP status code. + */ private int code; + private String message; private String route; diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/DefaultBlockFallbackProvider.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/fallback/DefaultBlockFallbackProvider.java similarity index 85% rename from sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/DefaultBlockFallbackProvider.java rename to sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/fallback/DefaultBlockFallbackProvider.java index 432db872b1..1bd9403475 100644 --- a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/DefaultBlockFallbackProvider.java +++ b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/fallback/DefaultBlockFallbackProvider.java @@ -14,9 +14,8 @@ * limitations under the License. */ -package com.alibaba.csp.sentinel.adapter.zuul.fallback; +package com.alibaba.csp.sentinel.adapter.gateway.zuul.fallback; -import com.alibaba.csp.sentinel.log.RecordLog; import com.alibaba.csp.sentinel.slots.block.BlockException; /** @@ -33,7 +32,6 @@ public String getRoute() { @Override public BlockResponse fallbackResponse(String route, Throwable cause) { - RecordLog.info(String.format("[Sentinel DefaultBlockFallbackProvider] Run fallback route: %s", route)); if (cause instanceof BlockException) { return new BlockResponse(429, "Sentinel block exception", route); } else { diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/ZuulBlockFallbackManager.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/fallback/ZuulBlockFallbackManager.java similarity index 88% rename from sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/ZuulBlockFallbackManager.java rename to sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/fallback/ZuulBlockFallbackManager.java index 2760acff6b..187017623e 100644 --- a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/ZuulBlockFallbackManager.java +++ b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/fallback/ZuulBlockFallbackManager.java @@ -14,11 +14,13 @@ * limitations under the License. */ -package com.alibaba.csp.sentinel.adapter.zuul.fallback; +package com.alibaba.csp.sentinel.adapter.gateway.zuul.fallback; import java.util.HashMap; import java.util.Map; +import com.alibaba.csp.sentinel.util.AssertUtil; + /** * This provide fall back class manager. * @@ -26,7 +28,7 @@ */ public class ZuulBlockFallbackManager { - private static Map fallbackProviderCache = new HashMap(); + private static Map fallbackProviderCache = new HashMap<>(); private static ZuulBlockFallbackProvider defaultFallbackProvider = new DefaultBlockFallbackProvider(); @@ -34,6 +36,7 @@ public class ZuulBlockFallbackManager { * Register special provider for different route. */ public static synchronized void registerProvider(ZuulBlockFallbackProvider provider) { + AssertUtil.notNull(provider, "fallback provider cannot be null"); String route = provider.getRoute(); if ("*".equals(route) || route == null) { defaultFallbackProvider = provider; diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/ZuulBlockFallbackProvider.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/fallback/ZuulBlockFallbackProvider.java similarity index 95% rename from sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/ZuulBlockFallbackProvider.java rename to sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/fallback/ZuulBlockFallbackProvider.java index e6b3acb0c8..766220eb9a 100644 --- a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/ZuulBlockFallbackProvider.java +++ b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/fallback/ZuulBlockFallbackProvider.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.alibaba.csp.sentinel.adapter.zuul.fallback; +package com.alibaba.csp.sentinel.adapter.gateway.zuul.fallback; /** * This interface is compatible for different spring cloud version. diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelEntryUtils.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelEntryUtils.java new file mode 100644 index 0000000000..8c93a1b7ec --- /dev/null +++ b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelEntryUtils.java @@ -0,0 +1,66 @@ +/* + * 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.zuul.filters; + +import java.util.Deque; + +import com.alibaba.csp.sentinel.AsyncEntry; +import com.alibaba.csp.sentinel.Tracer; +import com.alibaba.csp.sentinel.adapter.gateway.zuul.constants.ZuulConstant; +import com.alibaba.csp.sentinel.context.ContextUtil; + +import com.netflix.zuul.context.RequestContext; + +/** + * @author Eric Zhao + * @since 1.6.0 + */ +final class SentinelEntryUtils { + + @SuppressWarnings("unchecked") + static void tryExitFromCurrentContext() { + RequestContext ctx = RequestContext.getCurrentContext(); + if (ctx.containsKey(ZuulConstant.ZUUL_CTX_SENTINEL_ENTRIES_KEY)) { + Deque asyncEntries = (Deque) ctx.get(ZuulConstant.ZUUL_CTX_SENTINEL_ENTRIES_KEY); + AsyncEntry entry; + while (!asyncEntries.isEmpty()) { + entry = asyncEntries.pop(); + entry.exit(); + } + ctx.remove(ZuulConstant.ZUUL_CTX_SENTINEL_ENTRIES_KEY); + } + + ContextUtil.exit(); + } + + @SuppressWarnings("unchecked") + static void tryTraceExceptionThenExitFromCurrentContext(Throwable t) { + RequestContext ctx = RequestContext.getCurrentContext(); + if (ctx.containsKey(ZuulConstant.ZUUL_CTX_SENTINEL_ENTRIES_KEY)) { + Deque asyncEntries = (Deque) ctx.get(ZuulConstant.ZUUL_CTX_SENTINEL_ENTRIES_KEY); + AsyncEntry entry; + while (!asyncEntries.isEmpty()) { + entry = asyncEntries.pop(); + Tracer.traceEntry(t, entry); + entry.exit(); + } + ctx.remove(ZuulConstant.ZUUL_CTX_SENTINEL_ENTRIES_KEY); + } + ContextUtil.exit(); + } + + private SentinelEntryUtils() {} +} diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulErrorFilter.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulErrorFilter.java new file mode 100644 index 0000000000..2e1d856bbe --- /dev/null +++ b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulErrorFilter.java @@ -0,0 +1,75 @@ +/* + * 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.adapter.gateway.zuul.filters; + +import com.alibaba.csp.sentinel.adapter.gateway.zuul.constants.ZuulConstant; +import com.alibaba.csp.sentinel.log.RecordLog; +import com.alibaba.csp.sentinel.slots.block.BlockException; + +import com.netflix.zuul.ZuulFilter; +import com.netflix.zuul.context.RequestContext; +import com.netflix.zuul.exception.ZuulException; + +/** + * This filter track routing exception and exit entry; + * + * @author tiger + * @author Eric Zhao + */ +public class SentinelZuulErrorFilter extends ZuulFilter { + + private final int order; + + public SentinelZuulErrorFilter() { + this(-1); + } + + public SentinelZuulErrorFilter(int order) { + this.order = order; + } + + @Override + public String filterType() { + return ZuulConstant.ERROR_TYPE; + } + + @Override + public boolean shouldFilter() { + RequestContext ctx = RequestContext.getCurrentContext(); + return ctx.getThrowable() != null; + } + + @Override + public int filterOrder() { + return order; + } + + @Override + public Object run() throws ZuulException { + RequestContext ctx = RequestContext.getCurrentContext(); + Throwable throwable = ctx.getThrowable(); + if (throwable != null) { + if (!BlockException.isBlockException(throwable)) { + // Trace exception for each entry and exit entries in order. + // The entries can be retrieved from the request context. + SentinelEntryUtils.tryTraceExceptionThenExitFromCurrentContext(throwable); + RecordLog.info("[SentinelZuulErrorFilter] Trace error cause", throwable.getCause()); + } + } + return null; + } +} diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulPostFilter.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulPostFilter.java new file mode 100644 index 0000000000..03cdddbfee --- /dev/null +++ b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulPostFilter.java @@ -0,0 +1,66 @@ +/* + * 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.adapter.gateway.zuul.filters; + +import com.alibaba.csp.sentinel.adapter.gateway.zuul.constants.ZuulConstant; + +import com.netflix.zuul.ZuulFilter; +import com.netflix.zuul.exception.ZuulException; + +import static com.alibaba.csp.sentinel.adapter.gateway.zuul.constants.ZuulConstant.SEND_RESPONSE_FILTER_ORDER; + +/** + * This filter will mark complete and exit {@link com.alibaba.csp.sentinel.Entry}. + * + * @author tiger + * @author Eric Zhao + */ +public class SentinelZuulPostFilter extends ZuulFilter { + + private final int order; + + public SentinelZuulPostFilter() { + this(SEND_RESPONSE_FILTER_ORDER); + } + + public SentinelZuulPostFilter(int order) { + this.order = order; + } + + @Override + public String filterType() { + return ZuulConstant.POST_TYPE; + } + + @Override + public int filterOrder() { + return order; + } + + @Override + public boolean shouldFilter() { + return true; + } + + @Override + public Object run() throws ZuulException { + // Exit the entries in order. + // The entries can be retrieved from the request context. + SentinelEntryUtils.tryExitFromCurrentContext(); + return null; + } +} diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulPreFilter.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulPreFilter.java new file mode 100644 index 0000000000..39f85ff0dc --- /dev/null +++ b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulPreFilter.java @@ -0,0 +1,161 @@ +/* + * 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.adapter.gateway.zuul.filters; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.HashSet; +import java.util.Set; + +import com.alibaba.csp.sentinel.AsyncEntry; +import com.alibaba.csp.sentinel.EntryType; +import com.alibaba.csp.sentinel.SphU; +import com.alibaba.csp.sentinel.adapter.gateway.common.param.GatewayParamParser; +import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule; +import com.alibaba.csp.sentinel.adapter.gateway.zuul.RequestContextItemParser; +import com.alibaba.csp.sentinel.adapter.gateway.zuul.api.ZuulGatewayApiMatcherManager; +import com.alibaba.csp.sentinel.adapter.gateway.zuul.api.matcher.RequestContextApiMatcher; +import com.alibaba.csp.sentinel.adapter.gateway.zuul.callback.ZuulGatewayCallbackManager; +import com.alibaba.csp.sentinel.adapter.gateway.zuul.constants.ZuulConstant; +import com.alibaba.csp.sentinel.adapter.gateway.zuul.fallback.BlockResponse; +import com.alibaba.csp.sentinel.adapter.gateway.zuul.fallback.ZuulBlockFallbackManager; +import com.alibaba.csp.sentinel.adapter.gateway.zuul.fallback.ZuulBlockFallbackProvider; +import com.alibaba.csp.sentinel.context.ContextUtil; +import com.alibaba.csp.sentinel.slots.block.BlockException; +import com.alibaba.csp.sentinel.util.StringUtil; +import com.alibaba.csp.sentinel.util.function.Predicate; + +import com.netflix.zuul.ZuulFilter; +import com.netflix.zuul.context.RequestContext; +import com.netflix.zuul.exception.ZuulException; + +import javax.servlet.http.HttpServletRequest; + +import static com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants.*; + +/** + * This pre-filter will regard all {@code proxyId} and all customized API as resources. + * When a BlockException caught, the filter will try to find a fallback to execute. + * + * @author tiger + * @author Eric Zhao + */ +public class SentinelZuulPreFilter extends ZuulFilter { + + private final int order; + + private final GatewayParamParser paramParser = new GatewayParamParser<>( + new RequestContextItemParser()); + + public SentinelZuulPreFilter() { + this(10000); + } + + public SentinelZuulPreFilter(int order) { + this.order = order; + } + + @Override + public String filterType() { + return ZuulConstant.PRE_TYPE; + } + + /** + * This run before route filter so we can get more accurate RT time. + */ + @Override + public int filterOrder() { + return order; + } + + @Override + public boolean shouldFilter() { + return true; + } + + private void doSentinelEntry(String resourceName, final int resType, RequestContext requestContext, + Deque asyncEntries) throws BlockException { + Object[] params = paramParser.parseParameterFor(resourceName, requestContext, + new Predicate() { + @Override + public boolean test(GatewayFlowRule r) { + return r.getResourceMode() == resType; + } + }); + AsyncEntry entry = SphU.asyncEntry(resourceName, EntryType.IN, 1, params); + asyncEntries.push(entry); + } + + @Override + public Object run() throws ZuulException { + RequestContext ctx = RequestContext.getCurrentContext(); + String origin = parseOrigin(ctx.getRequest()); + String routeId = (String)ctx.get(ZuulConstant.PROXY_ID_KEY); + + Deque asyncEntries = new ArrayDeque<>(); + String fallBackRoute = routeId; + try { + if (StringUtil.isNotBlank(routeId)) { + ContextUtil.enter(GATEWAY_CONTEXT_ROUTE_PREFIX + routeId, origin); + doSentinelEntry(routeId, RESOURCE_MODE_ROUTE_ID, ctx, asyncEntries); + } + + Set matchingApis = pickMatchingApiDefinitions(ctx); + if (!matchingApis.isEmpty() && ContextUtil.getContext() == null) { + ContextUtil.enter(ZuulConstant.ZUUL_DEFAULT_CONTEXT, origin); + } + for (String apiName : matchingApis) { + fallBackRoute = apiName; + doSentinelEntry(apiName, RESOURCE_MODE_CUSTOM_API_NAME, ctx, asyncEntries); + } + } catch (BlockException ex) { + ZuulBlockFallbackProvider zuulBlockFallbackProvider = ZuulBlockFallbackManager.getFallbackProvider( + fallBackRoute); + BlockResponse blockResponse = zuulBlockFallbackProvider.fallbackResponse(fallBackRoute, ex); + // Prevent routing from running + ctx.setRouteHost(null); + ctx.set(ZuulConstant.SERVICE_ID_KEY, null); + + // Set fallback response. + ctx.setResponseBody(blockResponse.toString()); + ctx.setResponseStatusCode(blockResponse.getCode()); + // Set Response ContentType + ctx.getResponse().setContentType("application/json; charset=utf-8"); + } finally { + // We don't exit the entry here. We need to exit the entries in post filter to record Rt correctly. + // So here the entries will be carried in the request context. + if (!asyncEntries.isEmpty()) { + ctx.put(ZuulConstant.ZUUL_CTX_SENTINEL_ENTRIES_KEY, asyncEntries); + } + } + return null; + } + + private String parseOrigin(HttpServletRequest request) { + return ZuulGatewayCallbackManager.getOriginParser().parseOrigin(request); + } + + private Set pickMatchingApiDefinitions(RequestContext requestContext) { + Set apis = new HashSet<>(); + for (RequestContextApiMatcher matcher : ZuulGatewayApiMatcherManager.getApiMatcherMap().values()) { + if (matcher.test(requestContext)) { + apis.add(matcher.getApiName()); + } + } + return apis; + } +} diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/DefaultUrlCleaner.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/DefaultUrlCleaner.java deleted file mode 100755 index 097808204e..0000000000 --- a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/DefaultUrlCleaner.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * 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.adapter.zuul.fallback; - -/*** - * @author tiger - */ -public class DefaultUrlCleaner implements UrlCleaner { - - @Override - public String clean(String originUrl) { - return originUrl; - } -} diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/UrlCleaner.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/UrlCleaner.java deleted file mode 100755 index d6b9a33b46..0000000000 --- a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/UrlCleaner.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * 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.adapter.zuul.fallback; - -/*** - * @author tiger - */ -public interface UrlCleaner { - - /*** - *

Process the url. Some path variables should be handled and unified.

- *

e.g. collect_item_relation--10200012121-.html will be converted to collect_item_relation.html

- * - * @param originUrl original url - * @return processed url - */ - String clean(String originUrl); -} diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/filters/AbstractSentinelFilter.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/filters/AbstractSentinelFilter.java deleted file mode 100644 index 0becdf53d3..0000000000 --- a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/filters/AbstractSentinelFilter.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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.adapter.zuul.filters; - -import com.alibaba.csp.sentinel.adapter.zuul.properties.SentinelZuulProperties; -import com.alibaba.csp.sentinel.util.AssertUtil; -import com.netflix.zuul.ZuulFilter; - -/** - * Abstract class for sentinel filters. - * - * @author tiger - */ -public abstract class AbstractSentinelFilter extends ZuulFilter { - - private final SentinelZuulProperties sentinelZuulProperties; - - public SentinelZuulProperties getSentinelZuulProperties() { - return sentinelZuulProperties; - } - - public AbstractSentinelFilter(SentinelZuulProperties sentinelZuulProperties) { - AssertUtil.notNull(sentinelZuulProperties,"SentinelZuulProperties can not be null"); - this.sentinelZuulProperties = sentinelZuulProperties; - } - - @Override - public boolean shouldFilter() { - return sentinelZuulProperties.isEnabled(); - } - -} diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/filters/SentinelErrorFilter.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/filters/SentinelErrorFilter.java deleted file mode 100644 index dc192dba5a..0000000000 --- a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/filters/SentinelErrorFilter.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * 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.adapter.zuul.filters; - -import com.alibaba.csp.sentinel.Tracer; -import com.alibaba.csp.sentinel.adapter.zuul.properties.SentinelZuulProperties; -import com.alibaba.csp.sentinel.context.ContextUtil; -import com.alibaba.csp.sentinel.log.RecordLog; -import com.alibaba.csp.sentinel.slots.block.BlockException; -import com.netflix.zuul.context.RequestContext; -import com.netflix.zuul.exception.ZuulException; - -import static com.alibaba.csp.sentinel.adapter.zuul.constants.ZuulConstant.ERROR_TYPE; - -/** - * This filter track routing exception and exit entry; - * - * @author tiger - */ -public class SentinelErrorFilter extends AbstractSentinelFilter { - - public SentinelErrorFilter(SentinelZuulProperties sentinelZuulProperties) { - super(sentinelZuulProperties); - } - - @Override - public String filterType() { - return ERROR_TYPE; - } - - @Override - public boolean shouldFilter() { - RequestContext ctx = RequestContext.getCurrentContext(); - return getSentinelZuulProperties().isEnabled() && ctx.getThrowable() != null; - } - - @Override - public int filterOrder() { - return getSentinelZuulProperties().getOrder().getError(); - } - - /** - * Trace not {@link BlockException} ex. - * While loop will exit all entries, - * Case serviceId and URL entry twice in {@link SentinelPreFilter}. - * The ContextUtil.getContext().getCurEntry() will exit from inner to outer. - */ - @Override - public Object run() throws ZuulException { - try { - RequestContext ctx = RequestContext.getCurrentContext(); - Throwable throwable = ctx.getThrowable(); - if (throwable != null) { - if (!BlockException.isBlockException(throwable)) { - Tracer.trace(throwable.getCause()); - RecordLog.info("[Sentinel Error Filter] Trace cause", throwable.getCause()); - } - } - } finally { - while (ContextUtil.getContext() != null && ContextUtil.getContext().getCurEntry() != null) { - ContextUtil.getContext().getCurEntry().exit(); - } - ContextUtil.exit(); - } - return null; - } -} diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/filters/SentinelPostFilter.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/filters/SentinelPostFilter.java deleted file mode 100644 index 9159489c1c..0000000000 --- a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/filters/SentinelPostFilter.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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.adapter.zuul.filters; - -import com.alibaba.csp.sentinel.adapter.zuul.properties.SentinelZuulProperties; -import com.alibaba.csp.sentinel.context.ContextUtil; -import com.alibaba.csp.sentinel.log.RecordLog; -import com.netflix.zuul.exception.ZuulException; - -import static com.alibaba.csp.sentinel.adapter.zuul.constants.ZuulConstant.POST_TYPE; - -/** - * This filter do success routing RT statistic and exit {@link com.alibaba.csp.sentinel.Entry} - * - * @author tiger - */ -public class SentinelPostFilter extends AbstractSentinelFilter { - - public SentinelPostFilter(SentinelZuulProperties sentinelZuulProperties) { - super(sentinelZuulProperties); - } - - @Override - public String filterType() { - return POST_TYPE; - } - - @Override - public int filterOrder() { - return getSentinelZuulProperties().getOrder().getPost(); - } - - /** - * While loop will exit all entries, - * Case serviceId and URL entry twice in {@link SentinelPreFilter}. - * The ContextUtil.getContext().getCurEntry() will exit from inner to outer. - */ - @Override - public Object run() throws ZuulException { - while (ContextUtil.getContext() != null && ContextUtil.getContext().getCurEntry() != null) { - ContextUtil.getContext().getCurEntry().exit(); - } - ContextUtil.exit(); - return null; - } -} diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/filters/SentinelPreFilter.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/filters/SentinelPreFilter.java deleted file mode 100644 index 7ef2c787a2..0000000000 --- a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/filters/SentinelPreFilter.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * 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.adapter.zuul.filters; - -import com.alibaba.csp.sentinel.EntryType; -import com.alibaba.csp.sentinel.SphU; -import com.alibaba.csp.sentinel.adapter.zuul.fallback.*; -import com.alibaba.csp.sentinel.adapter.zuul.properties.SentinelZuulProperties; -import com.alibaba.csp.sentinel.adapter.zuul.util.FilterUtil; -import com.alibaba.csp.sentinel.context.ContextUtil; -import com.alibaba.csp.sentinel.log.RecordLog; -import com.alibaba.csp.sentinel.slots.block.BlockException; -import com.alibaba.csp.sentinel.util.AssertUtil; -import com.alibaba.csp.sentinel.util.StringUtil; -import com.netflix.zuul.context.RequestContext; -import com.netflix.zuul.exception.ZuulException; - -import javax.servlet.http.HttpServletRequest; - -import static com.alibaba.csp.sentinel.adapter.zuul.constants.ZuulConstant.PRE_TYPE; -import static com.alibaba.csp.sentinel.adapter.zuul.constants.ZuulConstant.SERVICE_ID_KEY; -import static com.alibaba.csp.sentinel.adapter.zuul.constants.ZuulConstant.ZUUL_DEFAULT_CONTEXT; - -/** - * This pre filter get an entry of resource,the first order is ServiceId, then API Path. - * When get a BlockException run fallback logic. - * - * @author tiger - */ -public class SentinelPreFilter extends AbstractSentinelFilter { - - private final UrlCleaner urlCleaner; - - private final RequestOriginParser requestOriginParser; - - public SentinelPreFilter(SentinelZuulProperties sentinelZuulProperties, - UrlCleaner urlCleaner, - RequestOriginParser requestOriginParser) { - super(sentinelZuulProperties); - AssertUtil.notNull(urlCleaner, "UrlCleaner can not be null"); - AssertUtil.notNull(requestOriginParser, "RequestOriginParser can not be null"); - this.urlCleaner = urlCleaner; - this.requestOriginParser = requestOriginParser; - } - - @Override - public String filterType() { - return PRE_TYPE; - } - - /** - * This run before route filter so we can get more accurate RT time. - */ - @Override - public int filterOrder() { - return getSentinelZuulProperties().getOrder().getPre(); - } - - @Override - public Object run() throws ZuulException { - RequestContext ctx = RequestContext.getCurrentContext(); - String origin = parseOrigin(ctx.getRequest()); - String serviceTarget = (String) ctx.get(SERVICE_ID_KEY); - // When serviceId blocked first get the service level fallback provider. - String fallBackRoute = serviceTarget; - try { - if (StringUtil.isNotEmpty(serviceTarget)) { - RecordLog.info(String.format("[Sentinel Pre Filter] Origin: %s enter ServiceId: %s", origin, serviceTarget)); - ContextUtil.enter(serviceTarget, origin); - SphU.entry(serviceTarget, EntryType.IN); - } else { - RecordLog.info("[Sentinel Pre Filter] ServiceId is empty"); - ContextUtil.enter(ZUUL_DEFAULT_CONTEXT, origin); - } - String uriTarget = FilterUtil.filterTarget(ctx.getRequest()); - // Clean and unify the URL. - // For REST APIs, you have to clean the URL (e.g. `/foo/1` and `/foo/2` -> `/foo/:id`), or - // the amount of context and resources will exceed the threshold. - uriTarget = urlCleaner.clean(uriTarget); - fallBackRoute = uriTarget; - RecordLog.info(String.format("[Sentinel Pre Filter] Origin: %s enter Uri Path: %s", origin, uriTarget)); - SphU.entry(uriTarget, EntryType.IN); - } catch (BlockException ex) { - RecordLog.warn(String.format("[Sentinel Pre Filter] Block Exception when Origin: %s enter fall back route: %s", origin, fallBackRoute), ex); - ZuulBlockFallbackProvider zuulBlockFallbackProvider = ZuulBlockFallbackManager.getFallbackProvider(fallBackRoute); - BlockResponse blockResponse = zuulBlockFallbackProvider.fallbackResponse(fallBackRoute, ex); - // prevent routing from running - ctx.setRouteHost(null); - ctx.set(SERVICE_ID_KEY, null); - ctx.setResponseBody(blockResponse.toString()); - } - return null; - } - - private String parseOrigin(HttpServletRequest request) { - return requestOriginParser.parseOrigin(request); - } -} diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/properties/SentinelZuulProperties.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/properties/SentinelZuulProperties.java deleted file mode 100644 index 8e7681ed14..0000000000 --- a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/properties/SentinelZuulProperties.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * 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.adapter.zuul.properties; - -import static com.alibaba.csp.sentinel.adapter.zuul.constants.ZuulConstant.SEND_RESPONSE_FILTER_ORDER; - -/** - * Sentinel Spring Cloud Zuul AutoConfiguration property. - * - * @author tiger - */ -public class SentinelZuulProperties { - - private boolean enabled = false; - - private Order order = new Order(); - - public static class Order { - - private int post = SEND_RESPONSE_FILTER_ORDER - 10; - - private int pre = 10000; - - private int error = -1; - - public int getPost() { - return post; - } - - public void setPost(int post) { - this.post = post; - } - - public int getPre() { - return pre; - } - - public void setPre(int pre) { - this.pre = pre; - } - - public int getError() { - return error; - } - - public void setError(int error) { - this.error = error; - } - } - - public boolean isEnabled() { - return enabled; - } - - public void setEnabled(boolean enabled) { - this.enabled = enabled; - } - - public Order getOrder() { - return order; - } - - public void setOrder(Order order) { - this.order = order; - } -} diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/util/FilterUtil.java b/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/util/FilterUtil.java deleted file mode 100755 index cc00cceea8..0000000000 --- a/sentinel-adapter/sentinel-zuul-adapter/src/main/java/com/alibaba/csp/sentinel/adapter/zuul/util/FilterUtil.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * 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.adapter.zuul.util; - -import com.alibaba.csp.sentinel.util.StringUtil; - -import javax.servlet.http.HttpServletRequest; - -/** - * Util class for web servlet filter. - * This is same as servlet adapter util class - * - * @author tiger - */ -public final class FilterUtil { - - public static String filterTarget(HttpServletRequest request) { - String pathInfo = getResourcePath(request); - if (!pathInfo.startsWith("/")) { - pathInfo = "/" + pathInfo; - } - - if ("/".equals(pathInfo)) { - return pathInfo; - } - - // Note: pathInfo should be converted to camelCase style. - int lastSlashIndex = pathInfo.lastIndexOf("/"); - - if (lastSlashIndex >= 0) { - pathInfo = pathInfo.substring(0, lastSlashIndex) + "/" - + StringUtil.trim(pathInfo.substring(lastSlashIndex + 1)); - } else { - pathInfo = "/" + StringUtil.trim(pathInfo); - } - - return pathInfo; - } - - private static String getResourcePath(HttpServletRequest request) { - String pathInfo = normalizeAbsolutePath(request.getPathInfo(), false); - String servletPath = normalizeAbsolutePath(request.getServletPath(), pathInfo.length() != 0); - - return servletPath + pathInfo; - } - - private static String normalizeAbsolutePath(String path, boolean removeTrailingSlash) throws IllegalStateException { - return normalizePath(path, true, false, removeTrailingSlash); - } - - private static String normalizePath(String path, boolean forceAbsolute, boolean forceRelative, - boolean removeTrailingSlash) throws IllegalStateException { - char[] pathChars = StringUtil.trimToEmpty(path).toCharArray(); - int length = pathChars.length; - - // Check path and slash. - boolean startsWithSlash = false; - boolean endsWithSlash = false; - - if (length > 0) { - char firstChar = pathChars[0]; - char lastChar = pathChars[length - 1]; - - startsWithSlash = firstChar == '/' || firstChar == '\\'; - endsWithSlash = lastChar == '/' || lastChar == '\\'; - } - - StringBuilder buf = new StringBuilder(length); - boolean isAbsolutePath = forceAbsolute || !forceRelative && startsWithSlash; - int index = startsWithSlash ? 0 : -1; - int level = 0; - - if (isAbsolutePath) { - buf.append("/"); - } - - while (index < length) { - index = indexOfSlash(pathChars, index + 1, false); - - if (index == length) { - break; - } - - int nextSlashIndex = indexOfSlash(pathChars, index, true); - - String element = new String(pathChars, index, nextSlashIndex - index); - index = nextSlashIndex; - - // Ignore "." - if (".".equals(element)) { - continue; - } - - // Backtrack ".." - if ("..".equals(element)) { - if (level == 0) { - if (isAbsolutePath) { - throw new IllegalStateException(path); - } else { - buf.append("../"); - } - } else { - buf.setLength(pathChars[--level]); - } - - continue; - } - - pathChars[level++] = (char)buf.length(); - buf.append(element).append('/'); - } - - // remove the last "/" - if (buf.length() > 0) { - if (!endsWithSlash || removeTrailingSlash) { - buf.setLength(buf.length() - 1); - } - } - - return buf.toString(); - } - - private static int indexOfSlash(char[] chars, int beginIndex, boolean slash) { - int i = beginIndex; - - for (; i < chars.length; i++) { - char ch = chars[i]; - - if (slash) { - if (ch == '/' || ch == '\\') { - break; // if a slash - } - } else { - if (ch != '/' && ch != '\\') { - break; // if not a slash - } - } - } - - return i; - } - - private FilterUtil() {} -} diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinitionChangeObserver b/sentinel-adapter/sentinel-zuul-adapter/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinitionChangeObserver new file mode 100644 index 0000000000..a924cf5bc3 --- /dev/null +++ b/sentinel-adapter/sentinel-zuul-adapter/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiDefinitionChangeObserver @@ -0,0 +1 @@ +com.alibaba.csp.sentinel.adapter.gateway.zuul.api.ZuulApiDefinitionChangeObserver \ No newline at end of file diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/ZuulBlockFallbackManagerTest.java b/sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/fallback/ZuulBlockFallbackManagerTest.java similarity index 95% rename from sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/ZuulBlockFallbackManagerTest.java rename to sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/fallback/ZuulBlockFallbackManagerTest.java index 81425be222..f3c0b3e201 100644 --- a/sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/ZuulBlockFallbackManagerTest.java +++ b/sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/fallback/ZuulBlockFallbackManagerTest.java @@ -14,13 +14,13 @@ * limitations under the License. */ -package com.alibaba.csp.sentinel.adapter.zuul.fallback; +package com.alibaba.csp.sentinel.adapter.gateway.zuul.fallback; import com.alibaba.csp.sentinel.slots.block.flow.FlowException; + import org.junit.Assert; import org.junit.Test; - /** * @author tiger */ @@ -56,7 +56,7 @@ public void clear() { ZuulBlockFallbackManager.registerProvider(myNullResponseFallBackProvider); Assert.assertEquals(myNullResponseFallBackProvider.getRoute(), ROUTE); ZuulBlockFallbackManager.clear(); - Assert.assertEquals(ZuulBlockFallbackManager.getFallbackProvider(ROUTE).getRoute(),DEFAULT_ROUTE); + Assert.assertEquals(ZuulBlockFallbackManager.getFallbackProvider(ROUTE).getRoute(), DEFAULT_ROUTE); } } \ No newline at end of file diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/ZuulBlockFallbackProviderTest.java b/sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/fallback/ZuulBlockFallbackProviderTest.java similarity index 94% rename from sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/ZuulBlockFallbackProviderTest.java rename to sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/fallback/ZuulBlockFallbackProviderTest.java index f701d4a9da..f7ec49bc66 100644 --- a/sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/zuul/fallback/ZuulBlockFallbackProviderTest.java +++ b/sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/fallback/ZuulBlockFallbackProviderTest.java @@ -14,9 +14,10 @@ * limitations under the License. */ -package com.alibaba.csp.sentinel.adapter.zuul.fallback; +package com.alibaba.csp.sentinel.adapter.gateway.zuul.fallback; import com.alibaba.csp.sentinel.slots.block.flow.FlowException; + import org.junit.Assert; import org.junit.Test; @@ -48,7 +49,8 @@ public void testGetNotInCacheRoute() throws Exception { @Test public void testFlowControlFallbackResponse() throws Exception { ZuulBlockFallbackProvider fallbackProvider = ZuulBlockFallbackManager.getFallbackProvider(ALL_ROUTE); - BlockResponse clientHttpResponse = fallbackProvider.fallbackResponse(ALL_ROUTE, new FlowException("flow exception")); + BlockResponse clientHttpResponse = fallbackProvider.fallbackResponse(ALL_ROUTE, + new FlowException("flow exception")); Assert.assertEquals(clientHttpResponse.getCode(), 429); } diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/zuul/filters/SentinelErrorFilterTest.java b/sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulErrorFilterTest.java similarity index 51% rename from sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/zuul/filters/SentinelErrorFilterTest.java rename to sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulErrorFilterTest.java index 8f2bca89e7..4c9096d548 100644 --- a/sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/zuul/filters/SentinelErrorFilterTest.java +++ b/sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulErrorFilterTest.java @@ -14,42 +14,37 @@ * limitations under the License. */ -package com.alibaba.csp.sentinel.adapter.zuul.filters; +package com.alibaba.csp.sentinel.adapter.gateway.zuul.filters; -import com.alibaba.csp.sentinel.adapter.zuul.properties.SentinelZuulProperties; import com.netflix.zuul.context.RequestContext; import org.junit.Assert; import org.junit.Test; -import static com.alibaba.csp.sentinel.adapter.zuul.constants.ZuulConstant.ERROR_TYPE; +import static com.alibaba.csp.sentinel.adapter.gateway.zuul.constants.ZuulConstant.ERROR_TYPE; /** * @author tiger */ -public class SentinelErrorFilterTest { +public class SentinelZuulErrorFilterTest { + @Test public void testFilterType() throws Exception { - SentinelZuulProperties properties = new SentinelZuulProperties(); - SentinelErrorFilter sentinelErrorFilter = new SentinelErrorFilter(properties); - Assert.assertEquals(sentinelErrorFilter.filterType(), ERROR_TYPE); + SentinelZuulErrorFilter sentinelZuulErrorFilter = new SentinelZuulErrorFilter(); + Assert.assertEquals(sentinelZuulErrorFilter.filterType(), ERROR_TYPE); } @Test public void testShouldFilter() { - SentinelZuulProperties properties = new SentinelZuulProperties(); - Assert.assertFalse(properties.isEnabled()); - properties.setEnabled(true); - SentinelErrorFilter sentinelErrorFilter = new SentinelErrorFilter(properties); + SentinelZuulErrorFilter sentinelZuulErrorFilter = new SentinelZuulErrorFilter(); RequestContext ctx = RequestContext.getCurrentContext(); ctx.setThrowable(new RuntimeException()); - Assert.assertTrue(sentinelErrorFilter.shouldFilter()); + Assert.assertTrue(sentinelZuulErrorFilter.shouldFilter()); } @Test public void testRun() throws Exception { - SentinelZuulProperties properties = new SentinelZuulProperties(); - SentinelErrorFilter sentinelErrorFilter = new SentinelErrorFilter(properties); - Object result = sentinelErrorFilter.run(); + SentinelZuulErrorFilter sentinelZuulErrorFilter = new SentinelZuulErrorFilter(); + Object result = sentinelZuulErrorFilter.run(); Assert.assertNull(result); } diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/zuul/filters/SentinelPostFilterTest.java b/sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulPostFilterTest.java similarity index 55% rename from sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/zuul/filters/SentinelPostFilterTest.java rename to sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulPostFilterTest.java index 6ccd6cae20..8d8046190d 100644 --- a/sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/zuul/filters/SentinelPostFilterTest.java +++ b/sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulPostFilterTest.java @@ -14,31 +14,28 @@ * limitations under the License. */ -package com.alibaba.csp.sentinel.adapter.zuul.filters; +package com.alibaba.csp.sentinel.adapter.gateway.zuul.filters; -import com.alibaba.csp.sentinel.adapter.zuul.properties.SentinelZuulProperties; import org.junit.Assert; import org.junit.Test; -import static com.alibaba.csp.sentinel.adapter.zuul.constants.ZuulConstant.POST_TYPE; +import static com.alibaba.csp.sentinel.adapter.gateway.zuul.constants.ZuulConstant.POST_TYPE; /** * @author tiger */ -public class SentinelPostFilterTest { +public class SentinelZuulPostFilterTest { @Test public void testFilterType() throws Exception { - SentinelZuulProperties properties = new SentinelZuulProperties(); - SentinelPostFilter sentinelPostFilter = new SentinelPostFilter(properties); - Assert.assertEquals(sentinelPostFilter.filterType(), POST_TYPE); + SentinelZuulPostFilter sentinelZuulPostFilter = new SentinelZuulPostFilter(); + Assert.assertEquals(sentinelZuulPostFilter.filterType(), POST_TYPE); } @Test public void testRun() throws Exception { - SentinelZuulProperties properties = new SentinelZuulProperties(); - SentinelPostFilter sentinelPostFilter = new SentinelPostFilter(properties); - Object result = sentinelPostFilter.run(); + SentinelZuulPostFilter sentinelZuulPostFilter = new SentinelZuulPostFilter(); + Object result = sentinelZuulPostFilter.run(); Assert.assertNull(result); } diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulPreFilterTest.java b/sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulPreFilterTest.java new file mode 100644 index 0000000000..289a9b92c8 --- /dev/null +++ b/sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/gateway/zuul/filters/SentinelZuulPreFilterTest.java @@ -0,0 +1,61 @@ +/* + * 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.adapter.gateway.zuul.filters; + +import com.netflix.zuul.context.RequestContext; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import javax.servlet.http.HttpServletRequest; + +import static com.alibaba.csp.sentinel.adapter.gateway.zuul.constants.ZuulConstant.PRE_TYPE; +import static com.alibaba.csp.sentinel.adapter.gateway.zuul.constants.ZuulConstant.SERVICE_ID_KEY; +import static org.mockito.Mockito.when; + +/** + * @author tiger + */ +public class SentinelZuulPreFilterTest { + + private String SERVICE_ID = "servicea"; + + private String URI = "/servicea/test"; + + @Mock + private HttpServletRequest httpServletRequest; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + when(httpServletRequest.getContextPath()).thenReturn(""); + when(httpServletRequest.getPathInfo()).thenReturn(URI); + RequestContext requestContext = new RequestContext(); + requestContext.set(SERVICE_ID_KEY, SERVICE_ID); + requestContext.setRequest(httpServletRequest); + RequestContext.testSetCurrentContext(requestContext); + } + + @Test + public void testFilterType() throws Exception { + SentinelZuulPreFilter sentinelZuulPreFilter = new SentinelZuulPreFilter(); + Assert.assertEquals(sentinelZuulPreFilter.filterType(), PRE_TYPE); + } + +} \ No newline at end of file diff --git a/sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/zuul/filters/SentinelPreFilterTest.java b/sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/zuul/filters/SentinelPreFilterTest.java deleted file mode 100644 index ab71e5cfd7..0000000000 --- a/sentinel-adapter/sentinel-zuul-adapter/src/test/java/com/alibaba/csp/sentinel/adapter/zuul/filters/SentinelPreFilterTest.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * 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.adapter.zuul.filters; - -import com.alibaba.csp.sentinel.adapter.zuul.fallback.DefaultRequestOriginParser; -import com.alibaba.csp.sentinel.adapter.zuul.fallback.RequestOriginParser; -import com.alibaba.csp.sentinel.adapter.zuul.fallback.UrlCleaner; -import com.alibaba.csp.sentinel.adapter.zuul.properties.SentinelZuulProperties; -import com.alibaba.csp.sentinel.slots.block.flow.FlowException; -import com.netflix.zuul.context.RequestContext; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; - -import javax.servlet.http.HttpServletRequest; - -import static com.alibaba.csp.sentinel.adapter.zuul.constants.ZuulConstant.PRE_TYPE; -import static com.alibaba.csp.sentinel.adapter.zuul.constants.ZuulConstant.SERVICE_ID_KEY; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.when; - -/** - * @author tiger - */ -public class SentinelPreFilterTest { - - private String SERVICE_ID = "servicea"; - - private String URI = "/servicea/test"; - - @Mock - private HttpServletRequest httpServletRequest; - - @Mock - private UrlCleaner urlCleaner; - - private final RequestOriginParser requestOriginParser = new DefaultRequestOriginParser(); - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - when(httpServletRequest.getContextPath()).thenReturn(""); - when(httpServletRequest.getPathInfo()).thenReturn(URI); - RequestContext requestContext = new RequestContext(); - requestContext.set(SERVICE_ID_KEY, SERVICE_ID); - requestContext.setRequest(httpServletRequest); - RequestContext.testSetCurrentContext(requestContext); - } - - @Test - public void testFilterType() throws Exception { - SentinelZuulProperties properties = new SentinelZuulProperties(); - SentinelPreFilter sentinelPreFilter = new SentinelPreFilter(properties, urlCleaner, requestOriginParser); - Assert.assertEquals(sentinelPreFilter.filterType(), PRE_TYPE); - } - - @Test - public void testRun() throws Exception { - RequestContext ctx = RequestContext.getCurrentContext(); - SentinelZuulProperties properties = new SentinelZuulProperties(); - SentinelPreFilter sentinelPreFilter = new SentinelPreFilter(properties, urlCleaner, requestOriginParser); - given(urlCleaner.clean(URI)).willReturn(URI); - sentinelPreFilter.run(); - Assert.assertNull(ctx.getRouteHost()); - Assert.assertEquals(ctx.get(SERVICE_ID_KEY), SERVICE_ID); - } - - @Test - public void testServiceFallBackRun() throws Exception { - RequestContext ctx = RequestContext.getCurrentContext(); - SentinelZuulProperties properties = new SentinelZuulProperties(); - properties.setEnabled(true); - SentinelPreFilter sentinelPreFilter = new SentinelPreFilter(properties, urlCleaner, requestOriginParser); - - given(urlCleaner.clean(URI)).willAnswer( - new Answer() { - @Override - public Object answer(InvocationOnMock invocation) throws Throwable { - throw new FlowException("flow ex"); - } - } - ); - sentinelPreFilter.run(); - Assert.assertNull(ctx.getRouteHost()); - Assert.assertNull(ctx.get(SERVICE_ID_KEY)); - } - -} \ No newline at end of file diff --git a/sentinel-benchmark/pom.xml b/sentinel-benchmark/pom.xml index de742517a3..7f7e9a88b1 100644 --- a/sentinel-benchmark/pom.xml +++ b/sentinel-benchmark/pom.xml @@ -5,7 +5,7 @@ sentinel-parent com.alibaba.csp - 1.6.0-SNAPSHOT + 1.7.0-SNAPSHOT 4.0.0 diff --git a/sentinel-cluster/pom.xml b/sentinel-cluster/pom.xml index b24811deb1..c58ee305bc 100644 --- a/sentinel-cluster/pom.xml +++ b/sentinel-cluster/pom.xml @@ -5,7 +5,7 @@ sentinel-parent com.alibaba.csp - 1.6.0-SNAPSHOT + 1.7.0-SNAPSHOT 4.0.0 pom diff --git a/sentinel-cluster/sentinel-cluster-client-default/pom.xml b/sentinel-cluster/sentinel-cluster-client-default/pom.xml index dd543bf596..80bf32dffe 100644 --- a/sentinel-cluster/sentinel-cluster-client-default/pom.xml +++ b/sentinel-cluster/sentinel-cluster-client-default/pom.xml @@ -5,7 +5,7 @@ sentinel-cluster com.alibaba.csp - 1.6.0-SNAPSHOT + 1.7.0-SNAPSHOT 4.0.0 diff --git a/sentinel-cluster/sentinel-cluster-common-default/pom.xml b/sentinel-cluster/sentinel-cluster-common-default/pom.xml index d22d9e038d..cc1a988060 100644 --- a/sentinel-cluster/sentinel-cluster-common-default/pom.xml +++ b/sentinel-cluster/sentinel-cluster-common-default/pom.xml @@ -5,7 +5,7 @@ sentinel-cluster com.alibaba.csp - 1.6.0-SNAPSHOT + 1.7.0-SNAPSHOT 4.0.0 diff --git a/sentinel-cluster/sentinel-cluster-server-default/pom.xml b/sentinel-cluster/sentinel-cluster-server-default/pom.xml index ddd5eb4a89..6522b710fb 100644 --- a/sentinel-cluster/sentinel-cluster-server-default/pom.xml +++ b/sentinel-cluster/sentinel-cluster-server-default/pom.xml @@ -5,7 +5,7 @@ sentinel-cluster com.alibaba.csp - 1.6.0-SNAPSHOT + 1.7.0-SNAPSHOT 4.0.0 diff --git a/sentinel-core/pom.xml b/sentinel-core/pom.xml index 2eb8159d47..9228ee7e3e 100755 --- a/sentinel-core/pom.xml +++ b/sentinel-core/pom.xml @@ -6,7 +6,7 @@ com.alibaba.csp sentinel-parent - 1.6.0-SNAPSHOT + 1.7.0-SNAPSHOT sentinel-core jar diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/Constants.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/Constants.java index 923e04ae26..6e728bb96e 100755 --- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/Constants.java +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/Constants.java @@ -30,7 +30,7 @@ */ public final class Constants { - public static final String SENTINEL_VERSION = VersionUtil.getVersion("1.6.0"); + public static final String SENTINEL_VERSION = VersionUtil.getVersion("1.7.0"); public final static int MAX_CONTEXT_NAME_SIZE = 2000; public final static int MAX_SLOT_CHAIN_SIZE = 6000; @@ -43,6 +43,16 @@ public final class Constants { */ public final static String TOTAL_IN_RESOURCE_NAME = "__total_inbound_traffic__"; + /** + * A virtual resource identifier for cpu usage statistics (since 1.6.1). + */ + public final static String CPU_USAGE_RESOURCE_NAME = "__cpu_usage__"; + + /** + * A virtual resource identifier for system load statistics (since 1.6.1). + */ + public final static String SYSTEM_LOAD_RESOURCE_NAME = "__system_load__"; + /** * Global ROOT statistic node that represents the universal parent node. */ diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/Tracer.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/Tracer.java index 8a5cdea7eb..08df6b2c94 100755 --- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/Tracer.java +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/Tracer.java @@ -17,9 +17,12 @@ import com.alibaba.csp.sentinel.context.Context; import com.alibaba.csp.sentinel.context.ContextUtil; +import com.alibaba.csp.sentinel.metric.extension.MetricExtensionProvider; import com.alibaba.csp.sentinel.node.ClusterNode; import com.alibaba.csp.sentinel.node.DefaultNode; import com.alibaba.csp.sentinel.slots.block.BlockException; +import com.alibaba.csp.sentinel.metric.extension.MetricExtension; +import com.alibaba.csp.sentinel.util.AssertUtil; /** * This class is used to record other exceptions except block exception. @@ -27,7 +30,12 @@ * @author jialiang.linjl * @author Eric Zhao */ -public final class Tracer { +public class Tracer { + + protected static Class[] traceClasses; + protected static Class[] ignoreClasses; + + protected Tracer() {} /** * Trace provided {@link Throwable} and increment exception count to entry in current context. @@ -45,7 +53,7 @@ public static void trace(Throwable e) { * @param count exception count to add */ public static void trace(Throwable e, int count) { - if (e == null || e instanceof BlockException) { + if (!shouldTrace(e)) { return; } @@ -55,7 +63,7 @@ public static void trace(Throwable e, int count) { } DefaultNode curNode = (DefaultNode)context.getCurNode(); - traceExceptionToNode(e, count, curNode); + traceExceptionToNode(e, count, context.getCurEntry(), curNode); } /** @@ -66,7 +74,7 @@ public static void trace(Throwable e, int count) { * @since 1.4.2 */ public static void traceContext(Throwable e, int count, Context context) { - if (e == null || e instanceof BlockException) { + if (!shouldTrace(e)) { return; } if (context == null) { @@ -74,7 +82,7 @@ public static void traceContext(Throwable e, int count, Context context) { } DefaultNode curNode = (DefaultNode)context.getCurNode(); - traceExceptionToNode(e, count, curNode); + traceExceptionToNode(e, count, context.getCurEntry(), curNode); } /** @@ -95,7 +103,7 @@ public static void traceEntry(Throwable e, Entry entry) { * @since 1.4.2 */ public static void traceEntry(Throwable e, int count, Entry entry) { - if (e == null || e instanceof BlockException) { + if (!shouldTrace(e)) { return; } if (entry == null || entry.getCurNode() == null) { @@ -103,13 +111,16 @@ public static void traceEntry(Throwable e, int count, Entry entry) { } DefaultNode curNode = (DefaultNode)entry.getCurNode(); - traceExceptionToNode(e, count, curNode); + traceExceptionToNode(e, count, entry, curNode); } - private static void traceExceptionToNode(Throwable t, int count, DefaultNode curNode) { + private static void traceExceptionToNode(Throwable t, int count, Entry entry, DefaultNode curNode) { if (curNode == null) { return; } + for (MetricExtension m : MetricExtensionProvider.getMetricExtensions()) { + m.addException(entry.getResourceWrapper().getName(), count, t); + } // clusterNode can be null when Constants.ON is false. ClusterNode clusterNode = curNode.getClusterNode(); @@ -119,5 +130,90 @@ private static void traceExceptionToNode(Throwable t, int count, DefaultNode cur clusterNode.trace(t, count); } - private Tracer() {} + /** + * Set exception to trace. If not set, all Exception except for {@link BlockException} will be traced. + *

+ * Note that if both {@link #setExceptionsToIgnore(Class[])} and this method is set, + * the ExceptionsToIgnore will be of higher precedence. + *

+ * + * @param traceClasses the list of exception classes to trace. + * @since 1.6.1 + */ + @SafeVarargs + public static void setExceptionsToTrace(Class... traceClasses) { + checkNotNull(traceClasses); + Tracer.traceClasses = traceClasses; + } + + /** + * Get exception classes to trace. + * + * @return an array of exception classes to trace. + * @since 1.6.1 + */ + public static Class[] getExceptionsToTrace() { + return traceClasses; + } + + /** + * Set exceptions to ignore. if not set, all Exception except for {@link BlockException} will be traced. + *

+ * Note that if both {@link #setExceptionsToTrace(Class[])} and this method is set, + * the ExceptionsToIgnore will be of higher precedence. + *

+ * + * @param ignoreClasses the list of exception classes to ignore. + * @since 1.6.1 + */ + @SafeVarargs + public static void setExceptionsToIgnore(Class... ignoreClasses) { + checkNotNull(ignoreClasses); + Tracer.ignoreClasses = ignoreClasses; + } + + /** + * Get exception classes to ignore. + * + * @return an array of exception classes to ignore. + * @since 1.6.1 + */ + public static Class[] getExceptionsToIgnore() { + return ignoreClasses; + } + + private static void checkNotNull(Class[] classes) { + AssertUtil.notNull(classes, "trace or ignore classes must not be null"); + for (Class clazz : classes) { + AssertUtil.notNull(clazz, "trace or ignore classes must not be null"); + } + } + + /** + * Check whether the throwable should be traced. + * + * @param t the throwable to check. + * @return true if the throwable should be traced, else return false. + */ + protected static boolean shouldTrace(Throwable t) { + if (t == null || t instanceof BlockException) { + return false; + } + if (ignoreClasses != null) { + for (Class clazz : ignoreClasses) { + if (clazz != null && clazz.isAssignableFrom(t.getClass())) { + return false; + } + } + } + if (traceClasses != null) { + for (Class clazz : traceClasses) { + if (clazz != null && clazz.isAssignableFrom(t.getClass())) { + return true; + } + } + return false; + } + return true; + } } diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/annotation/SentinelResource.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/annotation/SentinelResource.java index e4f07a66a3..a0fa11d675 100644 --- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/annotation/SentinelResource.java +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/annotation/SentinelResource.java @@ -60,6 +60,27 @@ */ String fallback() default ""; + /** + * The {@code defaultFallback} is used as the default universal fallback method. + * It should not accept any parameters, and the return type should be compatible + * with the original method. + * + * @return name of the default fallback method, empty by default + * @since 1.6.0 + */ + String defaultFallback() default ""; + + /** + * The {@code fallback} is located in the same class with the original method by default. + * However, if some methods share the same signature and intend to set the same fallback, + * then users can set the class where the fallback function exists. Note that the shared fallback method + * must be static. + * + * @return the class where the fallback method is located (only single class) + * @since 1.6.0 + */ + Class[] fallbackClass() default {}; + /** * @return the list of exception classes to trace, {@link Throwable} by default * @since 1.5.1 diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/config/SentinelConfig.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/config/SentinelConfig.java index 493b9e409a..5079191238 100755 --- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/config/SentinelConfig.java +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/config/SentinelConfig.java @@ -20,6 +20,7 @@ import java.util.Map; import java.util.Properties; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArraySet; import com.alibaba.csp.sentinel.log.LogBase; import com.alibaba.csp.sentinel.log.RecordLog; @@ -123,7 +124,7 @@ private static void loadProps() { } // JVM parameter override file config. - for (Map.Entry entry : System.getProperties().entrySet()) { + for (Map.Entry entry : new CopyOnWriteArraySet<>(System.getProperties().entrySet())) { String configKey = entry.getKey().toString(); String configValue = entry.getValue().toString(); String configValueOld = getConfig(configKey); diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/MetricCallbackInit.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/MetricCallbackInit.java new file mode 100644 index 0000000000..0ffef9e12f --- /dev/null +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/MetricCallbackInit.java @@ -0,0 +1,22 @@ +package com.alibaba.csp.sentinel.metric.extension; + +import com.alibaba.csp.sentinel.init.InitFunc; +import com.alibaba.csp.sentinel.metric.extension.callback.MetricEntryCallback; +import com.alibaba.csp.sentinel.metric.extension.callback.MetricExitCallback; +import com.alibaba.csp.sentinel.slots.statistic.StatisticSlotCallbackRegistry; + +/** + * Register callbacks for metric extension. + * + * @author Carpenter Lee + * @since 1.6.1 + */ +public class MetricCallbackInit implements InitFunc { + @Override + public void init() throws Exception { + StatisticSlotCallbackRegistry.addEntryCallback(MetricEntryCallback.class.getCanonicalName(), + new MetricEntryCallback()); + StatisticSlotCallbackRegistry.addExitCallback(MetricExitCallback.class.getCanonicalName(), + new MetricExitCallback()); + } +} diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/MetricExtension.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/MetricExtension.java new file mode 100644 index 0000000000..8f21328b8f --- /dev/null +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/MetricExtension.java @@ -0,0 +1,86 @@ +package com.alibaba.csp.sentinel.metric.extension; + +import com.alibaba.csp.sentinel.slots.block.BlockException; + +/** + * This interface provides extension to Sentinel internal statistics. + *

+ * Please note that all method in this class will invoke in the same thread of biz logic. + * It's necessary to not do time-consuming operation in any of the interface's method, + * otherwise biz logic will be blocked. + *

+ * + * @author Carpenter Lee + * @since 1.6.1 + */ +public interface MetricExtension { + + /** + * Add current pass count of the resource name. + * + * @param n count to add + * @param resource resource name + * @param args additional arguments of the resource, eg. if the resource is a method name, + * the args will be the parameters of the method. + */ + void addPass(String resource, int n, Object... args); + + /** + * Add current block count of the resource name. + * + * @param n count to add + * @param resource resource name + * @param origin the original invoker. + * @param blockException block exception related. + * @param args additional arguments of the resource, eg. if the resource is a method name, + * the args will be the parameters of the method. + */ + void addBlock(String resource, int n, String origin, BlockException blockException, Object... args); + + /** + * Add current completed count of the resource name. + * + * @param n count to add + * @param resource resource name + * @param args additional arguments of the resource, eg. if the resource is a method name, + * the args will be the parameters of the method. + */ + void addSuccess(String resource, int n, Object... args); + + /** + * Add current exception count of the resource name. + * + * @param n count to add + * @param resource resource name + * @param throwable exception related. + */ + void addException(String resource, int n, Throwable throwable); + + /** + * Add response time of the resource name. + * + * @param rt response time in millisecond + * @param resource resource name + * @param args additional arguments of the resource, eg. if the resource is a method name, + * the args will be the parameters of the method. + */ + void addRt(String resource, long rt, Object... args); + + /** + * Increase current thread count of the resource name. + * + * @param resource resource name + * @param args additional arguments of the resource, eg. if the resource is a method name, + * the args will be the parameters of the method. + */ + void increaseThreadNum(String resource, Object... args); + + /** + * Decrease current thread count of the resource name. + * + * @param resource resource name + * @param args additional arguments of the resource, eg. if the resource is a method name, + * the args will be the parameters of the method. + */ + void decreaseThreadNum(String resource, Object... args); +} diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/MetricExtensionProvider.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/MetricExtensionProvider.java new file mode 100644 index 0000000000..9980a6e137 --- /dev/null +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/MetricExtensionProvider.java @@ -0,0 +1,54 @@ +package com.alibaba.csp.sentinel.metric.extension; + +import java.util.ArrayList; +import java.util.List; + +import com.alibaba.csp.sentinel.log.RecordLog; +import com.alibaba.csp.sentinel.util.SpiLoader; + +/** + * Get all {@link MetricExtension}s via SPI. + * + * @author Carpenter Lee + * @since 1.6.1 + */ +public class MetricExtensionProvider { + private static List metricExtensions = new ArrayList<>(); + + static { + resolveInstance(); + } + + private static void resolveInstance() { + List extensions = SpiLoader.loadInstanceList(MetricExtension.class); + + if (extensions == null) { + RecordLog.warn("[MetricExtensionProvider] WARN: No existing MetricExtension found"); + } else { + metricExtensions.addAll(extensions); + RecordLog.info("[MetricExtensionProvider] MetricExtension resolved, size=" + extensions.size()); + } + } + + /** + * Get all metric extensions. DO NOT MODIFY the returned list, use {@link #addMetricExtension(MetricExtension)}. + * + * @return all metric extensions. + */ + public static List getMetricExtensions() { + return metricExtensions; + } + + /** + * Add metric extension. + *

+ * Not that this method is NOT thread safe. + *

+ * + * @param metricExtension the metric extension to add. + */ + public static void addMetricExtension(MetricExtension metricExtension) { + metricExtensions.add(metricExtension); + } + +} diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/callback/MetricEntryCallback.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/callback/MetricEntryCallback.java new file mode 100644 index 0000000000..e5409970ee --- /dev/null +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/callback/MetricEntryCallback.java @@ -0,0 +1,34 @@ +package com.alibaba.csp.sentinel.metric.extension.callback; + +import com.alibaba.csp.sentinel.context.Context; +import com.alibaba.csp.sentinel.metric.extension.MetricExtensionProvider; +import com.alibaba.csp.sentinel.metric.extension.MetricExtension; +import com.alibaba.csp.sentinel.node.DefaultNode; +import com.alibaba.csp.sentinel.slotchain.ProcessorSlotEntryCallback; +import com.alibaba.csp.sentinel.slotchain.ResourceWrapper; +import com.alibaba.csp.sentinel.slots.block.BlockException; + +/** + * Metric extension entry callback. + * + * @author Carpenter Lee + * @since 1.6.1 + */ +public class MetricEntryCallback implements ProcessorSlotEntryCallback { + @Override + public void onPass(Context context, ResourceWrapper resourceWrapper, DefaultNode param, + int count, Object... args) throws Exception { + for (MetricExtension m : MetricExtensionProvider.getMetricExtensions()) { + m.increaseThreadNum(resourceWrapper.getName(), args); + m.addPass(resourceWrapper.getName(), count, args); + } + } + + @Override + public void onBlocked(BlockException ex, Context context, ResourceWrapper resourceWrapper, + DefaultNode param, int count, Object... args) { + for (MetricExtension m : MetricExtensionProvider.getMetricExtensions()) { + m.addBlock(resourceWrapper.getName(), count, context.getOrigin(), ex, args); + } + } +} diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/callback/MetricExitCallback.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/callback/MetricExitCallback.java new file mode 100644 index 0000000000..334a8f3a1b --- /dev/null +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/metric/extension/callback/MetricExitCallback.java @@ -0,0 +1,28 @@ +package com.alibaba.csp.sentinel.metric.extension.callback; + +import com.alibaba.csp.sentinel.context.Context; +import com.alibaba.csp.sentinel.metric.extension.MetricExtensionProvider; +import com.alibaba.csp.sentinel.metric.extension.MetricExtension; +import com.alibaba.csp.sentinel.slotchain.ProcessorSlotExitCallback; +import com.alibaba.csp.sentinel.slotchain.ResourceWrapper; +import com.alibaba.csp.sentinel.util.TimeUtil; + +/** + * Metric extension exit callback. + * + * @author Carpenter Lee + * @since 1.6.1 + */ +public class MetricExitCallback implements ProcessorSlotExitCallback { + @Override + public void onExit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) { + for (MetricExtension m : MetricExtensionProvider.getMetricExtensions()) { + if (context.getCurEntry().getError() == null) { + long realRt = TimeUtil.currentTimeMillis() - context.getCurEntry().getCreateTime(); + m.addRt(resourceWrapper.getName(), realRt, args); + m.addSuccess(resourceWrapper.getName(), count, args); + m.decreaseThreadNum(resourceWrapper.getName(), args); + } + } + } +} diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/StatisticNode.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/StatisticNode.java index 44d2dc76ce..80a8443cad 100755 --- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/StatisticNode.java +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/node/StatisticNode.java @@ -15,15 +15,15 @@ */ package com.alibaba.csp.sentinel.node; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicInteger; - -import com.alibaba.csp.sentinel.util.TimeUtil; import com.alibaba.csp.sentinel.node.metric.MetricNode; +import com.alibaba.csp.sentinel.slots.statistic.base.LongAdder; import com.alibaba.csp.sentinel.slots.statistic.metric.ArrayMetric; import com.alibaba.csp.sentinel.slots.statistic.metric.Metric; +import com.alibaba.csp.sentinel.util.TimeUtil; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; /** *

The statistic node keep three kinds of real-time statistics metrics:

@@ -104,7 +104,7 @@ public class StatisticNode implements Node { /** * The counter for thread count. */ - private AtomicInteger curThreadNum = new AtomicInteger(0); + private LongAdder curThreadNum = new LongAdder(); /** * The last timestamp when metrics were fetched. @@ -147,8 +147,7 @@ public void reset() { @Override public long totalRequest() { - long totalRequest = rollingCounterInMinute.pass() + rollingCounterInMinute.block(); - return totalRequest; + return rollingCounterInMinute.pass() + rollingCounterInMinute.block(); } @Override @@ -233,7 +232,7 @@ public double minRt() { @Override public int curThreadNum() { - return curThreadNum.get(); + return (int)curThreadNum.sum(); } @Override @@ -265,12 +264,12 @@ public void increaseExceptionQps(int count) { @Override public void increaseThreadNum() { - curThreadNum.incrementAndGet(); + curThreadNum.increment(); } @Override public void decreaseThreadNum() { - curThreadNum.decrementAndGet(); + curThreadNum.decrement(); } @Override diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/FlowRuleChecker.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/FlowRuleChecker.java index 4799bfb363..26d34d39b4 100644 --- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/FlowRuleChecker.java +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/FlowRuleChecker.java @@ -15,6 +15,8 @@ */ package com.alibaba.csp.sentinel.slots.block.flow; +import java.util.Collection; + import com.alibaba.csp.sentinel.cluster.ClusterStateManager; import com.alibaba.csp.sentinel.cluster.server.EmbeddedClusterTokenServerProvider; import com.alibaba.csp.sentinel.cluster.client.TokenClientProvider; @@ -25,23 +27,42 @@ import com.alibaba.csp.sentinel.log.RecordLog; import com.alibaba.csp.sentinel.node.DefaultNode; import com.alibaba.csp.sentinel.node.Node; +import com.alibaba.csp.sentinel.slotchain.ResourceWrapper; +import com.alibaba.csp.sentinel.slots.block.BlockException; import com.alibaba.csp.sentinel.slots.block.RuleConstant; import com.alibaba.csp.sentinel.slots.clusterbuilder.ClusterBuilderSlot; import com.alibaba.csp.sentinel.util.StringUtil; +import com.alibaba.csp.sentinel.util.function.Function; /** * Rule checker for flow control rules. * * @author Eric Zhao */ -final class FlowRuleChecker { +public class FlowRuleChecker { - static boolean passCheck(/*@NonNull*/ FlowRule rule, Context context, DefaultNode node, int acquireCount) { - return passCheck(rule, context, node, acquireCount, false); + public void checkFlow(Function> ruleProvider, ResourceWrapper resource, + Context context, DefaultNode node, int count, boolean prioritized) throws BlockException { + if (ruleProvider == null || resource == null) { + return; + } + Collection rules = ruleProvider.apply(resource.getName()); + if (rules != null) { + for (FlowRule rule : rules) { + if (!canPassCheck(rule, context, node, count, prioritized)) { + throw new FlowException(rule.getLimitApp(), rule); + } + } + } } - static boolean passCheck(/*@NonNull*/ FlowRule rule, Context context, DefaultNode node, int acquireCount, - boolean prioritized) { + public boolean canPassCheck(/*@NonNull*/ FlowRule rule, Context context, DefaultNode node, + int acquireCount) { + return canPassCheck(rule, context, node, acquireCount, false); + } + + public boolean canPassCheck(/*@NonNull*/ FlowRule rule, Context context, DefaultNode node, int acquireCount, + boolean prioritized) { String limitApp = rule.getLimitApp(); if (limitApp == null) { return true; @@ -162,8 +183,9 @@ private static TokenService pickClusterService() { return null; } - private static boolean applyTokenResult(/*@NonNull*/ TokenResult result, FlowRule rule, Context context, DefaultNode node, - int acquireCount, boolean prioritized) { + private static boolean applyTokenResult(/*@NonNull*/ TokenResult result, FlowRule rule, Context context, + DefaultNode node, + int acquireCount, boolean prioritized) { switch (result.getStatus()) { case TokenResultStatus.OK: return true; @@ -185,6 +207,4 @@ private static boolean applyTokenResult(/*@NonNull*/ TokenResult result, FlowRul return false; } } - - private FlowRuleChecker() {} } \ No newline at end of file diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/FlowSlot.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/FlowSlot.java index 179ea8caa3..d05d51c8e7 100755 --- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/FlowSlot.java +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/FlowSlot.java @@ -15,15 +15,18 @@ */ package com.alibaba.csp.sentinel.slots.block.flow; -import java.util.List; -import java.util.Map; - 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.spi.SpiOrder; +import com.alibaba.csp.sentinel.util.AssertUtil; +import com.alibaba.csp.sentinel.util.function.Function; + +import java.util.Collection; +import java.util.List; +import java.util.Map; /** *

@@ -137,6 +140,23 @@ @SpiOrder(-2000) public class FlowSlot extends AbstractLinkedProcessorSlot { + private final FlowRuleChecker checker; + + public FlowSlot() { + this(new FlowRuleChecker()); + } + + /** + * Package-private for test. + * + * @param checker flow rule checker + * @since 1.6.1 + */ + FlowSlot(FlowRuleChecker checker) { + AssertUtil.notNull(checker, "flow checker should not be null"); + this.checker = checker; + } + @Override public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, boolean prioritized, Object... args) throws Throwable { @@ -145,26 +165,22 @@ public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode fireEntry(context, resourceWrapper, node, count, prioritized, args); } - void checkFlow(ResourceWrapper resource, Context context, DefaultNode node, int count, boolean prioritized) throws BlockException { - // Flow rule map cannot be null. - Map> flowRules = FlowRuleManager.getFlowRuleMap(); - - List rules = flowRules.get(resource.getName()); - if (rules != null) { - for (FlowRule rule : rules) { - if (!canPassCheck(rule, context, node, count, prioritized)) { - throw new FlowException(rule.getLimitApp(), rule); - } - } - } - } - - boolean canPassCheck(FlowRule rule, Context context, DefaultNode node, int count, boolean prioritized) { - return FlowRuleChecker.passCheck(rule, context, node, count, prioritized); + void checkFlow(ResourceWrapper resource, Context context, DefaultNode node, int count, boolean prioritized) + throws BlockException { + checker.checkFlow(ruleProvider, resource, context, node, count, prioritized); } @Override public void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) { fireExit(context, resourceWrapper, count, args); } + + private final Function> ruleProvider = new Function>() { + @Override + public Collection apply(String resource) { + // Flow rule map should not be null. + Map> flowRules = FlowRuleManager.getFlowRuleMap(); + return flowRules.get(resource); + } + }; } diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/nodeselector/NodeSelectorSlot.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/nodeselector/NodeSelectorSlot.java index a2dd085e67..fb26c6680b 100755 --- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/nodeselector/NodeSelectorSlot.java +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/nodeselector/NodeSelectorSlot.java @@ -15,9 +15,6 @@ */ package com.alibaba.csp.sentinel.slots.nodeselector; -import java.util.HashMap; -import java.util.Map; - import com.alibaba.csp.sentinel.context.Context; import com.alibaba.csp.sentinel.context.ContextUtil; import com.alibaba.csp.sentinel.node.ClusterNode; @@ -27,6 +24,9 @@ import com.alibaba.csp.sentinel.slotchain.ResourceWrapper; import com.alibaba.csp.sentinel.spi.SpiOrder; +import java.util.HashMap; +import java.util.Map; + /** *

* This class will try to build the calling traces via @@ -162,9 +162,10 @@ public void entry(Context context, ResourceWrapper resourceWrapper, Object obj, cacheMap.putAll(map); cacheMap.put(context.getName(), node); map = cacheMap; + // Build invocation tree + ((DefaultNode) context.getLastNode()).addChild(node); } - // Build invocation tree - ((DefaultNode)context.getLastNode()).addChild(node); + } } diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/base/LeapArray.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/base/LeapArray.java index 58a0dce0e3..2bba544be1 100755 --- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/base/LeapArray.java +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/base/LeapArray.java @@ -209,8 +209,7 @@ public WindowWrap getPreviousWindow(long timeMillis) { if (timeMillis < 0) { return null; } - long timeId = (timeMillis - windowLengthInMs) / windowLengthInMs; - int idx = (int)(timeId % array.length()); + int idx = calculateTimeIdx(timeMillis - windowLengthInMs); timeMillis = timeMillis - windowLengthInMs; WindowWrap wrap = array.get(idx); diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/metric/ArrayMetric.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/metric/ArrayMetric.java index f4c2a073d2..8647d99461 100755 --- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/metric/ArrayMetric.java +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/statistic/metric/ArrayMetric.java @@ -234,8 +234,8 @@ public void debug() { @Override public long previousWindowBlock() { - WindowWrap wrap = data.currentWindow(); - wrap = data.getPreviousWindow(); + data.currentWindow(); + WindowWrap wrap = data.getPreviousWindow(); if (wrap == null) { return 0; } @@ -244,8 +244,8 @@ public long previousWindowBlock() { @Override public long previousWindowPass() { - WindowWrap wrap = data.currentWindow(); - wrap = data.getPreviousWindow(); + data.currentWindow(); + WindowWrap wrap = data.getPreviousWindow(); if (wrap == null) { return 0; } diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/system/SystemRuleManager.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/system/SystemRuleManager.java index 99b93a931d..7a6091fe57 100755 --- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/system/SystemRuleManager.java +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/system/SystemRuleManager.java @@ -222,6 +222,7 @@ protected void restoreSetting() { qps = Double.MAX_VALUE; highestSystemLoadIsSet = false; + highestCpuUsageIsSet = false; maxRtIsSet = false; maxThreadIsSet = false; qpsIsSet = false; @@ -240,6 +241,10 @@ public static double getHighestSystemLoad() { public static void setHighestSystemLoad(double highestSystemLoad) { SystemRuleManager.highestSystemLoad = highestSystemLoad; } + + public static double getCpuUsageThreshold() { + return highestCpuUsage; + } public static void loadSystemConf(SystemRule rule) { boolean checkStatus = false; @@ -342,4 +347,4 @@ public static double getCurrentSystemAvgLoad() { public static double getCurrentCpuUsage() { return statusListener.getCpuUsage(); } -} +} \ No newline at end of file diff --git a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/system/SystemStatusListener.java b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/system/SystemStatusListener.java index 8cf45a37c6..e02f0c858b 100755 --- a/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/system/SystemStatusListener.java +++ b/sentinel-core/src/main/java/com/alibaba/csp/sentinel/slots/system/SystemStatusListener.java @@ -33,8 +33,6 @@ public class SystemStatusListener implements Runnable { volatile String reason = StringUtil.EMPTY; - static final int processor = ManagementFactory.getOperatingSystemMXBean().getAvailableProcessors(); - public double getSystemAverageLoad() { return currentLoad; } @@ -46,10 +44,6 @@ public double getCpuUsage() { @Override public void run() { try { - if (!SystemRuleManager.getCheckSystemStatus()) { - return; - } - OperatingSystemMXBean osBean = ManagementFactory.getPlatformMXBean(OperatingSystemMXBean.class); currentLoad = osBean.getSystemLoadAverage(); /** diff --git a/sentinel-core/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.init.InitFunc b/sentinel-core/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.init.InitFunc new file mode 100644 index 0000000000..68a6ac77f7 --- /dev/null +++ b/sentinel-core/src/main/resources/META-INF/services/com.alibaba.csp.sentinel.init.InitFunc @@ -0,0 +1 @@ +com.alibaba.csp.sentinel.metric.extension.MetricCallbackInit \ No newline at end of file diff --git a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/TracerTest.java b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/TracerTest.java new file mode 100644 index 0000000000..4b2352e1b8 --- /dev/null +++ b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/TracerTest.java @@ -0,0 +1,70 @@ +package com.alibaba.csp.sentinel; + +import org.junit.Assert; +import org.junit.Test; + +/** + * @author Carpenter Lee + */ +public class TracerTest extends Tracer { + + @Test + public void setExceptionsToTrace() { + Tracer.ignoreClasses = null; + Tracer.traceClasses = null; + Tracer.setExceptionsToTrace(TraceException.class, TraceException2.class); + Assert.assertTrue(Tracer.shouldTrace(new TraceException2())); + Assert.assertTrue(Tracer.shouldTrace(new TraceExceptionSub())); + Assert.assertFalse(Tracer.shouldTrace(new Exception())); + } + + @Test + public void setExceptionsToIgnore() { + Tracer.ignoreClasses = null; + Tracer.traceClasses = null; + Tracer.setExceptionsToIgnore(IgnoreException.class, IgnoreException2.class); + Assert.assertFalse(Tracer.shouldTrace(new IgnoreException())); + Assert.assertFalse(Tracer.shouldTrace(new IgnoreExceptionSub())); + Assert.assertTrue(Tracer.shouldTrace(new Exception())); + } + + @Test + public void testBoth() { + Tracer.ignoreClasses = null; + Tracer.traceClasses = null; + Tracer.setExceptionsToTrace(TraceException.class, TraceException2.class, BothException.class); + Tracer.setExceptionsToIgnore(IgnoreException.class, IgnoreException2.class, BothException.class); + Assert.assertFalse(Tracer.shouldTrace(new IgnoreException())); + Assert.assertFalse(Tracer.shouldTrace(new BothException())); + Assert.assertTrue(Tracer.shouldTrace(new TraceException())); + } + + @Test(expected = IllegalArgumentException.class) + public void testNull() { + Tracer.setExceptionsToTrace(null); + } + + @Test(expected = IllegalArgumentException.class) + public void testNull1() { + Tracer.setExceptionsToTrace(TraceException.class, null); + } + + @Test(expected = IllegalArgumentException.class) + public void testNull2() { + Tracer.setExceptionsToIgnore(IgnoreException.class, null); + } + + private class TraceException extends Exception {} + + private class TraceException2 extends Exception {} + + private class TraceExceptionSub extends TraceException {} + + private class IgnoreException extends Exception {} + + private class IgnoreException2 extends Exception {} + + private class IgnoreExceptionSub extends IgnoreException {} + + private class BothException extends Exception {} +} \ No newline at end of file diff --git a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/metric/extension/callback/FakeMetricExtension.java b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/metric/extension/callback/FakeMetricExtension.java new file mode 100644 index 0000000000..f9ac290830 --- /dev/null +++ b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/metric/extension/callback/FakeMetricExtension.java @@ -0,0 +1,51 @@ +package com.alibaba.csp.sentinel.metric.extension.callback; + +import com.alibaba.csp.sentinel.metric.extension.MetricExtension; +import com.alibaba.csp.sentinel.slots.block.BlockException; + +/** + * @author Carpenter Lee + */ +class FakeMetricExtension implements MetricExtension { + long pass = 0; + long block = 0; + long success = 0; + long exception = 0; + long rt = 0; + long thread = 0; + + @Override + public void addPass(String resource, int n, Object... args) { + pass += n; + } + + @Override + public void addBlock(String resource, int n, String origin, BlockException ex, Object... args) { + block += n; + } + + @Override + public void addSuccess(String resource, int n, Object... args) { + success += n; + } + + @Override + public void addException(String resource, int n, Throwable t) { + exception += n; + } + + @Override + public void addRt(String resource, long rt, Object... args) { + this.rt += rt; + } + + @Override + public void increaseThreadNum(String resource, Object... args) { + thread++; + } + + @Override + public void decreaseThreadNum(String resource, Object... args) { + thread--; + } +} diff --git a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/metric/extension/callback/MetricEntryCallbackTest.java b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/metric/extension/callback/MetricEntryCallbackTest.java new file mode 100644 index 0000000000..f1972c8e9e --- /dev/null +++ b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/metric/extension/callback/MetricEntryCallbackTest.java @@ -0,0 +1,48 @@ +package com.alibaba.csp.sentinel.metric.extension.callback; + +import com.alibaba.csp.sentinel.EntryType; +import com.alibaba.csp.sentinel.context.Context; +import com.alibaba.csp.sentinel.metric.extension.MetricExtensionProvider; +import com.alibaba.csp.sentinel.slotchain.StringResourceWrapper; +import com.alibaba.csp.sentinel.slots.block.flow.FlowException; + +import org.junit.Assert; +import org.junit.Test; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * @author Carpenter Lee + */ +public class MetricEntryCallbackTest { + + @Test + public void onPass() throws Exception { + FakeMetricExtension extension = new FakeMetricExtension(); + MetricExtensionProvider.addMetricExtension(extension); + + MetricEntryCallback entryCallback = new MetricEntryCallback(); + StringResourceWrapper resourceWrapper = new StringResourceWrapper("resource", EntryType.OUT); + int count = 2; + Object[] args = {"args1", "args2"}; + entryCallback.onPass(null, resourceWrapper, null, count, args); + Assert.assertEquals(extension.pass, count); + Assert.assertEquals(extension.thread, 1); + } + + @Test + public void onBlocked() throws Exception { + FakeMetricExtension extension = new FakeMetricExtension(); + MetricExtensionProvider.addMetricExtension(extension); + + MetricEntryCallback entryCallback = new MetricEntryCallback(); + StringResourceWrapper resourceWrapper = new StringResourceWrapper("resource", EntryType.OUT); + Context context = mock(Context.class); + when(context.getOrigin()).thenReturn("origin1"); + int count = 2; + Object[] args = {"args1", "args2"}; + entryCallback.onBlocked(new FlowException("xx"), context, resourceWrapper, null, count, args); + Assert.assertEquals(extension.block, count); + } +} \ No newline at end of file diff --git a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/metric/extension/callback/MetricExitCallbackTest.java b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/metric/extension/callback/MetricExitCallbackTest.java new file mode 100644 index 0000000000..8bf2b89520 --- /dev/null +++ b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/metric/extension/callback/MetricExitCallbackTest.java @@ -0,0 +1,43 @@ +package com.alibaba.csp.sentinel.metric.extension.callback; + +import com.alibaba.csp.sentinel.Entry; +import com.alibaba.csp.sentinel.EntryType; +import com.alibaba.csp.sentinel.context.Context; +import com.alibaba.csp.sentinel.metric.extension.MetricExtensionProvider; +import com.alibaba.csp.sentinel.slotchain.StringResourceWrapper; +import com.alibaba.csp.sentinel.util.TimeUtil; + +import org.junit.Assert; +import org.junit.Test; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * @author Carpenter Lee + */ +public class MetricExitCallbackTest { + + @Test + public void onExit() { + FakeMetricExtension extension = new FakeMetricExtension(); + MetricExtensionProvider.addMetricExtension(extension); + + MetricExitCallback exitCallback = new MetricExitCallback(); + StringResourceWrapper resourceWrapper = new StringResourceWrapper("resource", EntryType.OUT); + int count = 2; + Object[] args = {"args1", "args2"}; + extension.rt = 20; + extension.success = 6; + extension.thread = 10; + Context context = mock(Context.class); + Entry entry = mock(Entry.class); + when(entry.getError()).thenReturn(null); + when(entry.getCreateTime()).thenReturn(TimeUtil.currentTimeMillis() - 100); + when(context.getCurEntry()).thenReturn(entry); + exitCallback.onExit(context, resourceWrapper, count, args); + Assert.assertEquals(120, extension.rt, 10); + Assert.assertEquals(extension.success, 6 + count); + Assert.assertEquals(extension.thread, 10 - 1); + } +} \ No newline at end of file diff --git a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/node/StatisticNodeTest.java b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/node/StatisticNodeTest.java index 44674dd14c..c34b8bf919 100644 --- a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/node/StatisticNodeTest.java +++ b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/node/StatisticNodeTest.java @@ -17,17 +17,23 @@ import com.alibaba.csp.sentinel.Constants; import com.alibaba.csp.sentinel.util.TimeUtil; +import org.junit.Assert; import org.junit.Test; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; +import java.util.Map; import java.util.Random; import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -198,4 +204,125 @@ private static void logNode(StatisticNode node) { private static void log(Object obj) { System.out.println(LOG_PREFIX + obj); } + + + /** + * com.alibaba.csp.sentinel.node.StatisticNode#curThreadNum using LongAdder replace the AtomicInteger. + * now test the LongAdder is fast than AtomicInteger + * and get the right statistic or not + */ + @Test + public void testStatisticLongAdder() throws InterruptedException { + AtomicInteger atomicInteger = new AtomicInteger(0); + StatisticNode statisticNode = new StatisticNode(); + ExecutorService bizEs1 = new ThreadPoolExecutor(THREAD_COUNT, THREAD_COUNT, + 0L, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue()); + ExecutorService bizEs2 = new ThreadPoolExecutor(THREAD_COUNT, THREAD_COUNT, + 0L, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue()); + int taskCount = 100; + for (int i = 0; i < taskCount; i++) { + int op = i % 2; + bizEs2.submit(new StatisticAtomicIntegerTask(atomicInteger, op, i)); + bizEs1.submit(new StatisticLongAdderTask(statisticNode, op, i)); + } + Thread.sleep(5000); + + log("LongAdder totalCost : " + StatisticLongAdderTask.totalCost() + "ms"); + log("AtomicInteger totalCost : " + StatisticAtomicIntegerTask.totalCost() + "ms"); + Assert.assertEquals(statisticNode.curThreadNum(), atomicInteger.get()); + + + } + + private static class StatisticLongAdderTask implements Runnable { + + + private StatisticNode statisticNode; + /** + * 0 addition + * 1 subtraction + */ + private int op; + + private int taskId; + + private static Map taskCostMap = new ConcurrentHashMap<>(16); + + + public StatisticLongAdderTask(StatisticNode statisticNode, int op, int taskId) { + this.statisticNode = statisticNode; + this.op = op; + this.taskId = taskId; + } + + @Override + public void run() { + long startTime = System.currentTimeMillis(); + int calCount = 100000; + for (int i = 0; i < calCount; i++) { + if (op == 0) { + statisticNode.increaseThreadNum(); + } else if (op == 1) { + statisticNode.decreaseThreadNum(); + } + } + long cost = System.currentTimeMillis() - startTime; + taskCostMap.put(taskId, cost); + } + + public static long totalCost() { + long totalCost = 0; + for (long cost : taskCostMap.values()) { + totalCost += cost; + } + return totalCost; + } + } + + private static class StatisticAtomicIntegerTask implements Runnable { + + AtomicInteger atomicInteger; + /** + * 0 addition + * 1 subtraction + */ + private int op; + + private int taskId; + + private static Map taskCostMap = new ConcurrentHashMap<>(16); + + public StatisticAtomicIntegerTask(AtomicInteger atomicInteger, int op, int taskId) { + this.atomicInteger = atomicInteger; + this.op = op; + this.taskId = taskId; + } + + @Override + public void run() { + long startTime = System.currentTimeMillis(); + int calCount = 100000; + for (int i = 0; i < calCount; i++) { + if (op == 0) { + atomicInteger.incrementAndGet(); + } else if (op == 1) { + atomicInteger.decrementAndGet(); + } + } + long cost = System.currentTimeMillis() - startTime; + taskCostMap.put(taskId, cost); + } + + public static long totalCost() { + long totalCost = 0; + for (long cost : taskCostMap.values()) { + totalCost += cost; + } + return totalCost; + } + } + + } diff --git a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/FlowRuleCheckerTest.java b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/FlowRuleCheckerTest.java index 10c460f383..bdd8072d47 100644 --- a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/FlowRuleCheckerTest.java +++ b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/FlowRuleCheckerTest.java @@ -149,7 +149,8 @@ public void testSelectReferenceNodeForContextEntrance() { public void testPassCheckNullLimitApp() { FlowRule rule = new FlowRule("abc").setCount(1); rule.setLimitApp(null); - assertTrue(FlowRuleChecker.passCheck(rule, null, null, 1)); + FlowRuleChecker checker = new FlowRuleChecker(); + assertTrue(checker.canPassCheck(rule, null, null, 1)); } @Test @@ -161,7 +162,8 @@ public void testPassCheckSelectEmptyNodeSuccess() { Context context = mock(Context.class); when(context.getOrigin()).thenReturn("def"); - assertTrue(FlowRuleChecker.passCheck(rule, context, node, 1)); + FlowRuleChecker checker = new FlowRuleChecker(); + assertTrue(checker.canPassCheck(rule, context, node, 1)); } @Before diff --git a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/FlowSlotTest.java b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/FlowSlotTest.java index ee5de0a8b8..66026e2bb7 100644 --- a/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/FlowSlotTest.java +++ b/sentinel-core/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/FlowSlotTest.java @@ -23,6 +23,7 @@ import com.alibaba.csp.sentinel.node.DefaultNode; import com.alibaba.csp.sentinel.slotchain.ResourceWrapper; import com.alibaba.csp.sentinel.slotchain.StringResourceWrapper; +import com.alibaba.csp.sentinel.util.function.Function; import org.junit.After; import org.junit.Before; @@ -49,11 +50,13 @@ public void tearDown() { } @Test + @SuppressWarnings("unchecked") public void testCheckFlowPass() throws Exception { - FlowSlot flowSlot = mock(FlowSlot.class); + FlowRuleChecker checker = mock(FlowRuleChecker.class); + FlowSlot flowSlot = new FlowSlot(checker); Context context = mock(Context.class); DefaultNode node = mock(DefaultNode.class); - doCallRealMethod().when(flowSlot).checkFlow(any(ResourceWrapper.class), any(Context.class), + doCallRealMethod().when(checker).checkFlow(any(Function.class), any(ResourceWrapper.class), any(Context.class), any(DefaultNode.class), anyInt(), anyBoolean()); String resA = "resAK"; @@ -63,9 +66,9 @@ public void testCheckFlowPass() throws Exception { // Here we only load rules for resA. FlowRuleManager.loadRules(Collections.singletonList(rule1)); - when(flowSlot.canPassCheck(eq(rule1), any(Context.class), any(DefaultNode.class), anyInt(), anyBoolean())) + when(checker.canPassCheck(eq(rule1), any(Context.class), any(DefaultNode.class), anyInt(), anyBoolean())) .thenReturn(true); - when(flowSlot.canPassCheck(eq(rule2), any(Context.class), any(DefaultNode.class), anyInt(), anyBoolean())) + when(checker.canPassCheck(eq(rule2), any(Context.class), any(DefaultNode.class), anyInt(), anyBoolean())) .thenReturn(false); flowSlot.checkFlow(new StringResourceWrapper(resA, EntryType.IN), context, node, 1, false); @@ -73,20 +76,22 @@ public void testCheckFlowPass() throws Exception { } @Test(expected = FlowException.class) + @SuppressWarnings("unchecked") public void testCheckFlowBlock() throws Exception { - FlowSlot flowSlot = mock(FlowSlot.class); + FlowRuleChecker checker = mock(FlowRuleChecker.class); + FlowSlot flowSlot = new FlowSlot(checker); Context context = mock(Context.class); DefaultNode node = mock(DefaultNode.class); - doCallRealMethod().when(flowSlot).checkFlow(any(ResourceWrapper.class), any(Context.class), + doCallRealMethod().when(checker).checkFlow(any(Function.class), any(ResourceWrapper.class), any(Context.class), any(DefaultNode.class), anyInt(), anyBoolean()); String resA = "resAK"; FlowRule rule = new FlowRule(resA).setCount(10); FlowRuleManager.loadRules(Collections.singletonList(rule)); - when(flowSlot.canPassCheck(any(FlowRule.class), any(Context.class), any(DefaultNode.class), anyInt(), anyBoolean())) + when(checker.canPassCheck(any(FlowRule.class), any(Context.class), any(DefaultNode.class), anyInt(), anyBoolean())) .thenReturn(false); flowSlot.checkFlow(new StringResourceWrapper(resA, EntryType.IN), context, node, 1, false); } -} \ No newline at end of file +} diff --git a/sentinel-dashboard/pom.xml b/sentinel-dashboard/pom.xml index 4f7a9bc8dc..b8bafb1090 100755 --- a/sentinel-dashboard/pom.xml +++ b/sentinel-dashboard/pom.xml @@ -6,7 +6,7 @@ com.alibaba.csp sentinel-parent - 1.6.0-SNAPSHOT + 1.7.0-SNAPSHOT sentinel-dashboard @@ -16,6 +16,7 @@ 1.8 1.8 2.0.5.RELEASE + 4.0.1 @@ -106,6 +107,14 @@ test + + + org.apache.curator + curator-recipes + ${curator.version} + test + + junit junit @@ -130,6 +139,7 @@ org.springframework.boot spring-boot-maven-plugin + ${spring.boot.version} true com.alibaba.csp.sentinel.dashboard.DashboardApplication diff --git a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/SystemController.java b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/SystemController.java index 815efcafec..8e99718bc3 100755 --- a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/SystemController.java +++ b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/controller/SystemController.java @@ -78,7 +78,7 @@ Result> queryMachineRules(HttpServletRequest request, Str } } - private int countNotNullAndNotNegtive(Number... values) { + private int countNotNullAndNotNegative(Number... values) { int notNullCount = 0; for (int i = 0; i < values.length; i++) { if (values[i] != null && values[i].doubleValue() >= 0) { @@ -103,7 +103,7 @@ Result add(HttpServletRequest request, if (port == null) { return Result.ofFail(-1, "port can't be null"); } - int notNullCount = countNotNullAndNotNegtive(avgLoad, avgRt, maxThread, qps); + int notNullCount = countNotNullAndNotNegative(avgLoad, avgRt, maxThread, qps); if (notNullCount != 1) { return Result.ofFail(-1, "only one of [avgLoad, avgRt, maxThread, qps] " + "value must be set >= 0, but " + notNullCount + " values get"); diff --git a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/metric/MetricFetcher.java b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/metric/MetricFetcher.java index ffcfc62131..2c032249a8 100755 --- a/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/metric/MetricFetcher.java +++ b/sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/metric/MetricFetcher.java @@ -19,6 +19,7 @@ import java.net.SocketTimeoutException; import java.nio.charset.Charset; import java.util.Date; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -34,6 +35,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; +import com.alibaba.csp.sentinel.Constants; import com.alibaba.csp.sentinel.concurrent.NamedThreadFactory; import com.alibaba.csp.sentinel.config.SentinelConfig; import com.alibaba.csp.sentinel.dashboard.datasource.entity.MetricEntity; @@ -322,7 +324,10 @@ private void handleBody(String[] lines, MachineInfo machine, Map RES_EXCLUSION_SET = new HashSet() {{ + add(Constants.TOTAL_IN_RESOURCE_NAME); + add(Constants.SYSTEM_LOAD_RESOURCE_NAME); + add(Constants.CPU_USAGE_RESOURCE_NAME); + }}; + } diff --git a/sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/zookeeper/FlowRuleZookeeperProvider.java b/sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/zookeeper/FlowRuleZookeeperProvider.java new file mode 100644 index 0000000000..727b541017 --- /dev/null +++ b/sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/zookeeper/FlowRuleZookeeperProvider.java @@ -0,0 +1,47 @@ +/* + * 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.dashboard.rule.zookeeper; + +import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity; +import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider; +import com.alibaba.csp.sentinel.datasource.Converter; +import org.apache.curator.framework.CuratorFramework; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +@Component("flowRuleZookeeperProvider") +public class FlowRuleZookeeperProvider implements DynamicRuleProvider> { + + @Autowired + private CuratorFramework zkClient; + @Autowired + private Converter> converter; + + @Override + public List getRules(String appName) throws Exception { + String zkPath = ZookeeperConfigUtil.getPath(appName); + byte[] bytes = zkClient.getData().forPath(zkPath); + if (null == bytes || bytes.length == 0) { + return new ArrayList<>(); + } + String s = new String(bytes); + + return converter.convert(s); + } +} \ No newline at end of file diff --git a/sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/zookeeper/FlowRuleZookeeperPublisher.java b/sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/zookeeper/FlowRuleZookeeperPublisher.java new file mode 100644 index 0000000000..7d3f72bd07 --- /dev/null +++ b/sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/zookeeper/FlowRuleZookeeperPublisher.java @@ -0,0 +1,50 @@ +/* + * 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.dashboard.rule.zookeeper; + +import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity; +import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher; +import com.alibaba.csp.sentinel.datasource.Converter; +import com.alibaba.csp.sentinel.util.AssertUtil; +import org.apache.curator.framework.CuratorFramework; +import org.apache.zookeeper.CreateMode; +import org.apache.zookeeper.data.Stat; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.util.CollectionUtils; + +import java.util.List; + +@Component("flowRuleZookeeperPublisher") +public class FlowRuleZookeeperPublisher implements DynamicRulePublisher> { + @Autowired + private CuratorFramework zkClient; + @Autowired + private Converter, String> converter; + + @Override + public void publish(String app, List rules) throws Exception { + AssertUtil.notEmpty(app, "app name cannot be empty"); + + String path = ZookeeperConfigUtil.getPath(app); + Stat stat = zkClient.checkExists().forPath(path); + if (stat == null) { + zkClient.create().creatingParentContainersIfNeeded().withMode(CreateMode.PERSISTENT).forPath(path, null); + } + byte[] data = CollectionUtils.isEmpty(rules) ? "[]".getBytes() : converter.convert(rules).getBytes(); + zkClient.setData().forPath(path, data); + } +} \ No newline at end of file diff --git a/sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/zookeeper/ZookeeperConfig.java b/sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/zookeeper/ZookeeperConfig.java new file mode 100644 index 0000000000..601ff2f947 --- /dev/null +++ b/sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/zookeeper/ZookeeperConfig.java @@ -0,0 +1,51 @@ +/* + * 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.dashboard.rule.zookeeper; + +import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity; +import com.alibaba.csp.sentinel.datasource.Converter; +import com.alibaba.fastjson.JSON; +import org.apache.curator.framework.CuratorFramework; +import org.apache.curator.framework.CuratorFrameworkFactory; +import org.apache.curator.retry.ExponentialBackoffRetry; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.List; + +@Configuration +public class ZookeeperConfig { + + @Bean + public Converter, String> flowRuleEntityEncoder() { + return JSON::toJSONString; + } + + @Bean + public Converter> flowRuleEntityDecoder() { + return s -> JSON.parseArray(s, FlowRuleEntity.class); + } + + @Bean + public CuratorFramework zkClient() { + CuratorFramework zkClient = + CuratorFrameworkFactory.newClient("127.0.0.1:2181", + new ExponentialBackoffRetry(ZookeeperConfigUtil.SLEEP_TIME, ZookeeperConfigUtil.RETRY_TIMES)); + zkClient.start(); + + return zkClient; + } +} \ No newline at end of file diff --git a/sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/zookeeper/ZookeeperConfigUtil.java b/sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/zookeeper/ZookeeperConfigUtil.java new file mode 100644 index 0000000000..30605f2051 --- /dev/null +++ b/sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/zookeeper/ZookeeperConfigUtil.java @@ -0,0 +1,41 @@ +/* + * 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.dashboard.rule.zookeeper; + + +import org.apache.commons.lang.StringUtils; + +public class ZookeeperConfigUtil { + public static final String RULE_ROOT_PATH = "/sentinel_rule_config"; + + public static final int RETRY_TIMES = 3; + public static final int SLEEP_TIME = 1000; + + public static String getPath(String appName) { + StringBuilder stringBuilder = new StringBuilder(RULE_ROOT_PATH); + + if (StringUtils.isBlank(appName)) { + return stringBuilder.toString(); + } + if (appName.startsWith("/")) { + stringBuilder.append(appName); + } else { + stringBuilder.append("/") + .append(appName); + } + return stringBuilder.toString(); + } +} \ No newline at end of file diff --git a/sentinel-demo/pom.xml b/sentinel-demo/pom.xml index 5f5ccda74e..38735638dc 100755 --- a/sentinel-demo/pom.xml +++ b/sentinel-demo/pom.xml @@ -6,7 +6,7 @@ com.alibaba.csp sentinel-parent - 1.6.0-SNAPSHOT + 1.7.0-SNAPSHOT sentinel-demo pom @@ -33,6 +33,7 @@ sentinel-demo-spring-webflux sentinel-demo-apache-dubbo sentinel-demo-spring-cloud-gateway + sentinel-demo-zuul-gateway diff --git a/sentinel-demo/sentinel-demo-annotation-spring-aop/pom.xml b/sentinel-demo/sentinel-demo-annotation-spring-aop/pom.xml index 734e3656f8..79c2945a03 100644 --- a/sentinel-demo/sentinel-demo-annotation-spring-aop/pom.xml +++ b/sentinel-demo/sentinel-demo-annotation-spring-aop/pom.xml @@ -5,7 +5,7 @@ sentinel-demo com.alibaba.csp - 1.6.0-SNAPSHOT + 1.7.0-SNAPSHOT 4.0.0 diff --git a/sentinel-demo/sentinel-demo-annotation-spring-aop/src/main/java/com/alibaba/csp/sentinel/demo/annotation/aop/controller/DemoController.java b/sentinel-demo/sentinel-demo-annotation-spring-aop/src/main/java/com/alibaba/csp/sentinel/demo/annotation/aop/controller/DemoController.java index 6288fb8118..d2ba5a8697 100644 --- a/sentinel-demo/sentinel-demo-annotation-spring-aop/src/main/java/com/alibaba/csp/sentinel/demo/annotation/aop/controller/DemoController.java +++ b/sentinel-demo/sentinel-demo-annotation-spring-aop/src/main/java/com/alibaba/csp/sentinel/demo/annotation/aop/controller/DemoController.java @@ -19,6 +19,8 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; /** @@ -31,8 +33,16 @@ public class DemoController { private TestService service; @GetMapping("/foo") - public String foo() throws Exception { + public String apiFoo(@RequestParam(required = false) Long t) throws Exception { + if (t == null) { + t = System.currentTimeMillis(); + } service.test(); - return service.hello(System.currentTimeMillis()); + return service.hello(t); + } + + @GetMapping("/baz/{name}") + public String apiBaz(@PathVariable("name") String name) { + return service.helloAnother(name); } } diff --git a/sentinel-demo/sentinel-demo-annotation-spring-aop/src/main/java/com/alibaba/csp/sentinel/demo/annotation/aop/service/ExceptionUtil.java b/sentinel-demo/sentinel-demo-annotation-spring-aop/src/main/java/com/alibaba/csp/sentinel/demo/annotation/aop/service/ExceptionUtil.java index e589f85b2e..388563c3f9 100644 --- a/sentinel-demo/sentinel-demo-annotation-spring-aop/src/main/java/com/alibaba/csp/sentinel/demo/annotation/aop/service/ExceptionUtil.java +++ b/sentinel-demo/sentinel-demo-annotation-spring-aop/src/main/java/com/alibaba/csp/sentinel/demo/annotation/aop/service/ExceptionUtil.java @@ -23,6 +23,12 @@ public final class ExceptionUtil { public static void handleException(BlockException ex) { + // Handler method that handles BlockException when blocked. + // The method parameter list should match original method, with the last additional + // parameter with type BlockException. The return type should be same as the original method. + // The block handler method should be located in the same class with original method by default. + // If you want to use method in other classes, you can set the blockHandlerClass + // with corresponding Class (Note the method in other classes must be static). System.out.println("Oops: " + ex.getClass().getCanonicalName()); } } diff --git a/sentinel-demo/sentinel-demo-annotation-spring-aop/src/main/java/com/alibaba/csp/sentinel/demo/annotation/aop/service/TestService.java b/sentinel-demo/sentinel-demo-annotation-spring-aop/src/main/java/com/alibaba/csp/sentinel/demo/annotation/aop/service/TestService.java index c8a2d1856e..0395212287 100644 --- a/sentinel-demo/sentinel-demo-annotation-spring-aop/src/main/java/com/alibaba/csp/sentinel/demo/annotation/aop/service/TestService.java +++ b/sentinel-demo/sentinel-demo-annotation-spring-aop/src/main/java/com/alibaba/csp/sentinel/demo/annotation/aop/service/TestService.java @@ -23,4 +23,6 @@ public interface TestService { void test(); String hello(long s); + + String helloAnother(String name); } diff --git a/sentinel-demo/sentinel-demo-annotation-spring-aop/src/main/java/com/alibaba/csp/sentinel/demo/annotation/aop/service/TestServiceImpl.java b/sentinel-demo/sentinel-demo-annotation-spring-aop/src/main/java/com/alibaba/csp/sentinel/demo/annotation/aop/service/TestServiceImpl.java index ab1437e66f..9c793be7dc 100644 --- a/sentinel-demo/sentinel-demo-annotation-spring-aop/src/main/java/com/alibaba/csp/sentinel/demo/annotation/aop/service/TestServiceImpl.java +++ b/sentinel-demo/sentinel-demo-annotation-spring-aop/src/main/java/com/alibaba/csp/sentinel/demo/annotation/aop/service/TestServiceImpl.java @@ -33,14 +33,35 @@ public void test() { } @Override - @SentinelResource(value = "hello", blockHandler = "exceptionHandler") + @SentinelResource(value = "hello", fallback = "helloFallback") public String hello(long s) { + if (s < 0) { + throw new IllegalArgumentException("invalid arg"); + } return String.format("Hello at %d", s); } - public String exceptionHandler(long s, BlockException ex) { + @Override + @SentinelResource(value = "helloAnother", defaultFallback = "defaultFallback", + exceptionsToIgnore = {IllegalStateException.class}) + public String helloAnother(String name) { + if (name == null || "bad".equals(name)) { + throw new IllegalArgumentException("oops"); + } + if ("foo".equals(name)) { + throw new IllegalStateException("oops"); + } + return "Hello, " + name; + } + + public String helloFallback(long s, Throwable ex) { // Do some log here. ex.printStackTrace(); return "Oops, error occurred at " + s; } + + public String defaultFallback() { + System.out.println("Go to default fallback"); + return "default_fallback"; + } } diff --git a/sentinel-demo/sentinel-demo-apache-dubbo/pom.xml b/sentinel-demo/sentinel-demo-apache-dubbo/pom.xml index bd0fe95420..9ecd614326 100644 --- a/sentinel-demo/sentinel-demo-apache-dubbo/pom.xml +++ b/sentinel-demo/sentinel-demo-apache-dubbo/pom.xml @@ -5,7 +5,7 @@ sentinel-demo com.alibaba.csp - 1.6.0-SNAPSHOT + 1.7.0-SNAPSHOT 4.0.0 diff --git a/sentinel-demo/sentinel-demo-apollo-datasource/pom.xml b/sentinel-demo/sentinel-demo-apollo-datasource/pom.xml index bfbcf1b524..977a803d74 100644 --- a/sentinel-demo/sentinel-demo-apollo-datasource/pom.xml +++ b/sentinel-demo/sentinel-demo-apollo-datasource/pom.xml @@ -5,7 +5,7 @@ sentinel-demo com.alibaba.csp - 1.6.0-SNAPSHOT + 1.7.0-SNAPSHOT 4.0.0 diff --git a/sentinel-demo/sentinel-demo-basic/pom.xml b/sentinel-demo/sentinel-demo-basic/pom.xml index c556c53d4a..1538bfaa15 100755 --- a/sentinel-demo/sentinel-demo-basic/pom.xml +++ b/sentinel-demo/sentinel-demo-basic/pom.xml @@ -6,7 +6,7 @@ com.alibaba.csp sentinel-demo - 1.6.0-SNAPSHOT + 1.7.0-SNAPSHOT sentinel-demo-basic diff --git a/sentinel-demo/sentinel-demo-cluster/pom.xml b/sentinel-demo/sentinel-demo-cluster/pom.xml index 48de32f474..7f40848bcc 100644 --- a/sentinel-demo/sentinel-demo-cluster/pom.xml +++ b/sentinel-demo/sentinel-demo-cluster/pom.xml @@ -5,7 +5,7 @@ sentinel-demo com.alibaba.csp - 1.6.0-SNAPSHOT + 1.7.0-SNAPSHOT 4.0.0 diff --git a/sentinel-demo/sentinel-demo-cluster/sentinel-demo-cluster-embedded/pom.xml b/sentinel-demo/sentinel-demo-cluster/sentinel-demo-cluster-embedded/pom.xml index 1d423b2d08..521ef63540 100644 --- a/sentinel-demo/sentinel-demo-cluster/sentinel-demo-cluster-embedded/pom.xml +++ b/sentinel-demo/sentinel-demo-cluster/sentinel-demo-cluster-embedded/pom.xml @@ -5,7 +5,7 @@ sentinel-demo-cluster com.alibaba.csp - 1.6.0-SNAPSHOT + 1.7.0-SNAPSHOT 4.0.0 diff --git a/sentinel-demo/sentinel-demo-cluster/sentinel-demo-cluster-server-alone/pom.xml b/sentinel-demo/sentinel-demo-cluster/sentinel-demo-cluster-server-alone/pom.xml index 15c8e8deed..74b29d0f9a 100644 --- a/sentinel-demo/sentinel-demo-cluster/sentinel-demo-cluster-server-alone/pom.xml +++ b/sentinel-demo/sentinel-demo-cluster/sentinel-demo-cluster-server-alone/pom.xml @@ -5,7 +5,7 @@ sentinel-demo-cluster com.alibaba.csp - 1.6.0-SNAPSHOT + 1.7.0-SNAPSHOT 4.0.0 diff --git a/sentinel-demo/sentinel-demo-command-handler/pom.xml b/sentinel-demo/sentinel-demo-command-handler/pom.xml index 5cbe3bf2b5..a0ce8dff5c 100644 --- a/sentinel-demo/sentinel-demo-command-handler/pom.xml +++ b/sentinel-demo/sentinel-demo-command-handler/pom.xml @@ -5,7 +5,7 @@ sentinel-demo com.alibaba.csp - 1.6.0-SNAPSHOT + 1.7.0-SNAPSHOT 4.0.0 diff --git a/sentinel-demo/sentinel-demo-dubbo/pom.xml b/sentinel-demo/sentinel-demo-dubbo/pom.xml index 5386e9067c..19ca045b40 100644 --- a/sentinel-demo/sentinel-demo-dubbo/pom.xml +++ b/sentinel-demo/sentinel-demo-dubbo/pom.xml @@ -5,7 +5,7 @@ sentinel-demo com.alibaba.csp - 1.6.0-SNAPSHOT + 1.7.0-SNAPSHOT 4.0.0 diff --git a/sentinel-demo/sentinel-demo-dynamic-file-rule/pom.xml b/sentinel-demo/sentinel-demo-dynamic-file-rule/pom.xml index 675e4d9110..e488efe593 100755 --- a/sentinel-demo/sentinel-demo-dynamic-file-rule/pom.xml +++ b/sentinel-demo/sentinel-demo-dynamic-file-rule/pom.xml @@ -6,7 +6,7 @@ com.alibaba.csp sentinel-demo - 1.6.0-SNAPSHOT + 1.7.0-SNAPSHOT sentinel-demo-dynamic-file-rule diff --git a/sentinel-demo/sentinel-demo-nacos-datasource/pom.xml b/sentinel-demo/sentinel-demo-nacos-datasource/pom.xml index 20499a5e18..9fe557c340 100644 --- a/sentinel-demo/sentinel-demo-nacos-datasource/pom.xml +++ b/sentinel-demo/sentinel-demo-nacos-datasource/pom.xml @@ -5,7 +5,7 @@ sentinel-demo com.alibaba.csp - 1.6.0-SNAPSHOT + 1.7.0-SNAPSHOT 4.0.0 diff --git a/sentinel-demo/sentinel-demo-parameter-flow-control/pom.xml b/sentinel-demo/sentinel-demo-parameter-flow-control/pom.xml index 507e26ee60..502c96e7bd 100644 --- a/sentinel-demo/sentinel-demo-parameter-flow-control/pom.xml +++ b/sentinel-demo/sentinel-demo-parameter-flow-control/pom.xml @@ -5,7 +5,7 @@ sentinel-demo com.alibaba.csp - 1.6.0-SNAPSHOT + 1.7.0-SNAPSHOT 4.0.0 diff --git a/sentinel-demo/sentinel-demo-rocketmq/pom.xml b/sentinel-demo/sentinel-demo-rocketmq/pom.xml index 6479beeaa1..9dda443a82 100755 --- a/sentinel-demo/sentinel-demo-rocketmq/pom.xml +++ b/sentinel-demo/sentinel-demo-rocketmq/pom.xml @@ -5,7 +5,7 @@ com.alibaba.csp sentinel-demo - 1.6.0-SNAPSHOT + 1.7.0-SNAPSHOT 4.0.0 diff --git a/sentinel-demo/sentinel-demo-slot-chain-spi/pom.xml b/sentinel-demo/sentinel-demo-slot-chain-spi/pom.xml index f4a28104ba..9dfa4c6686 100644 --- a/sentinel-demo/sentinel-demo-slot-chain-spi/pom.xml +++ b/sentinel-demo/sentinel-demo-slot-chain-spi/pom.xml @@ -5,7 +5,7 @@ sentinel-demo com.alibaba.csp - 1.6.0-SNAPSHOT + 1.7.0-SNAPSHOT 4.0.0 diff --git a/sentinel-demo/sentinel-demo-spring-cloud-gateway/pom.xml b/sentinel-demo/sentinel-demo-spring-cloud-gateway/pom.xml index bbdf5c7985..133f98ade4 100644 --- a/sentinel-demo/sentinel-demo-spring-cloud-gateway/pom.xml +++ b/sentinel-demo/sentinel-demo-spring-cloud-gateway/pom.xml @@ -5,7 +5,7 @@ sentinel-demo com.alibaba.csp - 1.6.0-SNAPSHOT + 1.7.0-SNAPSHOT 4.0.0 diff --git a/sentinel-demo/sentinel-demo-spring-webflux/pom.xml b/sentinel-demo/sentinel-demo-spring-webflux/pom.xml index 0b0825fccc..53beea6783 100644 --- a/sentinel-demo/sentinel-demo-spring-webflux/pom.xml +++ b/sentinel-demo/sentinel-demo-spring-webflux/pom.xml @@ -5,7 +5,7 @@ sentinel-demo com.alibaba.csp - 1.6.0-SNAPSHOT + 1.7.0-SNAPSHOT 4.0.0 diff --git a/sentinel-demo/sentinel-demo-zookeeper-datasource/pom.xml b/sentinel-demo/sentinel-demo-zookeeper-datasource/pom.xml index 9c56208735..6ad37d8ef4 100644 --- a/sentinel-demo/sentinel-demo-zookeeper-datasource/pom.xml +++ b/sentinel-demo/sentinel-demo-zookeeper-datasource/pom.xml @@ -5,7 +5,7 @@ sentinel-demo com.alibaba.csp - 1.6.0-SNAPSHOT + 1.7.0-SNAPSHOT 4.0.0 diff --git a/sentinel-demo/sentinel-demo-zuul-gateway/pom.xml b/sentinel-demo/sentinel-demo-zuul-gateway/pom.xml new file mode 100644 index 0000000000..21784ca720 --- /dev/null +++ b/sentinel-demo/sentinel-demo-zuul-gateway/pom.xml @@ -0,0 +1,36 @@ + + + + sentinel-demo + com.alibaba.csp + 1.7.0-SNAPSHOT + + 4.0.0 + + sentinel-demo-zuul-gateway + + + + org.springframework.cloud + spring-cloud-starter-netflix-zuul + 2.0.3.RELEASE + + + com.fasterxml.jackson.core + jackson-databind + 2.9.4 + + + + com.alibaba.csp + sentinel-zuul-adapter + ${project.version} + + + com.alibaba.csp + sentinel-transport-simple-http + + + \ No newline at end of file diff --git a/sentinel-demo/sentinel-demo-zuul-gateway/src/main/java/com/alibaba/csp/sentinel/demo/zuul/gateway/GatewayRuleConfig.java b/sentinel-demo/sentinel-demo-zuul-gateway/src/main/java/com/alibaba/csp/sentinel/demo/zuul/gateway/GatewayRuleConfig.java new file mode 100644 index 0000000000..ef2c386964 --- /dev/null +++ b/sentinel-demo/sentinel-demo-zuul-gateway/src/main/java/com/alibaba/csp/sentinel/demo/zuul/gateway/GatewayRuleConfig.java @@ -0,0 +1,111 @@ +/* + * 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.demo.zuul.gateway; + +import java.util.HashSet; +import java.util.Set; + +import javax.annotation.PostConstruct; + +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.ApiPathPredicateItem; +import com.alibaba.csp.sentinel.adapter.gateway.common.api.ApiPredicateItem; +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 org.springframework.context.annotation.Configuration; + +/** + * @author Eric Zhao + */ +@Configuration +public class GatewayRuleConfig { + + @PostConstruct + public void doInit() { + // Prepare some gateway rules and API definitions (only for demo). + // It's recommended to leverage dynamic data source or the Sentinel dashboard to push the rules. + initCustomizedApis(); + initGatewayRules(); + } + + private void initCustomizedApis() { + Set definitions = new HashSet<>(); + ApiDefinition api1 = new ApiDefinition("some_customized_api") + .setPredicateItems(new HashSet() {{ + add(new ApiPathPredicateItem().setPattern("/ahas")); + add(new ApiPathPredicateItem().setPattern("/aliyun_product/**") + .setMatchStrategy(SentinelGatewayConstants.PARAM_MATCH_STRATEGY_PREFIX)); + }}); + ApiDefinition api2 = new ApiDefinition("another_customized_api") + .setPredicateItems(new HashSet() {{ + add(new ApiPathPredicateItem().setPattern("/**") + .setMatchStrategy(SentinelGatewayConstants.PARAM_MATCH_STRATEGY_PREFIX)); + }}); + definitions.add(api1); + definitions.add(api2); + GatewayApiDefinitionManager.loadApiDefinitions(definitions); + } + + private void initGatewayRules() { + Set rules = new HashSet<>(); + rules.add(new GatewayFlowRule("aliyun-product-route") + .setCount(10) + .setIntervalSec(1) + ); + rules.add(new GatewayFlowRule("aliyun-product-route") + .setCount(2) + .setIntervalSec(2) + .setBurst(2) + .setParamItem(new GatewayParamFlowItem() + .setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_CLIENT_IP) + ) + ); + rules.add(new GatewayFlowRule("another-route-httpbin") + .setCount(10) + .setIntervalSec(1) + .setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER) + .setMaxQueueingTimeoutMs(600) + .setParamItem(new GatewayParamFlowItem() + .setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_HEADER) + .setFieldName("X-Sentinel-Flag") + ) + ); + rules.add(new GatewayFlowRule("another-route-httpbin") + .setCount(1) + .setIntervalSec(1) + .setParamItem(new GatewayParamFlowItem() + .setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM) + .setFieldName("pa") + ) + ); + + rules.add(new GatewayFlowRule("some_customized_api") + .setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME) + .setCount(5) + .setIntervalSec(1) + .setParamItem(new GatewayParamFlowItem() + .setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM) + .setFieldName("pn") + ) + ); + GatewayRuleManager.loadRules(rules); + } +} diff --git a/sentinel-demo/sentinel-demo-zuul-gateway/src/main/java/com/alibaba/csp/sentinel/demo/zuul/gateway/ZuulConfig.java b/sentinel-demo/sentinel-demo-zuul-gateway/src/main/java/com/alibaba/csp/sentinel/demo/zuul/gateway/ZuulConfig.java new file mode 100644 index 0000000000..d1c83779ac --- /dev/null +++ b/sentinel-demo/sentinel-demo-zuul-gateway/src/main/java/com/alibaba/csp/sentinel/demo/zuul/gateway/ZuulConfig.java @@ -0,0 +1,46 @@ +/* + * 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.demo.zuul.gateway; + +import com.alibaba.csp.sentinel.adapter.gateway.zuul.filters.SentinelZuulErrorFilter; +import com.alibaba.csp.sentinel.adapter.gateway.zuul.filters.SentinelZuulPostFilter; +import com.alibaba.csp.sentinel.adapter.gateway.zuul.filters.SentinelZuulPreFilter; + +import com.netflix.zuul.ZuulFilter; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * @author Eric Zhao + */ +@Configuration +public class ZuulConfig { + + @Bean + public ZuulFilter sentinelZuulPreFilter() { + return new SentinelZuulPreFilter(10000); + } + + @Bean + public ZuulFilter sentinelZuulPostFilter() { + return new SentinelZuulPostFilter(1000); + } + + @Bean + public ZuulFilter sentinelZuulErrorFilter() { + return new SentinelZuulErrorFilter(-1); + } +} \ No newline at end of file diff --git a/sentinel-demo/sentinel-demo-zuul-gateway/src/main/java/com/alibaba/csp/sentinel/demo/zuul/gateway/ZuulGatewayDemoApplication.java b/sentinel-demo/sentinel-demo-zuul-gateway/src/main/java/com/alibaba/csp/sentinel/demo/zuul/gateway/ZuulGatewayDemoApplication.java new file mode 100644 index 0000000000..3b5f51ca33 --- /dev/null +++ b/sentinel-demo/sentinel-demo-zuul-gateway/src/main/java/com/alibaba/csp/sentinel/demo/zuul/gateway/ZuulGatewayDemoApplication.java @@ -0,0 +1,41 @@ +/* + * 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.demo.zuul.gateway; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cloud.netflix.zuul.EnableZuulProxy; + +/** + *

A demo for using Zuul 1.x with Spring Cloud and Sentinel.

+ * + *

To integrate with Sentinel dashboard, you can run the demo with the parameters (an example): + * + * -Dproject.name=zuul-gateway -Dcsp.sentinel.dashboard.server=localhost:8080 + * -Dcsp.sentinel.api.port=8720 -Dcsp.sentinel.app.type=1 + * + *

+ * + * @author Eric Zhao + */ +@SpringBootApplication +@EnableZuulProxy +public class ZuulGatewayDemoApplication { + + public static void main(String[] args) { + SpringApplication.run(ZuulGatewayDemoApplication.class, args); + } +} diff --git a/sentinel-demo/sentinel-demo-zuul-gateway/src/main/resources/application.yml b/sentinel-demo/sentinel-demo-zuul-gateway/src/main/resources/application.yml new file mode 100644 index 0000000000..296d5083b5 --- /dev/null +++ b/sentinel-demo/sentinel-demo-zuul-gateway/src/main/resources/application.yml @@ -0,0 +1,13 @@ +server: + port: 8097 +spring: + application: + name: zuul-gateway +zuul: + routes: + aliyun-product-route: + path: /aliyun_product/** + url: https://www.aliyun.com/product + another-route-httpbin: + path: /another/** + url: https://httpbin.org \ No newline at end of file diff --git a/sentinel-extension/pom.xml b/sentinel-extension/pom.xml index aa5202a934..075323a6d4 100755 --- a/sentinel-extension/pom.xml +++ b/sentinel-extension/pom.xml @@ -6,7 +6,7 @@ com.alibaba.csp sentinel-parent - 1.6.0-SNAPSHOT + 1.7.0-SNAPSHOT sentinel-extension pom diff --git a/sentinel-extension/sentinel-annotation-aspectj/README.md b/sentinel-extension/sentinel-annotation-aspectj/README.md index 15e7e0fb32..00967d25e6 100644 --- a/sentinel-extension/sentinel-annotation-aspectj/README.md +++ b/sentinel-extension/sentinel-annotation-aspectj/README.md @@ -9,26 +9,31 @@ The `@SentinelResource` annotation indicates a resource definition, including: - `value`: Resource name, required (cannot be empty) - `entryType`: Resource entry type (inbound or outbound), `EntryType.OUT` by default -- `fallback`: Fallback method when degraded (optional). The fallback method should be located in the same class with original method. The signature of the fallback method should match the original method (parameter types and return type). -- `blockHandler`: Handler method that handles `BlockException` when blocked. The signature should match original method, with the last additional parameter type `BlockException`. The block handler method should be located in the same class with original method by default. If you want to use method in other classes, you can set the `blockHandlerClass` with corresponding `Class` (Note the method in other classes must be *static*). -- `exceptionsToTrace`: List of business exception classes to trace and record (since 1.5.1). +- `fallback` (refactored since 1.6.0): Fallback method when exceptions caught (including `BlockException`, but except the exceptions defined in `exceptionsToIgnore`). The fallback method should be located in the same class with original method by default. If you want to use method in other classes, you can set the `fallbackClass` with corresponding `Class` (Note the method in other classes must be *static*). The method signature requirement: + - The return type should match the origin method; + - The parameter list should match the origin method, and an additional `Throwable` parameter can be provided to get the actual exception. +- `defaultFallback` (since 1.6.0): The default fallback method when exceptions caught (including `BlockException`, but except the exceptions defined in `exceptionsToIgnore`). Its intended to be a universal common fallback method. The method should be located in the same class with original method by default. If you want to use method in other classes, you can set the `fallbackClass` with corresponding `Class` (Note the method in other classes must be *static*). The default fallback method signature requirement: + - The return type should match the origin method; + - parameter list should be empty, and an additional `Throwable` parameter can be provided to get the actual exception. +- `blockHandler`: Handler method that handles `BlockException` when blocked. The parameter list of the method should match original method, with the last additional parameter type `BlockException`. The return type should be same as the original method. The `blockHandler` method should be located in the same class with original method by default. If you want to use method in other classes, you can set the `blockHandlerClass` with corresponding `Class` (Note the method in other classes must be *static*). +- `exceptionsToIgnore` (since 1.6.0): List of business exception classes that should not be traced and caught in fallback. +- `exceptionsToTrace` (since 1.5.1): List of business exception classes to trace and record. In most cases, using `exceptionsToIgnore` is better. If both `exceptionsToTrace` and `exceptionsToIgnore` are present, only `exceptionsToIgnore` will be activated. For example: ```java -@SentinelResource(value = "abc", fallback = "doFallback", blockHandler = "handleException") +@SentinelResource(value = "abc", fallback = "doFallback") public String doSomething(long i) { return "Hello " + i; } -public String doFallback(long i) { +public String doFallback(long i, Throwable t) { // Return fallback value. - return "Oops, degraded"; + return "fallback"; } -public String handleException(long i, BlockException ex) { - // Handle the block exception here. - return null; +public String defaultFallback(Throwable t) { + return "default_fallback"; } ``` diff --git a/sentinel-extension/sentinel-annotation-aspectj/pom.xml b/sentinel-extension/sentinel-annotation-aspectj/pom.xml index 6f35996460..287b7a4834 100644 --- a/sentinel-extension/sentinel-annotation-aspectj/pom.xml +++ b/sentinel-extension/sentinel-annotation-aspectj/pom.xml @@ -5,7 +5,7 @@ sentinel-extension com.alibaba.csp - 1.6.0-SNAPSHOT + 1.7.0-SNAPSHOT 4.0.0 diff --git a/sentinel-extension/sentinel-annotation-aspectj/src/main/java/com/alibaba/csp/sentinel/annotation/aspectj/AbstractSentinelAspectSupport.java b/sentinel-extension/sentinel-annotation-aspectj/src/main/java/com/alibaba/csp/sentinel/annotation/aspectj/AbstractSentinelAspectSupport.java index dce92b8e49..13ef92d6c2 100644 --- a/sentinel-extension/sentinel-annotation-aspectj/src/main/java/com/alibaba/csp/sentinel/annotation/aspectj/AbstractSentinelAspectSupport.java +++ b/sentinel-extension/sentinel-annotation-aspectj/src/main/java/com/alibaba/csp/sentinel/annotation/aspectj/AbstractSentinelAspectSupport.java @@ -19,7 +19,6 @@ import com.alibaba.csp.sentinel.annotation.SentinelResource; import com.alibaba.csp.sentinel.log.RecordLog; import com.alibaba.csp.sentinel.slots.block.BlockException; -import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException; import com.alibaba.csp.sentinel.util.MethodUtil; import com.alibaba.csp.sentinel.util.StringUtil; @@ -37,103 +36,127 @@ */ public abstract class AbstractSentinelAspectSupport { - protected String getResourceName(String resourceName, /*@NonNull*/ Method method) { - // If resource name is present in annotation, use this value. - if (StringUtil.isNotBlank(resourceName)) { - return resourceName; - } - // Parse name of target method. - return MethodUtil.resolveMethodName(method); - } - - protected Object handleBlockException(ProceedingJoinPoint pjp, SentinelResource annotation, BlockException ex) - throws Exception { - return handleBlockException(pjp, annotation.fallback(), annotation.blockHandler(), - annotation.blockHandlerClass(), ex); - } - - protected Object handleBlockException(ProceedingJoinPoint pjp, String fallback, String blockHandler, - Class[] blockHandlerClass, BlockException ex) throws Exception { - // Execute fallback for degrading if configured. - Object[] originArgs = pjp.getArgs(); - if (isDegradeFailure(ex)) { - Method method = extractFallbackMethod(pjp, fallback); - if (method != null) { - return method.invoke(pjp.getTarget(), originArgs); - } - } - // Execute block handler if configured. - Method blockHandlerMethod = extractBlockHandlerMethod(pjp, blockHandler, blockHandlerClass); - if (blockHandlerMethod != null) { - // Construct args. - Object[] args = Arrays.copyOf(originArgs, originArgs.length + 1); - args[args.length - 1] = ex; - if (isStatic(blockHandlerMethod)) { - return blockHandlerMethod.invoke(null, args); - } - return blockHandlerMethod.invoke(pjp.getTarget(), args); - } - // If no block handler is present, then directly throw the exception. - throw ex; + protected void traceException(Throwable ex) { + Tracer.trace(ex); } protected void traceException(Throwable ex, SentinelResource annotation) { Class[] exceptionsToIgnore = annotation.exceptionsToIgnore(); // The ignore list will be checked first. - if (exceptionsToIgnore.length > 0 && isIgnoredException(ex, exceptionsToIgnore)) { + if (exceptionsToIgnore.length > 0 && exceptionBelongsTo(ex, exceptionsToIgnore)) { return; } - if (isTracedException(ex, annotation.exceptionsToTrace())) { - Tracer.trace(ex); + if (exceptionBelongsTo(ex, annotation.exceptionsToTrace())) { + traceException(ex); } } /** - * Check whether the exception is in tracked list of exception classes. + * Check whether the exception is in provided list of exception classes. * - * @param ex - * provided throwable - * @param exceptionsToTrace - * list of exceptions to trace - * @return true if it should be traced, otherwise false + * @param ex provided throwable + * @param exceptions list of exceptions + * @return true if it is in the list, otherwise false */ - private boolean isTracedException(Throwable ex, Class[] exceptionsToTrace) { - if (exceptionsToTrace == null) { + protected boolean exceptionBelongsTo(Throwable ex, Class[] exceptions) { + if (exceptions == null) { return false; } - for (Class exceptionToTrace : exceptionsToTrace) { - if (exceptionToTrace.isAssignableFrom(ex.getClass())) { + for (Class exceptionClass : exceptions) { + if (exceptionClass.isAssignableFrom(ex.getClass())) { return true; } } return false; } - private boolean isIgnoredException(Throwable ex, Class[] exceptionsToIgnore) { - if (exceptionsToIgnore == null) { - return false; + protected String getResourceName(String resourceName, /*@NonNull*/ Method method) { + // If resource name is present in annotation, use this value. + if (StringUtil.isNotBlank(resourceName)) { + return resourceName; } - for (Class exceptionToIgnore : exceptionsToIgnore) { - if (exceptionToIgnore.isAssignableFrom(ex.getClass())) { - return true; + // Parse name of target method. + return MethodUtil.resolveMethodName(method); + } + + protected Object handleFallback(ProceedingJoinPoint pjp, SentinelResource annotation, Throwable ex) + throws Throwable { + return handleFallback(pjp, annotation.fallback(), annotation.defaultFallback(), annotation.fallbackClass(), ex); + } + + protected Object handleFallback(ProceedingJoinPoint pjp, String fallback, String defaultFallback, + Class[] fallbackClass, Throwable ex) throws Throwable { + Object[] originArgs = pjp.getArgs(); + + // Execute fallback function if configured. + Method fallbackMethod = extractFallbackMethod(pjp, fallback, fallbackClass); + if (fallbackMethod != null) { + // Construct args. + int paramCount = fallbackMethod.getParameterTypes().length; + Object[] args; + if (paramCount == originArgs.length) { + args = originArgs; + } else { + args = Arrays.copyOf(originArgs, originArgs.length + 1); + args[args.length - 1] = ex; } + if (isStatic(fallbackMethod)) { + return fallbackMethod.invoke(null, args); + } + return fallbackMethod.invoke(pjp.getTarget(), args); } - return false; + // If fallback is absent, we'll try the defaultFallback if provided. + return handleDefaultFallback(pjp, defaultFallback, fallbackClass, ex); } - private boolean isDegradeFailure(/*@NonNull*/ BlockException ex) { - return ex instanceof DegradeException; + protected Object handleDefaultFallback(ProceedingJoinPoint pjp, String defaultFallback, + Class[] fallbackClass, Throwable ex) throws Throwable { + // Execute the default fallback function if configured. + Method fallbackMethod = extractDefaultFallbackMethod(pjp, defaultFallback, fallbackClass); + if (fallbackMethod != null) { + // Construct args. + Object[] args = fallbackMethod.getParameterTypes().length == 0 ? new Object[0] : new Object[] {ex}; + if (isStatic(fallbackMethod)) { + return fallbackMethod.invoke(null, args); + } + return fallbackMethod.invoke(pjp.getTarget(), args); + } + + // If no any fallback is present, then directly throw the exception. + throw ex; } - private Method extractFallbackMethod(ProceedingJoinPoint pjp, String fallbackName) { + protected Object handleBlockException(ProceedingJoinPoint pjp, SentinelResource annotation, BlockException ex) + throws Throwable { + + // Execute block handler if configured. + Method blockHandlerMethod = extractBlockHandlerMethod(pjp, annotation.blockHandler(), + annotation.blockHandlerClass()); + if (blockHandlerMethod != null) { + Object[] originArgs = pjp.getArgs(); + // Construct args. + Object[] args = Arrays.copyOf(originArgs, originArgs.length + 1); + args[args.length - 1] = ex; + if (isStatic(blockHandlerMethod)) { + return blockHandlerMethod.invoke(null, args); + } + return blockHandlerMethod.invoke(pjp.getTarget(), args); + } + + // If no block handler is present, then go to fallback. + return handleFallback(pjp, annotation, ex); + } + + private Method extractFallbackMethod(ProceedingJoinPoint pjp, String fallbackName, Class[] locationClass) { if (StringUtil.isBlank(fallbackName)) { return null; } - Class clazz = pjp.getTarget().getClass(); + boolean mustStatic = locationClass != null && locationClass.length >= 1; + Class clazz = mustStatic ? locationClass[0] : pjp.getTarget().getClass(); MethodWrapper m = ResourceMetadataRegistry.lookupFallback(clazz, fallbackName); if (m == null) { // First time, resolve the fallback. - Method method = resolveFallbackInternal(pjp, fallbackName); + Method method = resolveFallbackInternal(pjp, fallbackName, clazz, mustStatic); // Cache the method instance. ResourceMetadataRegistry.updateFallbackFor(clazz, fallbackName, method); return method; @@ -144,10 +167,53 @@ private Method extractFallbackMethod(ProceedingJoinPoint pjp, String fallbackNam return m.getMethod(); } - private Method resolveFallbackInternal(ProceedingJoinPoint pjp, /*@NonNull*/ String name) { + private Method extractDefaultFallbackMethod(ProceedingJoinPoint pjp, String defaultFallback, + Class[] locationClass) { + if (StringUtil.isBlank(defaultFallback)) { + return null; + } + boolean mustStatic = locationClass != null && locationClass.length >= 1; + Class clazz = mustStatic ? locationClass[0] : pjp.getTarget().getClass(); + + MethodWrapper m = ResourceMetadataRegistry.lookupDefaultFallback(clazz, defaultFallback); + if (m == null) { + // First time, resolve the default fallback. + Class originReturnType = resolveMethod(pjp).getReturnType(); + // Default fallback allows two kinds of parameter list. + // One is empty parameter list. + Class[] defaultParamTypes = new Class[0]; + // The other is a single parameter {@link Throwable} to get relevant exception info. + Class[] paramTypeWithException = new Class[] {Throwable.class}; + // We first find the default fallback with empty parameter list. + Method method = findMethod(mustStatic, clazz, defaultFallback, originReturnType, defaultParamTypes); + // If default fallback with empty params is absent, we then try to find the other one. + if (method == null) { + method = findMethod(mustStatic, clazz, defaultFallback, originReturnType, paramTypeWithException); + } + // Cache the method instance. + ResourceMetadataRegistry.updateDefaultFallbackFor(clazz, defaultFallback, method); + return method; + } + if (!m.isPresent()) { + return null; + } + return m.getMethod(); + } + + private Method resolveFallbackInternal(ProceedingJoinPoint pjp, /*@NonNull*/ String name, Class clazz, + boolean mustStatic) { Method originMethod = resolveMethod(pjp); - Class[] parameterTypes = originMethod.getParameterTypes(); - return findMethod(false, pjp.getTarget().getClass(), name, originMethod.getReturnType(), parameterTypes); + // Fallback function allows two kinds of parameter list. + Class[] defaultParamTypes = originMethod.getParameterTypes(); + Class[] paramTypesWithException = Arrays.copyOf(defaultParamTypes, defaultParamTypes.length + 1); + paramTypesWithException[paramTypesWithException.length - 1] = Throwable.class; + // We first find the fallback matching the signature of origin method. + Method method = findMethod(mustStatic, clazz, name, originMethod.getReturnType(), defaultParamTypes); + // If fallback matching the origin method is absent, we then try to find the other one. + if (method == null) { + method = findMethod(mustStatic, clazz, name, originMethod.getReturnType(), paramTypesWithException); + } + return method; } private Method extractBlockHandlerMethod(ProceedingJoinPoint pjp, String name, Class[] locationClass) { @@ -195,8 +261,8 @@ private Method findMethod(boolean mustStatic, Class clazz, String name, Class Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { if (name.equals(method.getName()) && checkStatic(mustStatic, method) - && returnType.isAssignableFrom(method.getReturnType()) - && Arrays.equals(parameterTypes, method.getParameterTypes())) { + && returnType.isAssignableFrom(method.getReturnType()) + && Arrays.equals(parameterTypes, method.getParameterTypes())) { RecordLog.info("Resolved method [{0}] in class [{1}]", name, clazz.getCanonicalName()); return method; @@ -209,7 +275,7 @@ private Method findMethod(boolean mustStatic, Class clazz, String name, Class } else { String methodType = mustStatic ? " static" : ""; RecordLog.warn("Cannot find{0} method [{1}] in class [{2}] with parameters {3}", - methodType, name, clazz.getCanonicalName(), Arrays.toString(parameterTypes)); + methodType, name, clazz.getCanonicalName(), Arrays.toString(parameterTypes)); return null; } } @@ -223,7 +289,7 @@ protected Method resolveMethod(ProceedingJoinPoint joinPoint) { Class targetClass = joinPoint.getTarget().getClass(); Method method = getDeclaredMethodFor(targetClass, signature.getName(), - signature.getMethod().getParameterTypes()); + signature.getMethod().getParameterTypes()); if (method == null) { throw new IllegalStateException("Cannot resolve target method: " + signature.getMethod().getName()); } diff --git a/sentinel-extension/sentinel-annotation-aspectj/src/main/java/com/alibaba/csp/sentinel/annotation/aspectj/ResourceMetadataRegistry.java b/sentinel-extension/sentinel-annotation-aspectj/src/main/java/com/alibaba/csp/sentinel/annotation/aspectj/ResourceMetadataRegistry.java index 25bb1bfd6c..c4988130a8 100644 --- a/sentinel-extension/sentinel-annotation-aspectj/src/main/java/com/alibaba/csp/sentinel/annotation/aspectj/ResourceMetadataRegistry.java +++ b/sentinel-extension/sentinel-annotation-aspectj/src/main/java/com/alibaba/csp/sentinel/annotation/aspectj/ResourceMetadataRegistry.java @@ -28,13 +28,18 @@ */ final class ResourceMetadataRegistry { - private static final Map FALLBACK_MAP = new ConcurrentHashMap(); - private static final Map BLOCK_HANDLER_MAP = new ConcurrentHashMap(); + private static final Map FALLBACK_MAP = new ConcurrentHashMap<>(); + private static final Map DEFAULT_FALLBACK_MAP = new ConcurrentHashMap<>(); + private static final Map BLOCK_HANDLER_MAP = new ConcurrentHashMap<>(); static MethodWrapper lookupFallback(Class clazz, String name) { return FALLBACK_MAP.get(getKey(clazz, name)); } + static MethodWrapper lookupDefaultFallback(Class clazz, String name) { + return DEFAULT_FALLBACK_MAP.get(getKey(clazz, name)); + } + static MethodWrapper lookupBlockHandler(Class clazz, String name) { return BLOCK_HANDLER_MAP.get(getKey(clazz, name)); } @@ -46,6 +51,13 @@ static void updateFallbackFor(Class clazz, String name, Method method) { FALLBACK_MAP.put(getKey(clazz, name), MethodWrapper.wrap(method)); } + static void updateDefaultFallbackFor(Class clazz, String name, Method method) { + if (clazz == null || StringUtil.isBlank(name)) { + throw new IllegalArgumentException("Bad argument"); + } + DEFAULT_FALLBACK_MAP.put(getKey(clazz, name), MethodWrapper.wrap(method)); + } + static void updateBlockHandlerFor(Class clazz, String name, Method method) { if (clazz == null || StringUtil.isBlank(name)) { throw new IllegalArgumentException("Bad argument"); diff --git a/sentinel-extension/sentinel-annotation-aspectj/src/main/java/com/alibaba/csp/sentinel/annotation/aspectj/SentinelResourceAspect.java b/sentinel-extension/sentinel-annotation-aspectj/src/main/java/com/alibaba/csp/sentinel/annotation/aspectj/SentinelResourceAspect.java index 39f9a99676..0f5e9a9703 100644 --- a/sentinel-extension/sentinel-annotation-aspectj/src/main/java/com/alibaba/csp/sentinel/annotation/aspectj/SentinelResourceAspect.java +++ b/sentinel-extension/sentinel-annotation-aspectj/src/main/java/com/alibaba/csp/sentinel/annotation/aspectj/SentinelResourceAspect.java @@ -58,7 +58,17 @@ public Object invokeResourceWithSentinel(ProceedingJoinPoint pjp) throws Throwab } catch (BlockException ex) { return handleBlockException(pjp, annotation, ex); } catch (Throwable ex) { - traceException(ex, annotation); + Class[] exceptionsToIgnore = annotation.exceptionsToIgnore(); + // The ignore list will be checked first. + if (exceptionsToIgnore.length > 0 && exceptionBelongsTo(ex, exceptionsToIgnore)) { + throw ex; + } + if (exceptionBelongsTo(ex, annotation.exceptionsToTrace())) { + traceException(ex, annotation); + return handleFallback(pjp, annotation, ex); + } + + // No fallback function can handle the exception, so throw it out. throw ex; } finally { if (entry != null) { diff --git a/sentinel-extension/sentinel-annotation-aspectj/src/test/java/com/alibaba/csp/sentinel/annotation/aspectj/integration/SentinelAnnotationIntegrationTest.java b/sentinel-extension/sentinel-annotation-aspectj/src/test/java/com/alibaba/csp/sentinel/annotation/aspectj/integration/SentinelAnnotationIntegrationTest.java index 986c064ccc..bf127cc932 100644 --- a/sentinel-extension/sentinel-annotation-aspectj/src/test/java/com/alibaba/csp/sentinel/annotation/aspectj/integration/SentinelAnnotationIntegrationTest.java +++ b/sentinel-extension/sentinel-annotation-aspectj/src/test/java/com/alibaba/csp/sentinel/annotation/aspectj/integration/SentinelAnnotationIntegrationTest.java @@ -99,6 +99,56 @@ public void testAnnotationExceptionsToIgnore() { } } + @Test + public void testFallbackWithNoParams() throws Exception { + assertThat(fooService.fooWithFallback(1)).isEqualTo("Hello for 1"); + String resourceName = "apiFooWithFallback"; + ClusterNode cn = ClusterBuilderSlot.getClusterNode(resourceName); + assertThat(cn).isNotNull(); + assertThat(cn.passQps()).isPositive(); + + // Fallback should be ignored for this. + try { + fooService.fooWithFallback(5758); + fail("should not reach here"); + } catch (IllegalAccessException e) { + assertThat(cn.exceptionQps()).isZero(); + } + + // Fallback should take effect. + assertThat(fooService.fooWithFallback(5763)).isEqualTo("eee..."); + assertThat(cn.exceptionQps()).isPositive(); + assertThat(cn.blockQps()).isZero(); + + FlowRuleManager.loadRules(Collections.singletonList( + new FlowRule(resourceName).setCount(0) + )); + // Fallback should not take effect for BlockException, as blockHandler is configured. + assertThat(fooService.fooWithFallback(2221)).isEqualTo("Oops, 2221"); + assertThat(cn.blockQps()).isPositive(); + } + + @Test + public void testDefaultFallbackWithSingleParam() { + assertThat(fooService.anotherFoo(1)).isEqualTo("Hello for 1"); + String resourceName = "apiAnotherFooWithDefaultFallback"; + ClusterNode cn = ClusterBuilderSlot.getClusterNode(resourceName); + assertThat(cn).isNotNull(); + assertThat(cn.passQps()).isPositive(); + + // Default fallback should take effect. + assertThat(fooService.anotherFoo(5758)).isEqualTo(FooUtil.FALLBACK_DEFAULT_RESULT); + assertThat(cn.exceptionQps()).isPositive(); + assertThat(cn.blockQps()).isZero(); + + FlowRuleManager.loadRules(Collections.singletonList( + new FlowRule(resourceName).setCount(0) + )); + // Default fallback should also take effect for BlockException. + assertThat(fooService.anotherFoo(5758)).isEqualTo(FooUtil.FALLBACK_DEFAULT_RESULT); + assertThat(cn.blockQps()).isPositive(); + } + @Test public void testNormalBlockHandlerAndFallback() throws Exception { assertThat(fooService.foo(1)).isEqualTo("Hello for 1"); @@ -107,9 +157,6 @@ public void testNormalBlockHandlerAndFallback() throws Exception { assertThat(cn).isNotNull(); assertThat(cn.passQps()).isPositive(); - // Test for fallback. - assertThat(fooService.foo(9527)).isEqualTo("eee..."); - // Test for biz exception. try { fooService.foo(5758); diff --git a/sentinel-extension/sentinel-annotation-aspectj/src/test/java/com/alibaba/csp/sentinel/annotation/aspectj/integration/service/FooService.java b/sentinel-extension/sentinel-annotation-aspectj/src/test/java/com/alibaba/csp/sentinel/annotation/aspectj/integration/service/FooService.java index 25bb98beb4..a8dfe1bc3f 100644 --- a/sentinel-extension/sentinel-annotation-aspectj/src/test/java/com/alibaba/csp/sentinel/annotation/aspectj/integration/service/FooService.java +++ b/sentinel-extension/sentinel-annotation-aspectj/src/test/java/com/alibaba/csp/sentinel/annotation/aspectj/integration/service/FooService.java @@ -29,12 +29,21 @@ @Service public class FooService { - @SentinelResource(value = "apiFoo", blockHandler = "fooBlockHandler", fallback = "fooFallbackFunc", + @SentinelResource(value = "apiFoo", blockHandler = "fooBlockHandler", exceptionsToTrace = {IllegalArgumentException.class}) public String foo(int i) throws Exception { - if (i == 9527) { - throw new DegradeException("ggg"); + if (i == 5758) { + throw new IllegalAccessException(); } + if (i == 5763) { + throw new IllegalArgumentException(); + } + return "Hello for " + i; + } + + @SentinelResource(value = "apiFooWithFallback", blockHandler = "fooBlockHandler", fallback = "fooFallbackFunc", + exceptionsToTrace = {IllegalArgumentException.class}) + public String fooWithFallback(int i) throws Exception { if (i == 5758) { throw new IllegalAccessException(); } @@ -44,6 +53,15 @@ public String foo(int i) throws Exception { return "Hello for " + i; } + @SentinelResource(value = "apiAnotherFooWithDefaultFallback", defaultFallback = "globalDefaultFallback", + fallbackClass = {FooUtil.class}) + public String anotherFoo(int i) { + if (i == 5758) { + throw new IllegalArgumentException("oops"); + } + return "Hello for " + i; + } + @SentinelResource(blockHandler = "globalBlockHandler", blockHandlerClass = FooUtil.class) public int random() { return ThreadLocalRandom.current().nextInt(0, 30000); diff --git a/sentinel-extension/sentinel-annotation-aspectj/src/test/java/com/alibaba/csp/sentinel/annotation/aspectj/integration/service/FooUtil.java b/sentinel-extension/sentinel-annotation-aspectj/src/test/java/com/alibaba/csp/sentinel/annotation/aspectj/integration/service/FooUtil.java index 52bcb17ba4..2af079d30e 100644 --- a/sentinel-extension/sentinel-annotation-aspectj/src/test/java/com/alibaba/csp/sentinel/annotation/aspectj/integration/service/FooUtil.java +++ b/sentinel-extension/sentinel-annotation-aspectj/src/test/java/com/alibaba/csp/sentinel/annotation/aspectj/integration/service/FooUtil.java @@ -23,9 +23,15 @@ public class FooUtil { public static final int BLOCK_FLAG = 88888; + public static final String FALLBACK_DEFAULT_RESULT = "fallback"; public static int globalBlockHandler(BlockException ex) { System.out.println("Oops: " + ex.getClass().getSimpleName()); return BLOCK_FLAG; } + + public static String globalDefaultFallback(Throwable t) { + System.out.println("Fallback caught: " + t.getClass().getSimpleName()); + return FALLBACK_DEFAULT_RESULT; + } } diff --git a/sentinel-extension/sentinel-datasource-apollo/pom.xml b/sentinel-extension/sentinel-datasource-apollo/pom.xml index a8eedcdba0..e802417fa8 100644 --- a/sentinel-extension/sentinel-datasource-apollo/pom.xml +++ b/sentinel-extension/sentinel-datasource-apollo/pom.xml @@ -5,7 +5,7 @@ sentinel-extension com.alibaba.csp - 1.6.0-SNAPSHOT + 1.7.0-SNAPSHOT 4.0.0 diff --git a/sentinel-extension/sentinel-datasource-extension/pom.xml b/sentinel-extension/sentinel-datasource-extension/pom.xml index bb65ca3438..45e6d77f33 100755 --- a/sentinel-extension/sentinel-datasource-extension/pom.xml +++ b/sentinel-extension/sentinel-datasource-extension/pom.xml @@ -6,7 +6,7 @@ com.alibaba.csp sentinel-extension - 1.6.0-SNAPSHOT + 1.7.0-SNAPSHOT sentinel-datasource-extension diff --git a/sentinel-extension/sentinel-datasource-nacos/pom.xml b/sentinel-extension/sentinel-datasource-nacos/pom.xml index e260cd4fd2..4f80e984dd 100644 --- a/sentinel-extension/sentinel-datasource-nacos/pom.xml +++ b/sentinel-extension/sentinel-datasource-nacos/pom.xml @@ -5,7 +5,7 @@ sentinel-extension com.alibaba.csp - 1.6.0-SNAPSHOT + 1.7.0-SNAPSHOT 4.0.0 @@ -13,7 +13,7 @@ jar - 0.8.0 + 1.0.0 diff --git a/sentinel-extension/sentinel-datasource-redis/pom.xml b/sentinel-extension/sentinel-datasource-redis/pom.xml index 0768d08b21..a07831a3f0 100644 --- a/sentinel-extension/sentinel-datasource-redis/pom.xml +++ b/sentinel-extension/sentinel-datasource-redis/pom.xml @@ -5,7 +5,7 @@ sentinel-extension com.alibaba.csp - 1.6.0-SNAPSHOT + 1.7.0-SNAPSHOT 4.0.0 diff --git a/sentinel-extension/sentinel-datasource-zookeeper/pom.xml b/sentinel-extension/sentinel-datasource-zookeeper/pom.xml index 7e233958ad..2cd550d7ea 100644 --- a/sentinel-extension/sentinel-datasource-zookeeper/pom.xml +++ b/sentinel-extension/sentinel-datasource-zookeeper/pom.xml @@ -5,7 +5,7 @@ sentinel-extension com.alibaba.csp - 1.6.0-SNAPSHOT + 1.7.0-SNAPSHOT 4.0.0 diff --git a/sentinel-extension/sentinel-parameter-flow-control/pom.xml b/sentinel-extension/sentinel-parameter-flow-control/pom.xml index 6fe3c71216..406ec2b752 100644 --- a/sentinel-extension/sentinel-parameter-flow-control/pom.xml +++ b/sentinel-extension/sentinel-parameter-flow-control/pom.xml @@ -5,7 +5,7 @@ sentinel-extension com.alibaba.csp - 1.6.0-SNAPSHOT + 1.7.0-SNAPSHOT 4.0.0 diff --git a/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowChecker.java b/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowChecker.java index b61c2491e4..aa4598f8cc 100644 --- a/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowChecker.java +++ b/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowChecker.java @@ -43,9 +43,9 @@ * @author Eric Zhao * @since 0.2.0 */ -final class ParamFlowChecker { +public final class ParamFlowChecker { - static boolean passCheck(ResourceWrapper resourceWrapper, /*@Valid*/ ParamFlowRule rule, /*@Valid*/ int count, + public static boolean passCheck(ResourceWrapper resourceWrapper, /*@Valid*/ ParamFlowRule rule, /*@Valid*/ int count, Object... args) { if (args == null) { return true; @@ -249,7 +249,7 @@ static boolean passThrottleLocalCheck(ResourceWrapper resourceWrapper, ParamFlow private static ParameterMetric getParameterMetric(ResourceWrapper resourceWrapper) { // Should not be null. - return ParamFlowSlot.getParamMetric(resourceWrapper); + return ParameterMetricStorage.getParamMetric(resourceWrapper); } @SuppressWarnings("unchecked") diff --git a/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRuleManager.java b/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRuleManager.java index 2481c94373..2467ba6400 100644 --- a/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRuleManager.java +++ b/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRuleManager.java @@ -16,7 +16,6 @@ package com.alibaba.csp.sentinel.slots.block.flow.param; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -26,9 +25,7 @@ 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.util.AssertUtil; -import com.alibaba.csp.sentinel.util.StringUtil; /** * Manager for frequent ("hot-spot") parameter flow rules. @@ -39,7 +36,7 @@ */ public final class ParamFlowRuleManager { - private static final Map> paramFlowRules = new ConcurrentHashMap<>(); + private static final Map> paramFlowRules = new ConcurrentHashMap<>(); private final static RulePropertyListener PROPERTY_LISTENER = new RulePropertyListener(); private static SentinelProperty> currentProperty = new DynamicSentinelProperty<>(); @@ -83,7 +80,7 @@ public static List getRulesOfResource(String resourceName) { } public static boolean hasRules(String resourceName) { - Set rules = paramFlowRules.get(resourceName); + List rules = paramFlowRules.get(resourceName); return rules != null && !rules.isEmpty(); } @@ -94,7 +91,7 @@ public static boolean hasRules(String resourceName) { */ public static List getRules() { List rules = new ArrayList<>(); - for (Map.Entry> entry : paramFlowRules.entrySet()) { + for (Map.Entry> entry : paramFlowRules.entrySet()) { rules.addAll(entry.getValue()); } return rules; @@ -104,61 +101,38 @@ static class RulePropertyListener implements PropertyListener list) { - Map> rules = aggregateHotParamRules(list); + Map> rules = aggregateAndPrepareParamRules(list); if (rules != null) { paramFlowRules.clear(); paramFlowRules.putAll(rules); } - RecordLog.info("[ParamFlowRuleManager] Hot spot parameter flow rules received: " + paramFlowRules); + RecordLog.info("[ParamFlowRuleManager] Parameter flow rules received: " + paramFlowRules); } @Override public void configLoad(List list) { - Map> rules = aggregateHotParamRules(list); + Map> rules = aggregateAndPrepareParamRules(list); if (rules != null) { paramFlowRules.clear(); paramFlowRules.putAll(rules); } - RecordLog.info("[ParamFlowRuleManager] Hot spot parameter flow rules received: " + paramFlowRules); + RecordLog.info("[ParamFlowRuleManager] Parameter flow rules received: " + paramFlowRules); } - private Map> aggregateHotParamRules(List list) { - Map> newRuleMap = new ConcurrentHashMap<>(); - - if (list == null || list.isEmpty()) { + private Map> aggregateAndPrepareParamRules(List list) { + Map> newRuleMap = ParamFlowRuleUtil.buildParamRuleMap(list); + if (newRuleMap == null || newRuleMap.isEmpty()) { // No parameter flow rules, so clear all the metrics. - ParamFlowSlot.getMetricsMap().clear(); + ParameterMetricStorage.getMetricsMap().clear(); RecordLog.info("[ParamFlowRuleManager] No parameter flow rules, clearing all parameter metrics"); return newRuleMap; } - for (ParamFlowRule rule : list) { - if (!ParamFlowRuleUtil.isValidRule(rule)) { - RecordLog.warn("[ParamFlowRuleManager] Ignoring invalid rule when loading new rules: " + rule); - continue; - } - - if (StringUtil.isBlank(rule.getLimitApp())) { - rule.setLimitApp(RuleConstant.LIMIT_APP_DEFAULT); - } - - ParamFlowRuleUtil.fillExceptionFlowItems(rule); - - String resourceName = rule.getResource(); - Set ruleSet = newRuleMap.get(resourceName); - if (ruleSet == null) { - ruleSet = new HashSet<>(); - newRuleMap.put(resourceName, ruleSet); - } - - ruleSet.add(rule); - } - - // Clear unused hot param metrics. + // Clear unused parameter metrics. Set previousResources = paramFlowRules.keySet(); for (String resource : previousResources) { if (!newRuleMap.containsKey(resource)) { - ParamFlowSlot.clearHotParamMetricForName(resource); + ParameterMetricStorage.clearParamMetricForResource(resource); } } @@ -166,6 +140,5 @@ private Map> aggregateHotParamRules(List= 0 && rule.getGrade() >= 0 && rule.getParamIdx() != null @@ -55,6 +69,11 @@ public static boolean validClusterRuleId(Long id) { return id != null && id > 0; } + /** + * Fill the parameter rule with parsed items. + * + * @param rule valid parameter rule + */ public static void fillExceptionFlowItems(ParamFlowRule rule) { if (rule != null) { if (rule.getParamFlowItemList() == null) { @@ -66,11 +85,111 @@ public static void fillExceptionFlowItems(ParamFlowRule rule) { } } + /** + * Build the flow rule map from raw list of flow rules, grouping by resource name. + * + * @param list raw list of flow rules + * @return constructed new flow rule map; empty map if list is null or empty, or no valid rules + * @since 1.6.1 + */ + public static Map> buildParamRuleMap(List list) { + return buildParamRuleMap(list, null); + } + + /** + * Build the parameter flow rule map from raw list of rules, grouping by resource name. + * + * @param list raw list of parameter flow rules + * @param filter rule filter + * @return constructed new parameter flow rule map; empty map if list is null or empty, or no wanted rules + * @since 1.6.1 + */ + public static Map> buildParamRuleMap(List list, + Predicate filter) { + return buildParamRuleMap(list, filter, true); + } + + /** + * Build the parameter flow rule map from raw list of rules, grouping by resource name. + * + * @param list raw list of parameter flow rules + * @param filter rule filter + * @param shouldSort whether the rules should be sorted + * @return constructed new parameter flow rule map; empty map if list is null or empty, or no wanted rules + * @since 1.6.1 + */ + public static Map> buildParamRuleMap(List list, + Predicate filter, + boolean shouldSort) { + return buildParamRuleMap(list, EXTRACT_RESOURCE, filter, shouldSort); + } + + /** + * Build the rule map from raw list of parameter flow rules, grouping by provided group function. + * + * @param list raw list of parameter flow rules + * @param groupFunction grouping function of the map (by key) + * @param filter rule filter + * @param shouldSort whether the rules should be sorted + * @param type of key + * @return constructed new rule map; empty map if list is null or empty, or no wanted rules + * @since 1.6.1 + */ + public static Map> buildParamRuleMap(List list, + Function groupFunction, + Predicate filter, + boolean shouldSort) { + AssertUtil.notNull(groupFunction, "groupFunction should not be null"); + Map> newRuleMap = new ConcurrentHashMap<>(); + if (list == null || list.isEmpty()) { + return newRuleMap; + } + Map> tmpMap = new ConcurrentHashMap<>(); + + for (ParamFlowRule rule : list) { + if (!ParamFlowRuleUtil.isValidRule(rule)) { + RecordLog.warn("[ParamFlowRuleManager] Ignoring invalid rule when loading new rules: " + rule); + continue; + } + if (filter != null && !filter.test(rule)) { + continue; + } + if (StringUtil.isBlank(rule.getLimitApp())) { + rule.setLimitApp(RuleConstant.LIMIT_APP_DEFAULT); + } + + ParamFlowRuleUtil.fillExceptionFlowItems(rule); + + K key = groupFunction.apply(rule); + if (key == null) { + continue; + } + Set flowRules = tmpMap.get(key); + + if (flowRules == null) { + // Use hash set here to remove duplicate rules. + flowRules = new HashSet<>(); + tmpMap.put(key, flowRules); + } + + flowRules.add(rule); + } + for (Entry> entries : tmpMap.entrySet()) { + List rules = new ArrayList<>(entries.getValue()); + if (shouldSort) { + // TODO: Sort the rules. + } + newRuleMap.put(entries.getKey(), rules); + } + + return newRuleMap; + } + static Map parseHotItems(List items) { - Map itemMap = new HashMap(); if (items == null || items.isEmpty()) { - return itemMap; + return new HashMap<>(); } + Map itemMap = new HashMap<>(items.size()); for (ParamFlowItem item : items) { // Value should not be null. Object value; @@ -120,5 +239,12 @@ static Object parseItemValue(String value, String classType) { return value; } + private static final Function EXTRACT_RESOURCE = new Function() { + @Override + public String apply(ParamFlowRule rule) { + return rule.getResource(); + } + }; + private ParamFlowRuleUtil() {} } diff --git a/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowSlot.java b/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowSlot.java index 77129dbf78..3be42249d6 100644 --- a/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowSlot.java +++ b/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowSlot.java @@ -15,20 +15,14 @@ */ package com.alibaba.csp.sentinel.slots.block.flow.param; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -import com.alibaba.csp.sentinel.EntryType; import com.alibaba.csp.sentinel.context.Context; -import com.alibaba.csp.sentinel.log.RecordLog; 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.slotchain.StringResourceWrapper; import com.alibaba.csp.sentinel.slots.block.BlockException; import com.alibaba.csp.sentinel.spi.SpiOrder; -import com.alibaba.csp.sentinel.util.StringUtil; + +import java.util.List; /** * A processor slot that is responsible for flow control by frequent ("hot spot") parameters. @@ -40,13 +34,6 @@ @SpiOrder(-5000) public class ParamFlowSlot extends AbstractLinkedProcessorSlot { - private static final Map metricsMap = new ConcurrentHashMap<>(); - - /** - * Lock for a specific resource. - */ - private final Object LOCK = new Object(); - @Override public void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, boolean prioritized, Object... args) throws Throwable { @@ -70,7 +57,7 @@ void applyRealParamIdx(/*@NonNull*/ ParamFlowRule rule, int length) { if (-paramIdx <= length) { rule.setParamIdx(length + paramIdx); } else { - // illegal index, give it a illegal positive value, latter rule check will pass + // Illegal index, give it a illegal positive value, latter rule checking will pass. rule.setParamIdx(-paramIdx); } } @@ -89,7 +76,7 @@ void checkFlow(ResourceWrapper resourceWrapper, int count, Object... args) throw applyRealParamIdx(rule, args.length); // Initialize the parameter metrics. - initHotParamMetricsFor(resourceWrapper, rule); + ParameterMetricStorage.initParamMetricsFor(resourceWrapper, rule); if (!ParamFlowChecker.passCheck(resourceWrapper, rule, count, args)) { String triggeredParam = ""; @@ -101,60 +88,4 @@ void checkFlow(ResourceWrapper resourceWrapper, int count, Object... args) throw } } } - - /** - * Init the parameter metric and index map for given resource. - * Package-private for test. - * - * @param resourceWrapper resource to init - * @param rule relevant rule - */ - void initHotParamMetricsFor(ResourceWrapper resourceWrapper, /*@Valid*/ ParamFlowRule rule) { - ParameterMetric metric; - // Assume that the resource is valid. - if ((metric = metricsMap.get(resourceWrapper)) == null) { - synchronized (LOCK) { - if ((metric = metricsMap.get(resourceWrapper)) == null) { - metric = new ParameterMetric(); - metricsMap.put(resourceWrapper, metric); - RecordLog.info("[ParamFlowSlot] Creating parameter metric for: " + resourceWrapper.getName()); - } - } - } - metric.initialize(rule); - } - - public static ParameterMetric getParamMetric(ResourceWrapper resourceWrapper) { - if (resourceWrapper == null || resourceWrapper.getName() == null) { - return null; - } - return metricsMap.get(resourceWrapper); - } - - public static ParameterMetric getHotParamMetricForName(String resourceName) { - if (StringUtil.isBlank(resourceName)) { - return null; - } - for (EntryType nodeType : EntryType.values()) { - ParameterMetric metric = metricsMap.get(new StringResourceWrapper(resourceName, nodeType)); - if (metric != null) { - return metric; - } - } - return null; - } - - static void clearHotParamMetricForName(String resourceName) { - if (StringUtil.isBlank(resourceName)) { - return; - } - for (EntryType nodeType : EntryType.values()) { - metricsMap.remove(new StringResourceWrapper(resourceName, nodeType)); - } - RecordLog.info("[ParamFlowSlot] Clearing parameter metric for: " + resourceName); - } - - static Map getMetricsMap() { - return metricsMap; - } } diff --git a/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParameterMetricStorage.java b/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParameterMetricStorage.java new file mode 100644 index 0000000000..5b531bfaa9 --- /dev/null +++ b/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParameterMetricStorage.java @@ -0,0 +1,91 @@ +/* + * 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.slots.block.flow.param; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import com.alibaba.csp.sentinel.log.RecordLog; +import com.alibaba.csp.sentinel.slotchain.ResourceWrapper; +import com.alibaba.csp.sentinel.util.StringUtil; + +/** + * @author Eric Zhao + * @since 1.6.1 + */ +public final class ParameterMetricStorage { + + private static final Map metricsMap = new ConcurrentHashMap<>(); + + /** + * Lock for a specific resource. + */ + private static final Object LOCK = new Object(); + + /** + * Init the parameter metric and index map for given resource. + * Package-private for test. + * + * @param resourceWrapper resource to init + * @param rule relevant rule + */ + public static void initParamMetricsFor(ResourceWrapper resourceWrapper, /*@Valid*/ ParamFlowRule rule) { + if (resourceWrapper == null || resourceWrapper.getName() == null) { + return; + } + String resourceName = resourceWrapper.getName(); + ParameterMetric metric; + // Assume that the resource is valid. + if ((metric = metricsMap.get(resourceName)) == null) { + synchronized (LOCK) { + if ((metric = metricsMap.get(resourceName)) == null) { + metric = new ParameterMetric(); + metricsMap.put(resourceWrapper.getName(), metric); + RecordLog.info("[ParameterMetricStorage] Creating parameter metric for: " + resourceWrapper.getName()); + } + } + } + metric.initialize(rule); + } + + public static ParameterMetric getParamMetric(ResourceWrapper resourceWrapper) { + if (resourceWrapper == null || resourceWrapper.getName() == null) { + return null; + } + return metricsMap.get(resourceWrapper.getName()); + } + + public static ParameterMetric getParamMetricForResource(String resourceName) { + if (resourceName == null) { + return null; + } + return metricsMap.get(resourceName); + } + + public static void clearParamMetricForResource(String resourceName) { + if (StringUtil.isBlank(resourceName)) { + return; + } + metricsMap.remove(resourceName); + RecordLog.info("[ParameterMetricStorage] Clearing parameter metric for: " + resourceName); + } + + static Map getMetricsMap() { + return metricsMap; + } + + private ParameterMetricStorage() {} +} diff --git a/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/statistic/ParamFlowStatisticEntryCallback.java b/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/statistic/ParamFlowStatisticEntryCallback.java index b7c09d81c2..50073ec97a 100644 --- a/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/statistic/ParamFlowStatisticEntryCallback.java +++ b/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/statistic/ParamFlowStatisticEntryCallback.java @@ -20,8 +20,8 @@ import com.alibaba.csp.sentinel.slotchain.ProcessorSlotEntryCallback; import com.alibaba.csp.sentinel.slotchain.ResourceWrapper; import com.alibaba.csp.sentinel.slots.block.BlockException; -import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowSlot; import com.alibaba.csp.sentinel.slots.block.flow.param.ParameterMetric; +import com.alibaba.csp.sentinel.slots.block.flow.param.ParameterMetricStorage; /** * @author Eric Zhao @@ -32,7 +32,7 @@ public class ParamFlowStatisticEntryCallback implements ProcessorSlotEntryCallba @Override public void onPass(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, Object... args) { // The "hot spot" parameter metric is present only if parameter flow rules for the resource exist. - ParameterMetric parameterMetric = ParamFlowSlot.getParamMetric(resourceWrapper); + ParameterMetric parameterMetric = ParameterMetricStorage.getParamMetric(resourceWrapper); if (parameterMetric != null) { parameterMetric.addThreadCount(args); diff --git a/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/statistic/ParamFlowStatisticExitCallback.java b/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/statistic/ParamFlowStatisticExitCallback.java index 1cb63917a6..d3446ff57f 100644 --- a/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/statistic/ParamFlowStatisticExitCallback.java +++ b/sentinel-extension/sentinel-parameter-flow-control/src/main/java/com/alibaba/csp/sentinel/slots/statistic/ParamFlowStatisticExitCallback.java @@ -18,8 +18,8 @@ import com.alibaba.csp.sentinel.context.Context; import com.alibaba.csp.sentinel.slotchain.ProcessorSlotExitCallback; import com.alibaba.csp.sentinel.slotchain.ResourceWrapper; -import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowSlot; import com.alibaba.csp.sentinel.slots.block.flow.param.ParameterMetric; +import com.alibaba.csp.sentinel.slots.block.flow.param.ParameterMetricStorage; /** * @author Eric Zhao @@ -30,7 +30,7 @@ public class ParamFlowStatisticExitCallback implements ProcessorSlotExitCallback @Override public void onExit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) { if (context.getCurEntry().getError() == null) { - ParameterMetric parameterMetric = ParamFlowSlot.getParamMetric(resourceWrapper); + ParameterMetric parameterMetric = ParameterMetricStorage.getParamMetric(resourceWrapper); if (parameterMetric != null) { parameterMetric.decreaseThreadCount(args); diff --git a/sentinel-extension/sentinel-parameter-flow-control/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowCheckerTest.java b/sentinel-extension/sentinel-parameter-flow-control/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowCheckerTest.java index 628b859ffb..70c549f2b8 100644 --- a/sentinel-extension/sentinel-parameter-flow-control/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowCheckerTest.java +++ b/sentinel-extension/sentinel-parameter-flow-control/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowCheckerTest.java @@ -90,7 +90,7 @@ public void testSingleValueCheckQpsWithExceptionItems() throws InterruptedExcept rule.setParsedHotItems(map); ParameterMetric metric = new ParameterMetric(); - ParamFlowSlot.getMetricsMap().put(resourceWrapper, metric); + ParameterMetricStorage.getMetricsMap().put(resourceWrapper.getName(), metric); metric.getRuleTimeCounterMap().put(rule, new ConcurrentLinkedHashMapWrapper(4000)); assertTrue(ParamFlowChecker.passSingleValueCheck(resourceWrapper, rule, 1, valueA)); @@ -127,7 +127,7 @@ public void testSingleValueCheckThreadCountWithExceptionItems() { when(metric.getThreadCount(paramIdx, valueB)).thenReturn(globalThreshold - 1); when(metric.getThreadCount(paramIdx, valueC)).thenReturn(globalThreshold - 1); when(metric.getThreadCount(paramIdx, valueD)).thenReturn(globalThreshold + 1); - ParamFlowSlot.getMetricsMap().put(resourceWrapper, metric); + ParameterMetricStorage.getMetricsMap().put(resourceWrapper.getName(), metric); assertTrue(ParamFlowChecker.passSingleValueCheck(resourceWrapper, rule, 1, valueA)); assertFalse(ParamFlowChecker.passSingleValueCheck(resourceWrapper, rule, 1, valueB)); @@ -158,7 +158,7 @@ public void testPassLocalCheckForCollection() throws InterruptedException { String v1 = "a", v2 = "B", v3 = "Cc"; List list = Arrays.asList(v1, v2, v3); ParameterMetric metric = new ParameterMetric(); - ParamFlowSlot.getMetricsMap().put(resourceWrapper, metric); + ParameterMetricStorage.getMetricsMap().put(resourceWrapper.getName(), metric); metric.getRuleTimeCounterMap().put(rule, new ConcurrentLinkedHashMapWrapper(4000)); metric.getRuleTokenCounterMap().put(rule, new ConcurrentLinkedHashMapWrapper(4000)); @@ -181,7 +181,7 @@ public void testPassLocalCheckForArray() throws InterruptedException { String v1 = "a", v2 = "B", v3 = "Cc"; Object arr = new String[] {v1, v2, v3}; ParameterMetric metric = new ParameterMetric(); - ParamFlowSlot.getMetricsMap().put(resourceWrapper, metric); + ParameterMetricStorage.getMetricsMap().put(resourceWrapper.getName(), metric); metric.getRuleTimeCounterMap().put(rule, new ConcurrentLinkedHashMapWrapper(4000)); assertTrue(ParamFlowChecker.passCheck(resourceWrapper, rule, 1, arr)); @@ -190,11 +190,11 @@ public void testPassLocalCheckForArray() throws InterruptedException { @Before public void setUp() throws Exception { - ParamFlowSlot.getMetricsMap().clear(); + ParameterMetricStorage.getMetricsMap().clear(); } @After public void tearDown() throws Exception { - ParamFlowSlot.getMetricsMap().clear(); + ParameterMetricStorage.getMetricsMap().clear(); } } \ No newline at end of file diff --git a/sentinel-extension/sentinel-parameter-flow-control/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowDefaultCheckerTest.java b/sentinel-extension/sentinel-parameter-flow-control/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowDefaultCheckerTest.java index f28bd1c062..fca4fb1c4d 100644 --- a/sentinel-extension/sentinel-parameter-flow-control/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowDefaultCheckerTest.java +++ b/sentinel-extension/sentinel-parameter-flow-control/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowDefaultCheckerTest.java @@ -1,3 +1,18 @@ +/* + * 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.slots.block.flow.param; import static org.junit.Assert.assertEquals; @@ -41,7 +56,7 @@ public void testParamFlowDefaultCheckSingleQps() { String valueA = "valueA"; ParameterMetric metric = new ParameterMetric(); - ParamFlowSlot.getMetricsMap().put(resourceWrapper, metric); + ParameterMetricStorage.getMetricsMap().put(resourceWrapper.getName(), metric); metric.getRuleTimeCounterMap().put(rule, new ConcurrentLinkedHashMapWrapper(4000)); metric.getRuleTokenCounterMap().put(rule, new ConcurrentLinkedHashMapWrapper(4000)); @@ -81,7 +96,7 @@ public void testParamFlowDefaultCheckSingleQpsWithBurst() throws InterruptedExce String valueA = "valueA"; ParameterMetric metric = new ParameterMetric(); - ParamFlowSlot.getMetricsMap().put(resourceWrapper, metric); + ParameterMetricStorage.getMetricsMap().put(resourceWrapper.getName(), metric); metric.getRuleTimeCounterMap().put(rule, new ConcurrentLinkedHashMapWrapper(4000)); metric.getRuleTokenCounterMap().put(rule, new ConcurrentLinkedHashMapWrapper(4000)); @@ -151,7 +166,7 @@ public void testParamFlowDefaultCheckQpsInDifferentDuration() throws Interrupted String valueA = "helloWorld"; ParameterMetric metric = new ParameterMetric(); - ParamFlowSlot.getMetricsMap().put(resourceWrapper, metric); + ParameterMetricStorage.getMetricsMap().put(resourceWrapper.getName(), metric); metric.getRuleTimeCounterMap().put(rule, new ConcurrentLinkedHashMapWrapper(4000)); metric.getRuleTokenCounterMap().put(rule, new ConcurrentLinkedHashMapWrapper(4000)); @@ -204,7 +219,7 @@ public void testParamFlowDefaultCheckSingleValueCheckQpsMultipleThreads() throws final String valueA = "valueA"; ParameterMetric metric = new ParameterMetric(); - ParamFlowSlot.getMetricsMap().put(resourceWrapper, metric); + ParameterMetricStorage.getMetricsMap().put(resourceWrapper.getName(), metric); metric.getRuleTimeCounterMap().put(rule, new ConcurrentLinkedHashMapWrapper(4000)); metric.getRuleTokenCounterMap().put(rule, new ConcurrentLinkedHashMapWrapper(4000)); @@ -270,11 +285,11 @@ public void run() { @Before public void setUp() throws Exception { - ParamFlowSlot.getMetricsMap().clear(); + ParameterMetricStorage.getMetricsMap().clear(); } @After public void tearDown() throws Exception { - ParamFlowSlot.getMetricsMap().clear(); + ParameterMetricStorage.getMetricsMap().clear(); } } diff --git a/sentinel-extension/sentinel-parameter-flow-control/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRuleManagerTest.java b/sentinel-extension/sentinel-parameter-flow-control/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRuleManagerTest.java index 58af635873..8241517b60 100644 --- a/sentinel-extension/sentinel-parameter-flow-control/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRuleManagerTest.java +++ b/sentinel-extension/sentinel-parameter-flow-control/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowRuleManagerTest.java @@ -19,8 +19,6 @@ import java.util.Collections; import java.util.List; -import com.alibaba.csp.sentinel.EntryType; -import com.alibaba.csp.sentinel.slotchain.StringResourceWrapper; import com.alibaba.csp.sentinel.slots.block.RuleConstant; import org.junit.After; @@ -40,33 +38,36 @@ public class ParamFlowRuleManagerTest { @Before public void setUp() { ParamFlowRuleManager.loadRules(null); + ParameterMetricStorage.getMetricsMap().clear(); } @After public void tearDown() { ParamFlowRuleManager.loadRules(null); + ParameterMetricStorage.getMetricsMap().clear(); } @Test - public void testLoadHotParamRulesClearingUnusedMetrics() { + public void testLoadParamRulesClearingUnusedMetrics() { final String resA = "resA"; ParamFlowRule ruleA = new ParamFlowRule(resA) .setCount(1) .setParamIdx(0); ParamFlowRuleManager.loadRules(Collections.singletonList(ruleA)); - ParamFlowSlot.getMetricsMap().put(new StringResourceWrapper(resA, EntryType.IN), new ParameterMetric()); - assertNotNull(ParamFlowSlot.getHotParamMetricForName(resA)); + ParameterMetricStorage.getMetricsMap().put(resA, new ParameterMetric()); + assertNotNull(ParameterMetricStorage.getParamMetricForResource(resA)); final String resB = "resB"; ParamFlowRule ruleB = new ParamFlowRule(resB) .setCount(2) .setParamIdx(1); ParamFlowRuleManager.loadRules(Collections.singletonList(ruleB)); - assertNull("The unused hot param metric should be cleared", ParamFlowSlot.getHotParamMetricForName(resA)); + assertNull("The unused hot param metric should be cleared", + ParameterMetricStorage.getParamMetricForResource(resA)); } @Test - public void testLoadHotParamRulesAndGet() { + public void testLoadParamRulesAndGet() { final String resA = "abc"; final String resB = "foo"; final String resC = "baz"; @@ -107,4 +108,4 @@ public void testLoadHotParamRulesAndGet() { assertTrue(allRules.contains(ruleC)); assertTrue(allRules.contains(ruleD)); } -} \ No newline at end of file +} diff --git a/sentinel-extension/sentinel-parameter-flow-control/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowSlotTest.java b/sentinel-extension/sentinel-parameter-flow-control/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowSlotTest.java index df59432b08..0d9838449e 100644 --- a/sentinel-extension/sentinel-parameter-flow-control/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowSlotTest.java +++ b/sentinel-extension/sentinel-parameter-flow-control/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowSlotTest.java @@ -82,7 +82,7 @@ public void testEntryWhenParamFlowRuleNotExists() throws Throwable { ResourceWrapper resourceWrapper = new StringResourceWrapper(resourceName, EntryType.IN); paramFlowSlot.entry(null, resourceWrapper, null, 1, false, "abc"); // The parameter metric instance will not be created. - assertNull(ParamFlowSlot.getParamMetric(resourceWrapper)); + assertNull(ParameterMetricStorage.getParamMetric(resourceWrapper)); } @Test @@ -106,7 +106,7 @@ public void testEntryWhenParamFlowExists() throws Throwable { map.put(argToGo, new AtomicLong(TimeUtil.currentTimeMillis())); // Insert the mock metric to control pass or block. - ParamFlowSlot.getMetricsMap().put(resourceWrapper, metric); + ParameterMetricStorage.getMetricsMap().put(resourceWrapper.getName(), metric); // The first entry will pass. paramFlowSlot.entry(null, resourceWrapper, null, 1, false, argToGo); @@ -121,48 +121,16 @@ public void testEntryWhenParamFlowExists() throws Throwable { fail("The second entry should be blocked"); } - @Test - public void testGetNullParamMetric() { - assertNull(ParamFlowSlot.getParamMetric(null)); - } - - @Test - public void testInitParamMetrics() { - - ParamFlowRule rule = new ParamFlowRule(); - rule.setParamIdx(1); - int index = 1; - String resourceName = "res-" + System.currentTimeMillis(); - ResourceWrapper resourceWrapper = new StringResourceWrapper(resourceName, EntryType.IN); - - assertNull(ParamFlowSlot.getParamMetric(resourceWrapper)); - - paramFlowSlot.initHotParamMetricsFor(resourceWrapper, rule); - ParameterMetric metric = ParamFlowSlot.getParamMetric(resourceWrapper); - assertNotNull(metric); - assertNotNull(metric.getRuleTimeCounterMap().get(rule)); - assertNotNull(metric.getThreadCountMap().get(index)); - - // Duplicate init. - paramFlowSlot.initHotParamMetricsFor(resourceWrapper, rule); - assertSame(metric, ParamFlowSlot.getParamMetric(resourceWrapper)); - - ParamFlowRule rule2 = new ParamFlowRule(); - rule2.setParamIdx(1); - assertSame(metric, ParamFlowSlot.getParamMetric(resourceWrapper)); - - } - @Before - public void setUp() throws Exception { + public void setUp() { ParamFlowRuleManager.loadRules(null); - ParamFlowSlot.getMetricsMap().clear(); + ParameterMetricStorage.getMetricsMap().clear(); } @After - public void tearDown() throws Exception { + public void tearDown() { // Clean the metrics map. - ParamFlowSlot.getMetricsMap().clear(); ParamFlowRuleManager.loadRules(null); + ParameterMetricStorage.getMetricsMap().clear(); } -} \ No newline at end of file +} diff --git a/sentinel-extension/sentinel-parameter-flow-control/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowThrottleRateLimitingCheckerTest.java b/sentinel-extension/sentinel-parameter-flow-control/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowThrottleRateLimitingCheckerTest.java index 2f2fc3f8d8..8d9cfde48a 100644 --- a/sentinel-extension/sentinel-parameter-flow-control/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowThrottleRateLimitingCheckerTest.java +++ b/sentinel-extension/sentinel-parameter-flow-control/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParamFlowThrottleRateLimitingCheckerTest.java @@ -1,7 +1,20 @@ +/* + * 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.slots.block.flow.param; -import static org.junit.Assert.assertEquals; - import java.util.Random; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -19,6 +32,8 @@ import com.alibaba.csp.sentinel.slots.statistic.cache.ConcurrentLinkedHashMapWrapper; import com.alibaba.csp.sentinel.util.TimeUtil; +import static org.junit.Assert.assertEquals; + /** * @author jialiang.linjl */ @@ -41,7 +56,7 @@ public void testSingleValueThrottleCheckQps() throws Exception { String valueA = "valueA"; ParameterMetric metric = new ParameterMetric(); - ParamFlowSlot.getMetricsMap().put(resourceWrapper, metric); + ParameterMetricStorage.getMetricsMap().put(resourceWrapper.getName(), metric); metric.getRuleTimeCounterMap().put(rule, new ConcurrentLinkedHashMapWrapper(4000)); long currentTime = TimeUtil.currentTimeMillis(); @@ -85,7 +100,7 @@ public void testSingleValueThrottleCheckQpsMultipleThreads() throws Exception { final String valueA = "valueA"; ParameterMetric metric = new ParameterMetric(); - ParamFlowSlot.getMetricsMap().put(resourceWrapper, metric); + ParameterMetricStorage.getMetricsMap().put(resourceWrapper.getName(), metric); metric.getRuleTimeCounterMap().put(rule, new ConcurrentLinkedHashMapWrapper(4000)); int threadCount = 40; @@ -154,11 +169,11 @@ public void run() { @Before public void setUp() throws Exception { - ParamFlowSlot.getMetricsMap().clear(); + ParameterMetricStorage.getMetricsMap().clear(); } @After public void tearDown() throws Exception { - ParamFlowSlot.getMetricsMap().clear(); + ParameterMetricStorage.getMetricsMap().clear(); } } diff --git a/sentinel-extension/sentinel-parameter-flow-control/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParameterMetricStorageTest.java b/sentinel-extension/sentinel-parameter-flow-control/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParameterMetricStorageTest.java new file mode 100644 index 0000000000..ba2fd75ba6 --- /dev/null +++ b/sentinel-extension/sentinel-parameter-flow-control/src/test/java/com/alibaba/csp/sentinel/slots/block/flow/param/ParameterMetricStorageTest.java @@ -0,0 +1,72 @@ +/* + * 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.slots.block.flow.param; + +import com.alibaba.csp.sentinel.EntryType; +import com.alibaba.csp.sentinel.slotchain.ResourceWrapper; +import com.alibaba.csp.sentinel.slotchain.StringResourceWrapper; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * @author Eric Zhao + */ +public class ParameterMetricStorageTest { + + @Test + public void testGetNullParamMetric() { + assertNull(ParameterMetricStorage.getParamMetric(null)); + } + + @Test + public void testInitParamMetrics() { + ParamFlowRule rule = new ParamFlowRule(); + rule.setParamIdx(1); + int index = 1; + String resourceName = "res-" + System.currentTimeMillis(); + ResourceWrapper resourceWrapper = new StringResourceWrapper(resourceName, EntryType.IN); + + assertNull(ParameterMetricStorage.getParamMetric(resourceWrapper)); + + ParameterMetricStorage.initParamMetricsFor(resourceWrapper, rule); + ParameterMetric metric = ParameterMetricStorage.getParamMetric(resourceWrapper); + assertNotNull(metric); + assertNotNull(metric.getRuleTimeCounterMap().get(rule)); + assertNotNull(metric.getThreadCountMap().get(index)); + + // Duplicate init. + ParameterMetricStorage.initParamMetricsFor(resourceWrapper, rule); + assertSame(metric, ParameterMetricStorage.getParamMetric(resourceWrapper)); + + ParamFlowRule rule2 = new ParamFlowRule(); + rule2.setParamIdx(1); + assertSame(metric, ParameterMetricStorage.getParamMetric(resourceWrapper)); + } + + @Before + public void setUp() { + ParameterMetricStorage.getMetricsMap().clear(); + } + + @After + public void tearDown() { + ParameterMetricStorage.getMetricsMap().clear(); + } +} diff --git a/sentinel-transport/pom.xml b/sentinel-transport/pom.xml index 76eae99ef1..e53f957103 100755 --- a/sentinel-transport/pom.xml +++ b/sentinel-transport/pom.xml @@ -6,7 +6,7 @@ com.alibaba.csp sentinel-parent - 1.6.0-SNAPSHOT + 1.7.0-SNAPSHOT sentinel-transport The transport module of Sentinel diff --git a/sentinel-transport/sentinel-transport-common/pom.xml b/sentinel-transport/sentinel-transport-common/pom.xml index a2a16707a5..7206e440ce 100755 --- a/sentinel-transport/sentinel-transport-common/pom.xml +++ b/sentinel-transport/sentinel-transport-common/pom.xml @@ -5,7 +5,7 @@ com.alibaba.csp sentinel-transport - 1.6.0-SNAPSHOT + 1.7.0-SNAPSHOT 4.0.0 jar diff --git a/sentinel-transport/sentinel-transport-common/src/main/java/com/alibaba/csp/sentinel/command/handler/SendMetricCommandHandler.java b/sentinel-transport/sentinel-transport-common/src/main/java/com/alibaba/csp/sentinel/command/handler/SendMetricCommandHandler.java index e841004c42..b3692e14d9 100755 --- a/sentinel-transport/sentinel-transport-common/src/main/java/com/alibaba/csp/sentinel/command/handler/SendMetricCommandHandler.java +++ b/sentinel-transport/sentinel-transport-common/src/main/java/com/alibaba/csp/sentinel/command/handler/SendMetricCommandHandler.java @@ -15,18 +15,22 @@ */ package com.alibaba.csp.sentinel.command.handler; +import java.util.ArrayList; import java.util.List; +import com.alibaba.csp.sentinel.Constants; import com.alibaba.csp.sentinel.command.CommandHandler; import com.alibaba.csp.sentinel.command.CommandRequest; import com.alibaba.csp.sentinel.command.CommandResponse; import com.alibaba.csp.sentinel.command.annotation.CommandMapping; -import com.alibaba.csp.sentinel.util.PidUtil; import com.alibaba.csp.sentinel.config.SentinelConfig; -import com.alibaba.csp.sentinel.util.StringUtil; import com.alibaba.csp.sentinel.node.metric.MetricNode; import com.alibaba.csp.sentinel.node.metric.MetricSearcher; import com.alibaba.csp.sentinel.node.metric.MetricWriter; +import com.alibaba.csp.sentinel.slots.system.SystemRuleManager; +import com.alibaba.csp.sentinel.util.PidUtil; +import com.alibaba.csp.sentinel.util.StringUtil; +import com.alibaba.csp.sentinel.util.TimeUtil; /** * Retrieve and aggregate {@link MetricNode} metrics. @@ -34,7 +38,8 @@ * @author leyou * @author Eric Zhao */ -@CommandMapping(name = "metric", desc = "get and aggregate metrics, accept param: startTime={startTime}&endTime={endTime}&maxLines={maxLines}&identify={resourceName}") +@CommandMapping(name = "metric", desc = "get and aggregate metrics, accept param: " + + "startTime={startTime}&endTime={endTime}&maxLines={maxLines}&identify={resourceName}") public class SendMetricCommandHandler implements CommandHandler { private MetricSearcher searcher; @@ -83,9 +88,11 @@ public CommandResponse handle(CommandRequest request) { } catch (Exception ex) { return CommandResponse.ofFailure(new RuntimeException("Error when retrieving metrics", ex)); } - if (list == null) { - return CommandResponse.ofSuccess("No metrics"); + list = new ArrayList<>(); + } + if (StringUtil.isBlank(identity)) { + addCpuUsageAndLoad(list); } StringBuilder sb = new StringBuilder(); for (MetricNode node : list) { @@ -93,4 +100,43 @@ public CommandResponse handle(CommandRequest request) { } return CommandResponse.ofSuccess(sb.toString()); } + + /** + * add current cpu usage and load to the metric list. + * + * @param list metric list, should not be null + */ + private void addCpuUsageAndLoad(List list) { + long time = TimeUtil.currentTimeMillis() / 1000 * 1000; + double load = SystemRuleManager.getCurrentSystemAvgLoad(); + double usage = SystemRuleManager.getCurrentCpuUsage(); + if (load > 0) { + MetricNode loadNode = toNode(load, time, Constants.SYSTEM_LOAD_RESOURCE_NAME); + list.add(loadNode); + } + if (usage > 0) { + MetricNode usageNode = toNode(usage, time, Constants.CPU_USAGE_RESOURCE_NAME); + list.add(usageNode); + } + } + + /** + * transfer the value to a MetricNode, the value will multiply 10000 then truncate + * to long value, and as the {@link MetricNode#passQps}. + *

+ * This is an eclectic scheme before we have a standard metric format. + *

+ * + * @param value value to save. + * @param ts timestamp + * @param resource resource name. + * @return a MetricNode represents the value. + */ + private MetricNode toNode(double value, long ts, String resource) { + MetricNode node = new MetricNode(); + node.setPassQps((long)(value * 10000)); + node.setTimestamp(ts); + node.setResource(resource); + return node; + } } diff --git a/sentinel-transport/sentinel-transport-netty-http/pom.xml b/sentinel-transport/sentinel-transport-netty-http/pom.xml index 65a9e47148..928f0db503 100755 --- a/sentinel-transport/sentinel-transport-netty-http/pom.xml +++ b/sentinel-transport/sentinel-transport-netty-http/pom.xml @@ -5,7 +5,7 @@ com.alibaba.csp sentinel-transport - 1.6.0-SNAPSHOT + 1.7.0-SNAPSHOT 4.0.0 sentinel-transport-netty-http diff --git a/sentinel-transport/sentinel-transport-simple-http/pom.xml b/sentinel-transport/sentinel-transport-simple-http/pom.xml index e9c84359c5..5712b46343 100755 --- a/sentinel-transport/sentinel-transport-simple-http/pom.xml +++ b/sentinel-transport/sentinel-transport-simple-http/pom.xml @@ -5,7 +5,7 @@ sentinel-transport com.alibaba.csp - 1.6.0-SNAPSHOT + 1.7.0-SNAPSHOT 4.0.0