首页 > 编程语言 >滑动窗口算法实现分布式第三方请求限频

滑动窗口算法实现分布式第三方请求限频

时间:2023-04-26 09:22:05浏览次数:45  
标签:限频 窗口 请求 算法 限流 漏桶 var 滑动 分布式

一. 业务背景

 第三方服务接口存在频率调用限制(例如,1s5次,超过5次返回超出频率),己方服务存在并发处理的情况,为了保证服务的成功率,且达到第三方限制的最大吞吐量,故需要一个限频调用的算法

二.实现思路

常见限频算法一般有五种,漏桶算法、令牌桶算法、固定窗口算法,滑动窗口算法,漏斗算法,五种算各有优劣,详情见下表

  简介 优点 缺点 常见使用场景  
漏桶算法 漏桶算法是水先进入到漏桶里,漏桶再以一定的速率出水,当流入水的数量大于流出水时,多余的水直接溢出。把水换成请求来看,漏桶相当于服务器队列,但请求量大于限流阈值时,多出来的请求就会被拒绝服务。漏桶算法使用队列实现,可以以固定的速率控制流量的访问速度,可以做到流量的平整化处理。  限流的绝对平均化。

 不适合突发请求场景、请求延迟高:当短时间内有大量的突发请求时,即便此时服务器没有任何负载,每个请求也都得在队列中等待一段时间才能被响应。

   
漏斗算法

漏斗有一定的容量,并且以一定速率漏水,漏斗的剩余空间即允许请求的空间。

漏斗算法的模型和漏桶算法在模型上是一致的,容器叫法不同,一个叫漏斗,一个叫漏桶,剩余空间直接决定了请求是否可以通过,只不过在漏斗算法中,一旦通过,请求便可以立即访问;

而漏桶算法中,请求通过后,会被暂存在容器中,等待被匀速处理,两者的差别即在于此。

 预热限流和平滑限流兼备。      
令牌桶算法  简单来说,对于每一个请求,都需要获取令牌来访问,如果没有获得令牌,则需要触发限流策略。系统会以一个恒定的速度,往固定容量的令牌桶中放入令牌,如果此时有客户端请求进来,则需要先从令牌桶算法中获取令牌,然后才能访问系统。


 预热限流和平滑限流兼备。      
固定窗口限流 固定窗口是限流算法里最简单也是最容易实现的一种算法。比如我们规定,对于A接口来说,我们1分钟的访问次数不能超过100个。那么我们我们可以设置一个计数器counter,其有效时间为1分钟(即每分钟计数器会被重置为0),每当一个请求过来的时候,counter就加1,如果counter的值大于100,就说明请求数过多 简单

窗口切换时无法保证无法很好的控制临界值,常见的请求分布是不均匀的,例如每秒限制100次,前最后一秒的10ms请求100次,后最后一秒的前10ms请求100次,相当于200次/0.02s。

   
滑动窗口限流  

以当前时间为截止时间,往前取一定的时间作为时间窗口,比如:往前取 10s 的时间

当有新的请求进入时,删除时间窗口之外的请求,对时间窗口之内的请求进行计数统计,若未超过限制,则进行放行操作;若超过限制,则拒绝本次服务。

 可以避免固定窗口临界的缺点  时间区间越长,临界精度越高,占用资源越多    

 三.需求分析

 因为第三方的限频模式可能有多种多样,所以此处采用了滑动窗口算法的精确限频

四.代码实现

 这里调整了滑动窗口算法的逻辑,返回了需要等待的时间,保证了请求的成功率,缺点就是消耗资源增加,并且请求可能会延迟很久

 private async Task Sleep(RequestInfo requestInfo)
        {
            if (requestInfo is null || requestInfo.Key.IsNullOrEmpty())
            {
                return;
            }

            TimeSpan? remainingWaitTime;

            do
            {
                remainingWaitTime = await GetRemainingWaitTimeAsync(requestInfo);

                if (remainingWaitTime.HasValue)
                {
                    _logger.LogInformation("延迟时间:" + remainingWaitTime.Value.TotalMilliseconds);
                    await Task.Delay(remainingWaitTime.Value);
                }
                await Task.Delay(TimeSpan.FromMilliseconds(10)); // 做个时间切片,减少循环次数
            } while (remainingWaitTime.HasValue);
        }

        private static async Task<TimeSpan?> GetRemainingWaitTimeAsync(RequestInfo requestInfo)
        {
            var rules = requestInfo.RateLimitRules;
            long timeStamp = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();

            // 创建一个管道
            var pipe = RedisHelper.StartPipe();

            foreach (var rule in rules)
            {
                var key = "rate_limit:" + requestInfo.Key + ":" + rule.Period.TotalMilliseconds;
                pipe.ZAdd(key, (timeStamp, timeStamp))
                .ZRemRangeByScore(key, 0, timeStamp - (long)rule.Period.TotalMilliseconds)
                .ZCard(key)
                .Expire(key, rule.Period); // 为键设置过期时间
            }
            // 执行管道中的所有命令并获取结果
            var result = pipe.EndPipe();

            // 检查每个限流规则是否超出限制,并计算最长的剩余等待时间
            TimeSpan? longestRemainingWaitTime = null;
            for (int i = 0; i < rules.Count; i++)
            {
                // 上面每次循环执行四条命令  所以次数×4
                int currentCount = Convert.ToInt32(result[4 * i + 2]);
                if (currentCount >= rules[i].Limit)
                {
                    var key = "rate_limit:" + requestInfo.Key + ":" + rules[i].Period.TotalMilliseconds;
                    var scores = await RedisHelper.ZRangeByScoreWithScoresAsync<long>(key, 0, 0);
                    var minScore = scores.Length > 0 ? (long)scores[0].score : timeStamp;
                    var remainingMilliseconds = timeStamp - minScore;
                    var currentRemainingWaitTime = TimeSpan.FromMilliseconds(remainingMilliseconds);
                    if (longestRemainingWaitTime == null || currentRemainingWaitTime > longestRemainingWaitTime)
                    {
                        longestRemainingWaitTime = currentRemainingWaitTime;
                    }
                }
            }

            return longestRemainingWaitTime;
        }

  

 

五.参考文档

https://www.cnblogs.com/duanxz/p/4123068.html

https://www.cnblogs.com/Kingfans/p/16263774.html

标签:限频,窗口,请求,算法,限流,漏桶,var,滑动,分布式
From: https://www.cnblogs.com/qcqc/p/17338018.html

相关文章

  • “源擎”云原生分布式核心业务系统有什么产品优势?
     “源擎”核心系统利用云原生、分布式、微服务技术,基于企业架构设计思想,构建了基础服务、业务服务、交易中心以及系列支撑组件,包含业务架构和多个微服务应用。业务架构中,交易中心为银行提供了更灵活的选择,支持产品粒度的功能可替换,同时也能快速引入新的产品服务,支持未来业务发展,实......
  • 下一代大数据分布式存储技术Apache Ozone初步研究
    @目录概述定义特性架构总体架构写数据读数据部署安装方式安装Docker启动Docker-compose启动企业预置型(OnPremise)安装实践命令行接口Ofs(Hadoop兼容)ReconAPI概述定义ApacheOzone官网地址https://ozone.apache.org/最新版本1.3.0ApacheOzone官网最新文档地址http......
  • ray-分布式计算框架-集群与异步Job管理
    0.ray简介ray是开源分布式计算框架,为并行处理提供计算层,用于扩展AI与Python应用程序,是ML工作负载统一工具包RayAIRuntimeML应用程序库集RayCore通用分布式计算库Task--Ray允许任意Python函数在单独的Pythonworker上运行,这些异步Python函数称为任务Actor......
  • 力扣---2653. 滑动子数组的美丽值
    给你一个长度为n 的整数数组 nums ,请你求出每个长度为 k 的子数组的美丽值 。一个子数组的美丽值 定义为:如果子数组中第x 小整数 是负数 ,那么美丽值为第x 小的数,否则美丽值为0 。请你返回一个包含 n-k+1 个整数的数组,依次 表示数组中从第一个下标开始......
  • uni-app中使用uCharts图表设置横向滚动无法滑动。
    opts:{ color:["#1890FF","#91CB74","#FAC858","#EE6666","#73C0DE","#3CA272","#FC8452","#9A60B4","#ea7ccc"], padding:[15,10,0,0], enableScroll:true, upd......
  • LeetCode 周赛 342(2023/04/23)容斥原理、计数排序、滑动窗口、子数组 GCB
    本文已收录到AndroidFamily,技术和职场问题,请关注公众号[彭旭锐]提问。大家好,我是小彭。前天刚举办2023年力扣杯个人SOLO赛,昨天周赛就出了一场Easy-Easy-Medium-Medium的水场,不得不说LeetCode是懂礼数的......
  • 第五讲 Weldentity分布式身份解决方案、智能合约初探
    什么是智能合约1996年,NickSzabo在文章《SmartContracts:BuildingBlocksForDigitalMarkets》中提出了智能合约的概念所谓“合约”,就是条文、合同一类的东西,里面记录了发生的条件与对应执行的条款,以支持确权等操作;所谓”智能”,就意味着自动化、可编程。所以,智能合约就是......
  • redis实现分布式锁
    分布式锁是由共享存储系统维护的变量,多个客户端可以向共享存储系统发送命令进行加锁或释放锁操作。Redis作为一个共享存储系统,可以用来实现分布式锁。在基于单个Redis实例实现分布式锁时,对于加锁操作,我们需要满足三个条件。1.加锁包括了读取锁变量、检查锁变量值和设置锁变量......
  • .net使用nacos配置,手把手教你分布式配置中心
    .net使用nacos配置,手把手教你分布式配置中心Nacos是一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。这么优秀的分布式服务管理平台,怎么能不接入呢?nacos的安装和使用这里就不细说了,可以参考网上教程和官方文档。https://nacos.io/zh-cn/docs/quick-start.htm......
  • .NET CORE开源 DDD微服务 支持 多租户 单点登录 多级缓存、自动任务、分布式、日志、
    源代码地址https://github.com/junkai-li/NetCoreKevin基于NET6搭建跨平台DDD思想WebApi架构、IDS4单点登录、多缓存、自动任务、分布式、多租户、日志、授权和鉴权、CAP、SignalR、docker部署 如需简约项目可直接去除项目引用解耦设计都可以单独引用架构默认全部引用并启动......