All Downloads are FREE. Search and download functionalities are using the official Maven repository.

sviolet.slate.common.helper.sentinel.AbstractEzSentinelRuleConfigurer Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2015-2019 S.Violet
 *
 * 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.
 *
 * Project GitHub: https://github.com/shepherdviolet/slate
 * Email: [email protected]
 */

package sviolet.slate.common.helper.sentinel;

import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.slots.system.SystemRule;
import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

import static com.alibaba.csp.sentinel.slots.block.RuleConstant.*;

/**
 * 

说明文档见: https://github.com/shepherdviolet/slate/blob/master/docs/ezsentinel/guide.md

* *

Sentinel官方最佳实践的方案中, 应该是改造Dashboard, 由Dashboard向一个数据源推送规则, 而客户端则连接数据源获取更新. * 而EzSentinel是一个简易的变通方案, 不改造Dashboard, 也不配置客户端数据源. 通过一个大JSON来维护规则(人工), 然后手动 * 将这个JSON配置到Apollo配置中心, 借助Apollo准实时将配置用setter方法注入的特性, 在setter方法中显式地调用Sentinel的 * API来实现规则配置. 而Dashboard仅用于观察(不做配置).

* *

通过EzSentinel配置规则后, 可以在Dashboard的流控规则中看到_ez_开头的资源, 这个是用来观察某个应用规则是否生效/何时生效/ * 失败原因的.

* * @param 规则数据类型 * @author S.Violet */ public abstract class AbstractEzSentinelRuleConfigurer implements EzSentinelRuleConfigurer { private static final DateTimeFormatter TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyyMMdd-HHmmss"); private static final String RULE_VERSION = "_ez_rule_version="; private static final String UPDATE_TIME = "_ez_rule_update_time="; private static final String ERROR_MESSAGE = "_ez_error_msg="; private final Logger logger = LoggerFactory.getLogger(getClass()); /** * 在Apollo配置变化时, setter方法中调用该方法更新规则, 参考{@link EnableEzSentinel} * * @param ruleData 规则数据 */ public void update(T ruleData) { logger.info("EzSentinel | Update sentinel rule start"); //convert Rules rules = null; if (ruleData != null) { try { rules = convertRuleData(ruleData); } catch (Exception e) { handleException("illegal_rule_data", e, ruleData); return; } } //update updateRules(rules); logger.info("EzSentinel | Update sentinel rule finish"); } protected abstract Rules convertRuleData(T ruleData); protected void updateRules(Rules rules) { if (rules == null) { SystemRuleManager.loadRules(Collections.emptyList()); DegradeRuleManager.loadRules(Collections.emptyList()); FlowRuleManager.loadRules(Collections.emptyList()); logger.info("EzSentinel | No sentinel rules (It will clear all previous rules!)"); return; } //System rules if (rules.systemRule != null) { SystemRuleManager.loadRules(Collections.singletonList(rules.systemRule.build())); logger.info("EzSentinel | System rule updated, 1 rules"); } else { SystemRuleManager.loadRules(Collections.emptyList()); logger.info("EzSentinel | System rule updated, 0 rules"); } //Degrade / Flow rules List degradeRules = new LinkedList<>(); List flowRules = new LinkedList<>(); if (rules.getResourceRules() != null && rules.getResourceRules().size() > 0) { Map ruleGroups = rules.getRuleGroups(); if (ruleGroups == null) { handleException("missing_rule_groups", new Exception("Missing 'ruleGroups' in the rule data"), null); return; } for (Map.Entry entry : rules.getResourceRules().entrySet()) { //Entry -> key: resourceName value: ruleGroupId String resource = entry.getKey(); String ruleGroupId = entry.getValue(); RuleGroup ruleGroup = ruleGroups.get(ruleGroupId); if (ruleGroup == null) { handleException("undefined_rule_group", new Exception("Undefined rule group '" + ruleGroupId + "' in ruleGroups, required by resource '" + resource + "'"), null); return; } if (logger.isDebugEnabled()) { logger.debug("EzSentinel | Resource '" + resource + "' -> ruleGroup '" + ruleGroupId + "'"); } //Degrade rules if (ruleGroup.getDegradeRules() != null) { for (DegradeRuleBuilder degradeRuleBuilder : ruleGroup.getDegradeRules()) { degradeRules.add(degradeRuleBuilder.build(resource)); if (logger.isDebugEnabled()) { logger.debug("EzSentinel | Resource '" + resource + "' -> " + degradeRuleBuilder); } } } //Flow rules if (ruleGroup.getFlowRules() != null) { for (FlowRuleBuilder flowRuleBuilder : ruleGroup.getFlowRules()) { flowRules.add(flowRuleBuilder.build(resource)); if (logger.isDebugEnabled()) { logger.debug("EzSentinel | Resource '" + resource + "' -> " + flowRuleBuilder); } } } } } //comment flowRules.add(commentRule(UPDATE_TIME + TIME_FORMATTER.format(LocalDateTime.now()))); flowRules.add(commentRule(RULE_VERSION + rules.getRuleVersion())); //load DegradeRuleManager.loadRules(degradeRules); FlowRuleManager.loadRules(flowRules); logger.info("EzSentinel | Degrade rule updated, " + degradeRules.size() + " rules"); logger.info("EzSentinel | Flow rule updated, " + flowRules.size() + " rules"); } protected void handleException(String errorCode, Exception e, T ruleData){ if (e != null) { logger.error("EzSentinel | Error while updating sentinel rules, " + errorCode + ", " + TIME_FORMATTER.format(LocalDateTime.now()), e); } else { logger.error("EzSentinel | Error while updating sentinel rules, " + errorCode + ", " + TIME_FORMATTER.format(LocalDateTime.now())); } if (ruleData != null) { logger.error("EzSentinel | Rule data:" + ruleData); } List flowRules = FlowRuleManager.getRules(); flowRules.add(commentRule(ERROR_MESSAGE + errorCode + "_" + TIME_FORMATTER.format(LocalDateTime.now()))); FlowRuleManager.loadRules(flowRules); } // Utils ///////////////////////////////////////////////////////////////////////////////////////////// protected static int nullable(Integer value, int fallback){ if (value == null) { return fallback; } return value; } protected static FlowRule commentRule(String comment){ FlowRule flowRule = new FlowRule(comment); flowRule.setCount(0); return flowRule; } // Classes ///////////////////////////////////////////////////////////////////////////////////////////// public static class Rules { private Map ruleGroups; private SystemRuleBuilder systemRule; private Map resourceRules; private String ruleVersion; public Map getRuleGroups() { return ruleGroups; } public void setRuleGroups(Map ruleGroups) { this.ruleGroups = ruleGroups; } public SystemRuleBuilder getSystemRule() { return systemRule; } public void setSystemRule(SystemRuleBuilder systemRule) { this.systemRule = systemRule; } public Map getResourceRules() { return resourceRules; } public void setResourceRules(Map resourceRules) { this.resourceRules = resourceRules; } public String getRuleVersion() { return ruleVersion; } public void setRuleVersion(String ruleVersion) { this.ruleVersion = ruleVersion; } } public static class RuleGroup { private List flowRules; private List degradeRules; public List getFlowRules() { return flowRules; } public void setFlowRules(List flowRules) { this.flowRules = flowRules; } public List getDegradeRules() { return degradeRules; } public void setDegradeRules(List degradeRules) { this.degradeRules = degradeRules; } } public static class FlowRuleBuilder { private double count; private String grade = "THREAD"; private String limitApp = "default"; private String strategy = "DIRECT"; private String controlBehavior = "DEFAULT"; public double getCount() { return count; } public void setCount(double count) { this.count = count; } public String getGrade() { return grade; } public void setGrade(String grade) { this.grade = grade; } public String getLimitApp() { return limitApp; } public void setLimitApp(String limitApp) { this.limitApp = limitApp; } public String getStrategy() { return strategy; } public void setStrategy(String strategy) { this.strategy = strategy; } public String getControlBehavior() { return controlBehavior; } public void setControlBehavior(String controlBehavior) { this.controlBehavior = controlBehavior; } private static final Map FLOW_GRADE_MAP = new HashMap(){{ put("THREAD", FLOW_GRADE_THREAD); put("QPS", FLOW_GRADE_QPS); }}; private static final Map STRATEGY_MAP = new HashMap(){{ put("DIRECT", STRATEGY_DIRECT); put("RELATE", STRATEGY_RELATE); put("CHAIN", STRATEGY_CHAIN); }}; private static final Map CONTROL_BEHAVIOR_MAP = new HashMap(){{ put("DEFAULT", CONTROL_BEHAVIOR_DEFAULT); put("WARM_UP", CONTROL_BEHAVIOR_WARM_UP); put("RATE_LIMITER", CONTROL_BEHAVIOR_RATE_LIMITER); put("WARM_UP_RATE_LIMITER", CONTROL_BEHAVIOR_WARM_UP_RATE_LIMITER); }}; protected FlowRule build(String resource){ FlowRule flowRule = new FlowRule(resource); flowRule.setCount(getCount()); flowRule.setGrade(nullable(FLOW_GRADE_MAP.get(getGrade()), FLOW_GRADE_THREAD)); flowRule.setLimitApp(getLimitApp()); flowRule.setStrategy(nullable(STRATEGY_MAP.get(getStrategy()), STRATEGY_DIRECT)); flowRule.setControlBehavior(nullable(CONTROL_BEHAVIOR_MAP.get(getControlBehavior()), CONTROL_BEHAVIOR_DEFAULT)); return flowRule; } @Override public String toString() { return "FlowRule{" + "count=" + count + ", grade='" + grade + '\'' + ", limitApp='" + limitApp + '\'' + ", strategy='" + strategy + '\'' + ", controlBehavior='" + controlBehavior + '\'' + '}'; } } public static class DegradeRuleBuilder { private double count; private int timeWindow;//s private String grade = "RT"; private String limitApp = "default"; public double getCount() { return count; } public void setCount(double count) { this.count = count; } public String getGrade() { return grade; } public void setGrade(String grade) { this.grade = grade; } public int getTimeWindow() { return timeWindow; } public void setTimeWindow(int timeWindow) { this.timeWindow = timeWindow; } public String getLimitApp() { return limitApp; } public void setLimitApp(String limitApp) { this.limitApp = limitApp; } private static final Map DEGRADE_GRADE_MAP = new HashMap(){{ put("RT", DEGRADE_GRADE_RT); put("EXCEPTION_RATIO", DEGRADE_GRADE_EXCEPTION_RATIO); put("EXCEPTION_COUNT", DEGRADE_GRADE_EXCEPTION_COUNT); }}; protected DegradeRule build(String resource){ DegradeRule degradeRule = new DegradeRule(resource); degradeRule.setCount(getCount()); degradeRule.setGrade(nullable(DEGRADE_GRADE_MAP.get(getGrade()), DEGRADE_GRADE_RT)); degradeRule.setTimeWindow(getTimeWindow()); degradeRule.setLimitApp(getLimitApp()); return degradeRule; } @Override public String toString() { return "DegradeRule{" + "count=" + count + ", timeWindow=" + timeWindow + ", grade='" + grade + '\'' + ", limitApp='" + limitApp + '\'' + '}'; } } public static class SystemRuleBuilder { private int highestSystemLoad = -1;//-1 disable private int avgRt = -1;//-1 disable private int maxThread = -1;//-1 disable private int qps = -1;//-1 disable public int getHighestSystemLoad() { return highestSystemLoad; } public void setHighestSystemLoad(int highestSystemLoad) { this.highestSystemLoad = highestSystemLoad; } public int getAvgRt() { return avgRt; } public void setAvgRt(int avgRt) { this.avgRt = avgRt; } public int getMaxThread() { return maxThread; } public void setMaxThread(int maxThread) { this.maxThread = maxThread; } public int getQps() { return qps; } public void setQps(int qps) { this.qps = qps; } protected SystemRule build(){ SystemRule systemRule = new SystemRule(); systemRule.setHighestSystemLoad(getHighestSystemLoad()); systemRule.setAvgRt(getAvgRt()); systemRule.setMaxThread(getMaxThread()); systemRule.setQps(getQps()); return systemRule; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy