百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

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

toyiye 2024-07-15 01:18 9 浏览 0 评论

1. 为什么需要全局限流

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

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

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

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

2. 如何改造实现

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

  1. 主要改动的地方:

  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) 整体实现

    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配置实现

    通过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管理后台,实现全局流控的动态管理,支持正则匹配。

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

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

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

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


本文由mirson创作分享, 欢迎关注, 谢谢

相关推荐

Python 可视化工具包(python常见的可视化工具)

喜欢用Python做项目的小伙伴不免会遇到这种情况:做图表时,用哪种好看又实用的可视化工具包呢?本文将介绍一些常用的Python可视化包,包括这些包的优缺点以及分别适用于什么样的场景。这篇文章...

Python的GPU编程实例——近邻表计算

目录技术背景...

python算法体验-3.python实现欧式距离的三种方式

欧式距离也称欧几里得距离,是最常见的距离度量,衡量的是多维空间中两个点之间的绝对距离。欧式距离源自N维欧氏空间中两点...

python实现Lasso回归分析(特征筛选、建模预测)

实现功能:...

python语言检测模块langid、langdetect使用

本文首发地址:https://blog.csdn.net/Together_CZ/article/details/86678423欢迎关注我的博客【Together_CZ】,我是沂水寒城!之前使用数据...

7天学会Python最佳可视化工具Seaborn(一):可视化变量间的关系

众所周知,Seaborn“可能”是Python下最友好、易用的可视化工具了,可视化效果也非常好。但是截止目前,并没有一份中文教程供广大国内Python使用者查阅学习。怎么能因为语言的问题,让大家错过这...

在Python中使用K-Means聚类和PCA主成分分析进行图像压缩

各位读者好,在这篇文章中我们尝试使用sklearn库比较k-means聚类算法和主成分分析(PCA)在图像压缩上的实现和结果。压缩图像的效果通过占用的减少比例以及和原始图像的差异大小来评估。图像压...

OpenCV-Python 相机校准 | 四十九

目标在本节中,我们将学习由相机引起的失真类型,如何找到相机的固有和非固有特性如何根据这些特性使图像不失真基础一些针孔相机会给图像带来明显的失真。两种主要的变形是径向变形和切向变形。径向变形会导致直线出...

python数据预处理技术(python 数据预处理)

在真实世界中,经常需要处理大量的原始数据,这些原始数据是机器学习算法无法理解的。为了让机器学习算法理解原始数据,需要对数据进行预处理。我们运行anaconda集成环境下的“jupyternotebo...

【Python可视化系列】一文教你绘制不同类型散点图(理论+源码)

这是...

OpenCV-Python 特征匹配 | 四十四

目标在本章中,我们将看到如何将一个图像中的特征与其他图像进行匹配。我们将在OpenCV中使用Brute-Force匹配器和FLANN匹配器Brute-Force匹配器的基础蛮力匹配器很简单。它使用第一...

实战python中Random模块使用(python中的random模块)

一、random模块简介Python标准库中的random函数,可以生成随机浮点数、整数、字符串,甚至帮助你随机选择列表序列中的一个元素,打乱一组数据等。要在Python中使用random模块,只需要...

Python随机模块22个函数详解(python随机函数的应用)

随机数可以用于数学,游戏,安全等领域中,还经常被嵌入到算法中,用以提高算法效率,并提高程序的安全性。平时数据分析各种分布的数据构造也会用到。random模块,用于生成伪随机数,之所以称之为伪随机数,是...

说冲A就冲A,这个宝藏男孩冯俊杰我pick了

爱奇艺新上架了一部网剧叫《最后一个女神》。有个惊人的发现,剧里男三居然是《青春有你》的训练生冯俊杰。剧组穷,戏服没几件,冯俊杰几乎靠一件背背佳撑起了整部剧。冯俊杰快速了解一下。四川人,来自觉醒东方,人...

唐山打人嫌犯陈继志去医院就医的背后,隐藏着三个精心设计的步骤

种种迹象表明,陈继志这帮人对处理打人之后的善后工作是轻车驾熟的,他们想实施的计划应该是这样的:首先第一步与伤者进同一家医院做伤情鉴定,鉴定级别最好要比对方严重,于是两位女伤者被鉴定为轻伤,他们就要求医...

取消回复欢迎 发表评论:

请填写验证码