Sentinel如何实现支持全局接口限流功能

1. 为什么需要全局限流

在实际应用中, 一个服务可能有上百个接口,如果每个接口都去配置限流策略, 会非常繁琐, 那么有没有全局配置功能,如果接口没有做独立的限流配置,那么就采用全局的配置策略。

查阅官方资料,也有提出相似的问题, 并且也给出了代码实现,但官方目前还没正式发布推出,那么就需要我们改造源码去做对应的实现。

参考: https://github.com/alibaba/Sentinel/issues/66

这里是基于Sentinel相对成熟的Release版本1.7.2进行改造。

2. 如何改造实现

这里主要分为两部分改造, 一部分是修改Sentinel-Core核心组件, 另一部分是增加sentinel-extension插件,通过SPI扩展方式实现。

  1. 主要改动的地方:

    file

  2. 核心代码:

    1. FlowRuleManager 流控管理
    ...
        private volatile static BaseFlowRulePropertyListener LISTENER = null;
    
        private static RuleSelector ruleSelector = null;
    ...
        static {
            loadFlowRulePropertyListener();
            ruleSelector = RuleSelectorLoader.getSelector(RuleConstant.RULE_SELECTOR_TYPE_FLOW_RULE);
            currentProperty.addListener(LISTENER);
            startMetricTimerListener();
        }
    
    	// 加载流控规则监听器
    	private synchronized static void loadFlowRulePropertyListener() {
            if (Objects.nonNull(LISTENER)) {
                return;
            }
            BaseFlowRulePropertyListener flowRulePropertyListeners = SpiLoader.of(BaseFlowRulePropertyListener.class).loadFirstInstance();
            if (Objects.isNull(flowRulePropertyListeners)) {
                flowRulePropertyListeners = new DefaultFlowRulePropertyListener();
            }
            LISTENER = flowRulePropertyListeners;
        }
    ...
        // 提供方法供外部修改
        public static void setFlowRuleMap(Map<String, List<FlowRule>> rules) {
            FlowRuleManager.flowRules = rules;
        }
    
        @SuppressWarnings(value = "unchecked")
        public static RuleSelector<FlowRule> getRuleSelector() {
            return ruleSelector;
        }
    
        public static void setRuleSelector(RuleSelector<FlowRule> ruleSelector) {
            FlowRuleManager.ruleSelector = ruleSelector;
        }
    
        public static BaseFlowRulePropertyListener getListener() {
            return LISTENER;
        }
    

    2) 将流控规则监听器抽取出来

    DefaultFlowRulePropertyListener(流控规则属性变化监听器):

    public class DefaultFlowRulePropertyListener extends BaseFlowRulePropertyListener {
    
        @Override
        public synchronized void configUpdate(List<FlowRule> value) {
            Map<String, List<FlowRule>> rules = FlowRuleUtil.buildFlowRuleMap(value);
            if (rules != null) {
                FlowRuleManager.setFlowRuleMap(rules);
            }
            RecordLog.info("[FlowRuleManager] Flow rules received: {}", rules);
        }
    
        @Override
        public synchronized void configLoad(List<FlowRule> conf) {
            Map<String, List<FlowRule>> rules = FlowRuleUtil.buildFlowRuleMap(conf);
            if (rules != null) {
                FlowRuleManager.setFlowRuleMap(rules);
            }
            RecordLog.info("[FlowRuleManager] Flow rules loaded: {}", rules);
        }
    }
    

    DefaultFlowRuleSelector(流控规则筛选器):

    public class DefaultFlowRuleSelector implements RuleSelector<FlowRule> {
    
        @Override
        public List<String> getSupportedRuleTypes() {
            return Arrays.asList(RuleConstant.RULE_SELECTOR_TYPE_FLOW_RULE);
        }
    
        @Override
        public int getPriority() {
            return 0;
        }
    
        @Override
        public List<FlowRule> select(String resource) {
            // Flow rule map should not be null.
            Map<String, List<FlowRule>> flowRules = FlowRuleManager.getFlowRuleMap();
            return flowRules.get(resource);
        }
    }
    

    3) RuleSelectorLoader (规则选择器实现)

    public class RuleSelectorLoader {
    
        private volatile static List<RuleSelector> selectors = null;
    
        public static RuleSelector getSelector(String useType) {
            RuleSelector highestPrioritySelector = getHighestPrioritySelector(useType);
            if (Objects.isNull(highestPrioritySelector)) {
                return getDefaultSelector(useType);
            }
            return highestPrioritySelector;
        }
    
        public static RuleSelector getHighestPrioritySelector(String useType) {
            List<RuleSelector> selectors = getSelector(useType, true);
            if (Objects.isNull(selectors) || selectors.size() <= 0) {
                return null;
            }
            selectors.sort(Comparator.comparingInt(RuleSelector::getPriority));
            return selectors.get(0);
        }
    
        public static List<RuleSelector> getSelector(String useType, boolean reloadWhenNoExist) {
            if ((Objects.isNull(selectors) || selectors.size() == 0) && reloadWhenNoExist) {
                loadRuleSelector();
            }
            if (selectors.size() == 0) {
                return null;
            }
            List<RuleSelector> matchedSelectorList = new ArrayList<>();
            for (RuleSelector selector : selectors) {
                List supportedRuleTypes = selector.getSupportedRuleTypes();
                if (Objects.isNull(supportedRuleTypes) || supportedRuleTypes.size() <= 0 || !supportedRuleTypes.contains(useType)) {
                    continue;
                }
                matchedSelectorList.add(selector);
            }
            return matchedSelectorList;
        }
    
        private synchronized static void loadRuleSelector() {
            selectors = SpiLoader.of(RuleSelector.class).loadInstanceListSorted();
        }
    
        private static RuleSelector getDefaultSelector(String useType) {
            switch (useType) {
                case RuleConstant.RULE_SELECTOR_TYPE_FLOW_RULE:
                    return new DefaultFlowRuleSelector();
                default:
                    return null;
            }
        }
    }
    
    
  3. sentinel-extension扩展插件限流实现

    1) 整体实现

    file

    2) 核心代码

    GlobalFlowRuleSelector(全局流控选择器):

    public class GlobalFlowRuleSelector extends DefaultFlowRuleSelector {
    
        private Map<String, Pattern> patternCacheMap = new ConcurrentHashMap<>(16);
    
        /**
         * return abstract class impl priority
         *
         * @return int priority
         */
        @Override
        public int getPriority() {
            return 10;
        }
    
        /**
         * 检查判断是否符合流控规则
         * @param resource
         * @return
         */
        @Override
        public List<FlowRule> select(String resource) {
            List<FlowRule> matchedRules = new ArrayList<>();
            List<FlowRule> matchedNormalRules = super.select(resource);
            if (Objects.nonNull(matchedNormalRules)) {
                matchedRules.addAll(matchedNormalRules);
            }
            if (isCanMergingRule() || (Objects.isNull(matchedNormalRules)) || matchedNormalRules.size() <= 0) {
                Map<String, List<FlowRule>> globalFlowMap = GlobalRuleManager.getGlobalFlowRules();
                for (Map.Entry<String, List<FlowRule>> globalFlowEntry : globalFlowMap.entrySet()) {
                    List<FlowRule> globalFlowRules = globalFlowEntry.getValue();
                    if (matchGlobalRuleByRegularExpression(globalFlowEntry.getKey(), resource)
                            && Objects.nonNull(globalFlowRules)) {
                        matchedRules.addAll(globalFlowRules);
                    }
                }
            }
            return matchedRules;
        }
    
        /**
         * 支持正则方式全局匹配
         *
         * @param regularExpression regular Expression
         * @param resourceName resource name
         * @return boolean is match
         */
        private boolean matchGlobalRuleByRegularExpression(String regularExpression, String resourceName) {
            Pattern pattern = patternCacheMap.get(regularExpression);
            if (Objects.isNull(pattern)) {
                pattern = Pattern.compile(regularExpression);
                patternCacheMap.put(regularExpression, pattern);
            }
            return pattern.matcher(resourceName).matches();
        }
    
        /**
         * is need merge normal rule and global rule
         *
         * @return boolean is need merge
         */
        private boolean isCanMergingRule() {
            return Boolean.parseBoolean(SentinelConfig.getConfig(GlobalRuleConfig.GLOBAL_RULE_MERGING_FLOW_RULE));
        }
    }
    
    

    GlobalFlowRulePropertyListener(全局流控规则属性监听器):

    public class GlobalFlowRulePropertyListener extends DefaultFlowRulePropertyListener {
        private final String GLOBAL_FLAG = "@Global:";
    
        /**
         * 属性更新监听
         * @param rules
         */
        @Override
        public void configUpdate(List<FlowRule> rules) {
            handleAllRule(rules);
        }
    
        /**
         * 启动加载属性
         * @param rules
         */
        @Override
        public void configLoad(List<FlowRule> rules) {
            handleAllRule(rules);
        }
    
        private void handleAllRule(List<FlowRule> rules) {
            if (Objects.isNull(rules) || rules.size() <= 0) {
                return;
            }
            List<FlowRule> globalFlowRule = new ArrayList<>();
            List<FlowRule> normalFlowRule = new ArrayList<>();
            rules.forEach(rule -> {
                if(rule.getResource().startsWith(GLOBAL_FLAG)) {
                    rule.setResource(rule.getResource().replaceAll(GLOBAL_FLAG, "").trim());
                    globalFlowRule.add(rule);
                } else {
                    normalFlowRule.add(rule);
                }
            });
            handleNormalRule(normalFlowRule);
            handleGlobalRule(globalFlowRule);
        }
    
        private void handleNormalRule(List<FlowRule> rules) {
            super.configUpdate(rules);
        }
    
        private void handleGlobalRule(List<FlowRule> rules) {
            Map<String, List<FlowRule>> globalRuleMap = FlowRuleUtil.buildFlowRuleMap(rules);
            GlobalRuleManager.updateGlobalFlowRules(globalRuleMap);
        }
    }
    

    GlobalRuleManager(全局流控规则管理器):

    public class GlobalRuleManager {
    
        private static volatile Map<String, List<FlowRule>> flowRuleMap = new HashMap<>();
    
        private static ReentrantLock flowRuleUpdateLock = new ReentrantLock();
    
        /**
         * 更新全局流控规则
         * @param flowRuleMap
         */
        public static void updateGlobalFlowRules(Map<String, List<FlowRule>> flowRuleMap) {
            flowRuleUpdateLock.lock();
            try {
                GlobalRuleManager.flowRuleMap = flowRuleMap;
            } finally {
                flowRuleUpdateLock.unlock();
            }
        }
    
        public static Map<String, List<FlowRule>> getGlobalFlowRules() {
            return flowRuleMap;
        }
    
    }
    

    3) SPI配置实现

    file

    通过SPI机制实现全局流控功能的接入管理

    com.alibaba.csp.sentinel.slots.block.flow.extension.BaseFlowRulePropertyListener内容:

    com.custom.sentinel.extension.flow.GlobalFlowRulePropertyListener
    

    com.alibaba.csp.sentinel.slots.block.flow.extension.rule.RuleSelector内容:

    com.custom.sentinel.extension.flow.GlobalFlowRuleSelector
    

3. 全局流控的动态配置管理

通过sentinel的dashboard管理后台,实现全局流控的动态管理,支持正则匹配。

file

file

为了与普通资源的流控规则区分, 这里的全局流控规则必须以“@Global:“开头,后台匹配时会自动识别过滤匹配。

在实际应用中, POM要加入重新修改源码后的sentinel-core依赖,并且要加入sentinel-extension依赖, 才能实现全局流控规则管理。

在没有配置具体的资源接口名时, 如果配置了全局流控规则, 则会以全局流控作为控制; 如果配置了具体的资源流控规则,则会忽略全局流控规则, 以具体的资源流控规则为准。

改造后的源码已上传,地址:https://download.csdn.net/download/hxx688/21725580

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

麦神-mirson

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值