diff --git a/gateway-support-lib/pom.xml b/gateway-support-lib/pom.xml
index 0465afa..d66586a 100644
--- a/gateway-support-lib/pom.xml
+++ b/gateway-support-lib/pom.xml
@@ -45,6 +45,11 @@
+
+ com.alibaba.csp
+ sentinel-core
+ 1.8.0
+
\ No newline at end of file
diff --git a/gateway-support-lib/src/main/java/cn/axzo/foundation/gateway/support/plugin/impl/RequestFilterHook.java b/gateway-support-lib/src/main/java/cn/axzo/foundation/gateway/support/plugin/impl/RequestFilterHook.java
index 20aafdc..719175b 100644
--- a/gateway-support-lib/src/main/java/cn/axzo/foundation/gateway/support/plugin/impl/RequestFilterHook.java
+++ b/gateway-support-lib/src/main/java/cn/axzo/foundation/gateway/support/plugin/impl/RequestFilterHook.java
@@ -1,5 +1,6 @@
package cn.axzo.foundation.gateway.support.plugin.impl;
+import cn.axzo.foundation.exception.BusinessException;
import cn.axzo.foundation.gateway.support.entity.GateResponse;
import cn.axzo.foundation.gateway.support.entity.ProxyContext;
import cn.axzo.foundation.gateway.support.entity.RequestContext;
@@ -8,6 +9,13 @@ import cn.axzo.foundation.gateway.support.plugin.impl.filters.*;
import cn.axzo.foundation.util.FastjsonUtils;
import cn.axzo.foundation.web.support.rpc.RequestParams;
import cn.axzo.foundation.web.support.rpc.RpcClient;
+import com.alibaba.csp.sentinel.Entry;
+import com.alibaba.csp.sentinel.EntryType;
+import com.alibaba.csp.sentinel.SphU;
+import com.alibaba.csp.sentinel.Tracer;
+import com.alibaba.csp.sentinel.slots.block.BlockException;
+import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
+import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Preconditions;
@@ -15,6 +23,7 @@ import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
import lombok.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
@@ -74,6 +83,17 @@ public class RequestFilterHook implements ProxyHook {
.put("requestParamCheckFilter", new RequestParamCheckFilter())
.put("moveInputFieldFilter", new MoveInputFieldFilter())
.put("keyNoQueryFilter", new KeyNoQueryFilter())
+ .put("emptyFilter", new RequestFilter() {
+ @Override
+ public JSON filterIn(RequestContext reqContext, JSON params, JSONObject config) {
+ return RequestFilter.super.filterIn(reqContext, params, config);
+ }
+
+ @Override
+ public JSON filterOut(RequestContext reqContext, JSON response, JSONObject config) {
+ return RequestFilter.super.filterOut(reqContext, response, config);
+ }
+ })
.build();
@Builder
@@ -103,9 +123,32 @@ public class RequestFilterHook implements ProxyHook {
for (FilterBean bean : beans) {
RequestFilter requestFilter = filterBeanResolver.apply(bean.getName());
Preconditions.checkState(requestFilter != null, bean.getName() + " 没在系统注册");
- requestBody = requestFilter.filterIn(reqContext, requestBody, bean.getConfig());
- }
+ FallBackConfig fallBackConfig = bean.getFallBackConfig();
+ Entry entry = null;
+ try {
+ if (fallBackConfig != null) {
+ String resourceName = bean.getResourceName(reqContext.getRequestURI());
+ fallBackConfig.registerRule(resourceName);
+ entry = SphU.entry(resourceName, EntryType.IN);
+ }
+ requestBody = requestFilter.filterIn(reqContext, requestBody, bean.getConfig());
+ } catch (BlockException ex) {
+ //降级处理
+ requestBody = filterBeanResolver.apply(fallBackConfig.getFallBackFilter())
+ .filterIn(reqContext, requestBody, fallBackConfig.getConfig());
+ } catch (Exception ex) {
+ //非业务异常记录trace
+ if (!BusinessException.class.isAssignableFrom(ex.getClass())) {
+ Tracer.traceEntry(ex, entry);
+ }
+ throw ex;
+ } finally {
+ if (entry != null) {
+ entry.exit();
+ }
+ }
+ }
// 只支持 json 格式
return ((RequestParams.BodyParams) postParams).toBuilder().content(requestBody).build();
}
@@ -133,7 +176,31 @@ public class RequestFilterHook implements ProxyHook {
for (FilterBean bean : beans) {
RequestFilter requestFilter = filterBeanResolver.apply(bean.getName());
Preconditions.checkState(requestFilter != null, bean.getName() + " 没在系统注册");
- responseBody = requestFilter.filterOut(reqContext, responseBody, bean.getConfig());
+ FallBackConfig fallBackConfig = bean.getFallBackConfig();
+
+ Entry entry = null;
+ try {
+ if (fallBackConfig != null) {
+ String resourceName = bean.getResourceName(reqContext.getRequestURI());
+ fallBackConfig.registerRule(resourceName);
+ entry = SphU.entry(resourceName, EntryType.OUT);
+ }
+ responseBody = requestFilter.filterOut(reqContext, responseBody, bean.getConfig());
+ } catch (BlockException ex) {
+ //降级处理
+ responseBody = filterBeanResolver.apply(fallBackConfig.getFallBackFilter())
+ .filterIn(reqContext, responseBody, fallBackConfig.getConfig());
+ } catch (Exception ex) {
+ //非业务异常记录trace
+ if (!BusinessException.class.isAssignableFrom(ex.getClass())) {
+ Tracer.traceEntry(ex, entry);
+ }
+ throw ex;
+ } finally {
+ if (entry != null) {
+ entry.exit();
+ }
+ }
}
GateResponse res = response.toBuilder().build();
@@ -195,5 +262,49 @@ public class RequestFilterHook implements ProxyHook {
private static class FilterBean {
private String name;
private JSONObject config;
+
+ public FallBackConfig getFallBackConfig() {
+ return Optional.ofNullable(config)
+ .flatMap(e -> Optional.ofNullable(e.getJSONObject("fallBack"))
+ .map(fallBack -> fallBack.toJavaObject(FallBackConfig.class)))
+ .orElse(null);
+ }
+
+ public String getResourceName(String requestUrl) {
+ return "RequestFilterHook:" + name + "@" + requestUrl;
+ }
+ }
+
+ @Data
+ @Builder
+ @NoArgsConstructor
+ @AllArgsConstructor
+ private static class FallBackConfig {
+ String fallBackFilter = "emptyFilter";
+ JSONObject config;
+ /** 策略, 支持FLOW_GRADE_QPS慢查询比例, DEGRADE_GRADE_EXCEPTION_RATIO异常比例, DEGRADE_GRADE_EXCEPTION_COUNT异常数 */
+ Integer grade = 2;
+ /** 默认10s内请求大于5个, 同时错误超过一半则降级30s */
+ Double count = 0.5D;
+ Integer timeWindow = 30;
+ Integer minRequestAmount = 5;
+ /** 慢比例有效 */
+ Double slowRatioThreshold = 1.0;
+ Integer statIntervalMs = 10000;
+
+ public void registerRule(String resourceName) {
+ if (DegradeRuleManager.hasConfig(resourceName)) {
+ return;
+ }
+ DegradeRule degradeRule = new DegradeRule(resourceName);
+ degradeRule.setGrade(grade);
+ degradeRule.setCount(count);
+ degradeRule.setTimeWindow(timeWindow);
+ degradeRule.setMinRequestAmount(minRequestAmount);
+ degradeRule.setSlowRatioThreshold(slowRatioThreshold);
+ degradeRule.setStatIntervalMs(statIntervalMs);
+
+ DegradeRuleManager.setRulesForResource(resourceName, ImmutableSet.of(degradeRule));
+ }
}
}
diff --git a/gateway-support-lib/src/test/java/cn/axzo/foundation/gateway/support/plugin/impl/RequestFilterHookTest.java b/gateway-support-lib/src/test/java/cn/axzo/foundation/gateway/support/plugin/impl/RequestFilterHookTest.java
new file mode 100644
index 0000000..64cb226
--- /dev/null
+++ b/gateway-support-lib/src/test/java/cn/axzo/foundation/gateway/support/plugin/impl/RequestFilterHookTest.java
@@ -0,0 +1,57 @@
+package cn.axzo.foundation.gateway.support.plugin.impl;
+
+
+import com.alibaba.csp.sentinel.Entry;
+import com.alibaba.csp.sentinel.EntryType;
+import com.alibaba.csp.sentinel.SphU;
+import com.alibaba.csp.sentinel.Tracer;
+import com.alibaba.csp.sentinel.slots.block.BlockException;
+import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
+import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
+import com.google.common.collect.ImmutableSet;
+
+import java.util.List;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+
+class RequestFilterHookTest {
+
+
+ public static void main(String[] args) {
+
+ String resourceName = "aaaa";
+
+ DegradeRule degradeRule = new DegradeRule(resourceName);
+ degradeRule.setGrade(2);
+ degradeRule.setCount(0.5);
+ degradeRule.setTimeWindow(30);
+ degradeRule.setMinRequestAmount(5);
+ degradeRule.setStatIntervalMs(10);
+
+ DegradeRuleManager.setRulesForResource(resourceName, ImmutableSet.of(degradeRule));
+
+ List blocked = IntStream.range(0, 20).mapToObj(e -> {
+ Entry entry = null;
+ try {
+ entry = SphU.entry(resourceName, EntryType.IN);
+ if (e % 2 == 0) {
+ throw new RuntimeException();
+ }
+ return e;
+ } catch (BlockException ex) {
+ return -1;
+ } catch (Exception ex) {
+ Tracer.traceEntry(ex, entry);
+ return -2;
+ } finally {
+ if (entry != null) {
+ entry.exit();
+ }
+ }
+ })
+ .collect(Collectors.toList());
+
+ System.out.printf(blocked.toString());
+ }
+
+}
\ No newline at end of file