手写RPC-令牌桶限流算法实现,以及常见限流算法

为什么需要服务限流、降级

        分布式架构下,不同服务之间频繁调用,对于某个具体的服务而言,可能会面临高并发场景。在这样的情况下,提供服务的每个服务节点就都可能由于访问量过大而引起一系列问题,比如业务处理耗时过长、CPU飘高、频繁Full GC以及服务进程直接宕机等。但是在生产环境中,要保证服务的稳定性和高可用性,这时就需要业务进行自我保护,从而保证在高访问量、高并发的场 景下,应用系统依然稳定,服务依然高可用。 我们再次借助 RPC 框架来分析,RPC 调用包括服务端和调用端。对于服务端来讲一般实现限流、降级算法;对于调用方来说一般实现熔断算法。

        加入服务调用方调用某个多服务节点的其中一个实现如下:

        此时对于特定的服务节点来说,可能存在多个不同调用者的调用,从而使得节点接收服务量超过其最大承载力。此时如果不采取某种策略进行调整,对应的具体服务可能由于过多调用而崩溃,无法对于提供服务能力。限流算法可以很好解决以上问题:当调用端发送请求过来时,服务端在执行业务逻辑之前先执行限流逻辑,如果发现访问量过大并 且超出了限流的阈值,就让服务端直接抛回给调用端一个限流异常,否则就执行正常的业务逻辑。

常见限流算法实现有哪些

        常见限流算法实现固定窗口计数器法、滑动窗口计数器法、漏桶算法、令牌桶算法。

令牌桶算法

        令牌桶是一个限流容器,容器有最大容量(最大处理能力),每秒或每100ms产生一个令牌(具体取决于业务实现以及机器的最大处理能力),当容器中令牌数量达到最大容量时,令牌数量此时不会增加,只有当请求过来时,才会使得令牌数量减少(只有获取到令牌的请求才会执行业务逻辑),才会不断的以一定的速率生成令牌。

         令牌桶限流范围:

        假设令牌桶最大容量为n ,每秒产生 r 个令牌
        平均速率:则随着时间推延,处理请求的平均速率越来越趋近于每秒处理r 个请求,说明令牌桶算法可以控制平均速率
        瞬时速率:如果在一瞬间有很多请求进来,此时来不及产生令牌,则在一瞬间最多只有n 个请求能获取到令牌执行业务逻辑,所以令牌桶算
法也可以控制瞬时速率
        在这里提下漏桶,漏桶由于出水量固定,所以无法应对突然的流量爆发访问,也就是没有保证瞬时速率的功能,但是可以保证平均速率

漏桶算法

        漏桶算法强调把服务调用比作水滴,当有调用(任务)发生时,将其加入桶中,随后采用某种机制(如:定时器)以一定的速率从桶中取出任务进行处理(相当于桶以一定的速率不断在漏水,也就是【漏桶】算法)

        漏桶算法的实现可以借助于Redis实现的消息队列和定时任务。

优点:

  • 实现简单、易于理解。
  • 可以控制限流速率,避免网络拥塞和系统过载。

缺点:

  • 无法应对突然激增的流量,因为只能以固定的速率处理请求,对系统资源利用不够友好。
  • 桶流入水(发请求)的速率如果一直大于桶流出水(处理请求)的速率的话,那么桶会一直是满的,一部分新的请求会被丢弃,导致服务质量下降。

        在实际业务场景中一般不适用漏桶算法,基于Redis实现的消息队列,其容量非常大,可以使用,其可以理解为容量无线的桶,在一定程度下不会发生消息丢失。

固定窗口计数器法

固定窗口其实就是时间窗口,其原理是将时间划分为固定大小的窗口,在每个窗口内限制请求的数量或速率,即固定窗口计数器算法规定了系统单位时间处理的请求数量。

假如我们规定系统中某个接口 1 分钟只能被访问 33 次的话,使用固定窗口计数器算法的实现思路如下:

  • 将时间划分固定大小窗口,这里是 1 分钟一个窗口。
  • 给定一个变量 counter 来记录当前接口处理的请求数量,初始值为 0(代表接口当前 1 分钟内还未处理请求)。
  • 1 分钟之内每处理一个请求之后就将 counter+1 ,当 counter=33 之后(也就是说在这 1 分钟内接口已经被访问 33 次的话),后续的请求就会被全部拒绝。
  • 等到 1 分钟结束后,将 counter 重置 0,重新开始计数。

优点:实现简单,易于理解。

缺点:

  • 限流不够平滑。例如,我们限制某个接口每分钟只能访问 30 次,假设前 30 秒就有 30 个请求到达的话,那后续 30 秒将无法处理请求,这是不可取的,用户体验极差!
  • 无法保证限流速率,因而无法应对突然激增的流量。

        对于固定窗口计数器算法而言,存在非常严重的一个问题就是临界问题: 假设1min内服务器的负载能力为100,因此一个周期的访问量限制在100,然后在第一个周期的最后5s和下一个周期的开始5s时间段内,分别涌入了100的访问量,虽然没有超过每个周期的限制量,但是整体上10s内已经达到了200的访问量,远超服务器的承载能力。

滑动窗口计数器法

滑动窗口计数器算法 算的上是固定窗口计数器算法的升级版,限流的颗粒度更小。

滑动窗口计数器算法相比于固定窗口计数器算法的优化在于:它把时间以一定比例分片

例如我们的接口限流每分钟处理 60 个请求,我们可以把 1 分钟分为 60 个窗口。每隔 1 秒移动一次,每个窗口一秒只能处理不大于 60(请求数)/60(窗口数) 的请求, 如果当前窗口的请求计数总和超过了限制的数量的话就不再处理其他请求。

        如下图,假设时间周期为1min,将1min再分为2个小周期,统计每个小周期的访问数量,则可以看到,第一个时间周期内,访问数量为75,第二个时间周期内,访问数量为100,超过100的访问则被限流掉了 

        很显然, 当滑动窗口的格子划分的越多,滑动窗口的滚动就越平滑,限流的统计就会越精确。

优点:

  • 相比于固定窗口算法,滑动窗口计数器算法可以应对突然激增的流量。
  • 相比于固定窗口算法,滑动窗口计数器算法的颗粒度更小,可以提供更精确的限流控制。

缺点:

  • 与固定窗口计数器算法类似,滑动窗口计数器算法依然存在限流不够平滑的问题。
  • 相比较于固定窗口计数器算法,滑动窗口计数器算法实现和理解起来更复杂一些。

四种限流算法的比较 

令牌桶限流算法在RPC服务中的应用

        为了实现令牌桶算法,我们并不需要严格按照算法的定义多长时间产生一个令牌,我们只需要当桶中无令牌时,根据(当前系统时间 - 上次令牌生成时间之间的差值 )x 令牌生成速率,便可以得到这段时间应该生成的令牌总数,随后取其和容量CAPACITY的最小值即可。

        代码实现:

package main.version4.v1.server.rateLimit.impl;import lombok.extern.slf4j.Slf4j;
import main.version4.v1.server.rateLimit.RateLimit;/*** 令牌桶限流算法实现* CAPACITY为令牌桶的最大容量,curCAPACITY为当前令牌桶中令牌的数量,timeStamp为上一次请求获取令牌的时间,* 我们在这里并没有实现计数器每秒产生多少令牌放入容器中,而是记住了上一次请求到来的时间,和这次请求之间的时间差值* 进一步根据RATE计算出这段时间能够产生的令牌数量,取min(CAPACITY, CURCAPAITY)。*/
@Slf4j
public class TokenBucketRateLimitImpl implements RateLimit {// 令牌产生速率(单位ms)private static int RATE;// 桶容量private static int CAPACITY;// 当前桶容量private volatile static int curCapacity;// 时间戳private volatile long timeStamp = System.currentTimeMillis();public TokenBucketRateLimitImpl(int rate, int capacity){RATE = rate;CAPACITY = capacity;curCapacity = CAPACITY;}@Overridepublic synchronized boolean getToken() {// 如果当前桶有剩余,直接返回if(curCapacity > 0){curCapacity--;return true;}// 如果桶无剩余long currentTime = System.currentTimeMillis();// 如果距离上一次的请求的时间大于RATE的时间if(currentTime - timeStamp >= RATE){//计算这段时间间隔中生成的令牌,如果>2,桶容量加上(计算的令牌-1)//如果==1,就不做操作(因为这一次操作要消耗一个令牌)if((currentTime - timeStamp) / RATE >= 2){curCapacity += (int) (currentTime - timeStamp) / RATE - 1;}//保持桶内令牌容量<=CAPACITYif(curCapacity > CAPACITY){curCapacity = CAPACITY;}//刷新时间戳为本次请求timeStamp = currentTime;return true;}return false;}
}

参考资料

 服务限流详解 | JavaGuide

常用4种限流算法介绍及比较-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/382701.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

px4ctrl里calculateControl的代码解析

px4ctrl的代码里将目标加速度转成飞控的rpy的calculateControl代码 LinearControl::LinearControl(Parameter_t &param) : param_(param) {resetThrustMapping(); } quadrotor_msgs::Px4ctrlDebug LinearControl::calculateControl(const Desired_State_t &des,const …

【Apache Doris】数据副本问题排查指南

【Apache Doris】数据副本问题排查指南 一、问题现象二、问题定位三、问题处理 本文主要分享Doris中数据副本异常的问题现象、问题定位以及如何处理此类问题。 一、问题现象 问题日志 查询报错 Failed to initialize storage reader, tablet{tablet_id}.xxx.xxx问题说明 查…

Linux下docker快速安装gitea

之前在服务器上装的gitlab来管理个人项目&#xff0c;但是gitlab服务启动后能明显感受到占用资源比较严重。最近服务器到期&#xff0c;换了个服务器还没来得及装gitlab&#xff0c;刚好最近接触到gitea&#xff0c;网上是这么说的 占用资源少&#xff0c;适合个人开发者&…

DeepFaceLive黄仙人高清模型416DFM

链接&#xff1a;https://pan.baidu.com/s/1b5nu23Z93bDcxgNyyR3KjA?pwdj8kz 提取码&#xff1a;j8kz 黄羿DF-5M-UDT416_320_80_80_26DFM

【ProtoBuf】通讯录实现(网络版)

Protobuf 还常用于通讯协议、服务端数据交换场景。那么在这个示例中&#xff0c;我们将实现一个网络版本的通讯录&#xff0c;模拟实现客户端与服务端的交互&#xff0c;通过 Protobuf 来实现各端之间的协议序列化。 需求如下&#xff1a; 客户端可以选择对通讯录进行以下操…

关于pycharm上push项目到gitee失败原因

版权声明&#xff1a;本文为博主原创文章&#xff0c;如需转载请贴上原博文链接&#xff1a;https://blog.csdn.net/u011628215/article/details/140577821?spm1001.2014.3001.5502 前言&#xff1a;最近新建项目push上gitee都没有问题&#xff0c;但是当在gitee网站进行了一个…

新生上大学提前去西藏旅游有什么要注意的,语言上该怎么办?

新生前往西藏旅游并提前适应大学生活是一次充满挑战与发现的旅程。在准备过程中&#xff0c;重要的是要对高原反应有所准备&#xff0c;了解其症状并采取预防措施&#xff0c;同时携带必要的防晒和保暖衣物以应对极端的气候条件。在交通和饮食方面&#xff0c;选择安全可靠的选…

第二证券:回购队伍持续扩容!这类公司行动

近期&#xff0c;上市公司密布发布回购方案&#xff0c;一些中期成果较好的公司赫然在列。除了发布正式的回购方案&#xff0c;部分上市公司控股股东、实践控制人、董事长、总经理提议回购公司股份。 绩优股活泼回购 7月23日晚&#xff0c;包括圣泉集团、中科三环、翔楼新材在…

IntelliJ IDEA 直接在软件中更新为最新版

当我们的 IDEA 工具许久没有更新&#xff0c;已经拖了好几个版本&#xff0c;想跨大版本更新&#xff0c;比如从2020.2.1 -> 2023.x.x 此时&#xff0c;我们菜单栏点击 Help -> Check for Updates… &#xff0c;右下角会有提示更新&#xff0c;如下图&#xff1a; 点…

Unity Shader - 2024 工具篇

目录 IDE 工具建议 IDE工具 Sublime 3 大势所趋&#xff0c;但是Sublime 使用插件还是相当的不习惯 代码跳转 Go to definite IDE 工具建议 () what is the best ide for coding shaderlab - #4 by DaveAstator - Unity Engine - Unity Discussions​​​​​​​I IDE工…

大模型学习笔记 - LLM模型架构

LLM 模型架构 LLM 模型架构 1. LLM 核心模型 Transformer2. 详细配置 2.1 归一化方法2.2 归一化模块位置2.3 激活函数2.4 位置编码 2.4.1 绝对位置编码2.4.2 相对位置编码2.4.3 旋转位置编码 RoPE2.4.4 ALiBi位置编码 2.5 注意力机制 2.5.1 完整自注意力机制2.5.2 稀疏注意力机…

计算机技术基础 (bat 批处理)Note4

计算机技术基础 &#xff08;bat 批处理&#xff09;Note4 本节主要讲解一些 bat 批处理文件中的一些特殊符号&#xff0c;包括 , %, > 和 >>, |, ^, & 和 && 和 ||, " ", ,, ;, ()。 回显屏蔽符 回显屏蔽符 : 这个字符在批处理中的意思是关…

数据结构中的八大金刚--------八大排序算法

目录 引言 一&#xff1a;InsertSort(直接插入排序) 二&#xff1a;ShellSort(希尔排序) 三&#xff1a;BubbleSort(冒泡排序) 四&#xff1a; HeapSort(堆排序) 五&#xff1a;SelectSort(直接选择排序) 六&#xff1a;QuickSort(快速排序) 1.Hoare版本 2.前后指针版本 …

【Hot100】LeetCode—416. 分割等和子集

目录 题目1- 思路2- 实现⭐152. 乘积最大子数组——题解思路 3- ACM 实现 题目 原题连接&#xff1a;416. 分割等和子集 1- 思路 理解为背包问题 思路&#xff1a; 能否将均分的子集理解为一个背包&#xff0c;比如对于 [1,5,11,5]&#xff0c;判断能否凑齐背包为 11 的容量…

Linux云计算 |【第二阶段】AUTOMATION-DAY2

主要内容&#xff1a; 部署GitLab、配置管理GitLab、CI/CD概述、Jenkins概述、部署Jenkins&#xff08;初始化、拷贝插件&#xff09; 一、GitLab概述 GitLab 是一个基于 Web 的 Git 仓库管理工具&#xff0c;它提供了一个集成的开发环境和代码管理平台。GitLab 不仅支持 Git…

便携式自动气象站:科技赋能气象观测

便携式自动气象站&#xff0c;顾名思义&#xff0c;就是一款集成了多种气象传感器&#xff0c;能够自动进行气象观测和数据记录的设备。它体积小巧、重量轻&#xff0c;便于携带和快速部署&#xff0c;可以在各种环境下进行气象数据的实时监测。同时&#xff0c;通过内置的无线…

keil单步调试需要点击多次

问题描述&#xff1a;在进行调试的时候&#xff0c;点击单步调试&#xff0c;总是需要点击好几次才可以运行到下一条语句。 我点击单步调试的时候&#xff0c;会在红色框内进行单步运行&#xff0c;也就是在汇编代码内单步执行。 解决办法&#xff1a; 关闭汇编窗口 就是这个小…

案例研究|柯尼卡美能达软件开发(大连)有限公司基于DataEase构筑内部数据可视化体系

柯尼卡美能达软件开发&#xff08;大连&#xff09;有限公司于2007年5月25日注册成立。公司以“洞悉在工作的人们真实情况&#xff0c;探寻他们的愿望&#xff0c;持续提供使人们更加幸福的服务”为使命&#xff0c;致力于系统品质测试服务、软件开发服务、IT安全服务、高级BPO…

SpringBoot缓存注解使用

背景 除了 RedisTemplate 外&#xff0c; 自Spring3.1开始&#xff0c;Spring自带了对缓存的支持。我们可以直接使用Spring缓存技术将某些数据放入本机的缓存中&#xff1b;Spring缓存技术也可以搭配其他缓存中间件(如Redis等)进行使用&#xff0c;将某些数据写入到缓存中间件…

Golang | Leetcode Golang题解之第264题丑数II

题目&#xff1a; 题解&#xff1a; func nthUglyNumber(n int) int {dp : make([]int, n1)dp[1] 1p2, p3, p5 : 1, 1, 1for i : 2; i < n; i {x2, x3, x5 : dp[p2]*2, dp[p3]*3, dp[p5]*5dp[i] min(min(x2, x3), x5)if dp[i] x2 {p2}if dp[i] x3 {p3}if dp[i] x5 {p5…