feat: filter级别降级处理

原因
    需要支持降级处理

修改
    修改filterHook, 支持降级
This commit is contained in:
zengxiaobo 2024-07-12 11:50:22 +08:00
parent 35bfdff8e4
commit 06a8b49c54
3 changed files with 176 additions and 3 deletions

View File

@ -45,6 +45,11 @@
</exclusion> </exclusion>
</exclusions> </exclusions>
</dependency> </dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>1.8.0</version>
</dependency>
</dependencies> </dependencies>
</project> </project>

View File

@ -1,5 +1,6 @@
package cn.axzo.foundation.gateway.support.plugin.impl; 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.GateResponse;
import cn.axzo.foundation.gateway.support.entity.ProxyContext; import cn.axzo.foundation.gateway.support.entity.ProxyContext;
import cn.axzo.foundation.gateway.support.entity.RequestContext; 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.util.FastjsonUtils;
import cn.axzo.foundation.web.support.rpc.RequestParams; import cn.axzo.foundation.web.support.rpc.RequestParams;
import cn.axzo.foundation.web.support.rpc.RpcClient; 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.JSON;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.google.common.base.Preconditions; 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.base.Strings;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import lombok.*; import lombok.*;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@ -74,6 +83,17 @@ public class RequestFilterHook implements ProxyHook {
.put("requestParamCheckFilter", new RequestParamCheckFilter()) .put("requestParamCheckFilter", new RequestParamCheckFilter())
.put("moveInputFieldFilter", new MoveInputFieldFilter()) .put("moveInputFieldFilter", new MoveInputFieldFilter())
.put("keyNoQueryFilter", new KeyNoQueryFilter()) .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(); .build();
@Builder @Builder
@ -103,9 +123,32 @@ public class RequestFilterHook implements ProxyHook {
for (FilterBean bean : beans) { for (FilterBean bean : beans) {
RequestFilter requestFilter = filterBeanResolver.apply(bean.getName()); RequestFilter requestFilter = filterBeanResolver.apply(bean.getName());
Preconditions.checkState(requestFilter != null, 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 格式 // 只支持 json 格式
return ((RequestParams.BodyParams) postParams).toBuilder().content(requestBody).build(); return ((RequestParams.BodyParams) postParams).toBuilder().content(requestBody).build();
} }
@ -133,7 +176,31 @@ public class RequestFilterHook implements ProxyHook {
for (FilterBean bean : beans) { for (FilterBean bean : beans) {
RequestFilter requestFilter = filterBeanResolver.apply(bean.getName()); RequestFilter requestFilter = filterBeanResolver.apply(bean.getName());
Preconditions.checkState(requestFilter != null, bean.getName() + " 没在系统注册"); Preconditions.checkState(requestFilter != null, bean.getName() + " 没在系统注册");
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()); 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(); GateResponse res = response.toBuilder().build();
@ -195,5 +262,49 @@ public class RequestFilterHook implements ProxyHook {
private static class FilterBean { private static class FilterBean {
private String name; private String name;
private JSONObject config; 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));
}
} }
} }

View File

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