手写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.xdnf.cn/news/1486657.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot把nacos配置注入时数据注入时出现莫名错误

一、错误详情 我在nacos的配置a是003457 但是注入的数据是1839 二、解决方法 通过加号可以解决这个问题: 数据正确了&#xff1a;

【BUG】已解决:SyntaxError: invalid syntax

SyntaxError: invalid syntax 目录 SyntaxError: invalid syntax 【常见模块错误】 【解决方案】 常见原因及解决方法 解决步骤 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&#xff0c;我是博主英杰&#xff0c;211科班出身&#xff0c;就职…

Ubuntu22.04离线安装nginx

下载安装包 nginx nginx下载地址&#xff0c;选stable的即可&#xff0c;传到服务器上面&#xff0c;记住上传路径 提示: 下面的openssl&#xff0c;zlib,pcre也可以不下载也可以&#xff0c;我这里是考虑到完全离线下载的情况openssl 这个是https需要弄得&#xff0c;如果生产…

【JavaScript】箭头函数

具体讲解 之前写 this 的指向时就提到过箭头函数&#xff0c;但是由于其比较复杂&#xff0c;还是单独开一篇来讲箭头函数。 箭头函数&#xff0c;箭头函数不能作为构造函数&#xff0c;没有原型 prototype&#xff0c;不能 new。 在箭头函数中&#xff0c;this 关键字指向的是…

MMROTATE的混淆矩阵confusion matrix生成

mmdetection中加入了混淆矩阵生成并可视化的功能&#xff0c;具体的代码在tools/analysis_tools/confusion_matrix.py。 mmrotate由于主流遥感数据集中的DOTA数据集标注格式问题&#xff0c;做了一些修改&#xff0c;所以我们如果是做遥感图像检测的Dota数据集的混淆矩阵&…

C:图案打印

引言 本篇文章讲了一些常见的图形编程题&#xff0c;并总结了一些规律。 1、打印空心正方形 1.1 代码展示&#xff1a; #include<stdio.h> int main() {int a 0;//边长初始化scanf("%d", &a);//输入边长的值{int i 0;for (i 0; i < a; i)//控制行…

数据结构C++——优先队列

文章目录 一、定义二、ADT三、优先队列的描述3.1 线性表3.2 堆3.2.1 最大堆的ADT3.2.2 最大堆的插入3.2.3 最大堆的删除3.2.4 最大堆的初始化3.3 左高树 LT3.3.1 高度优先左高树HBLT3.3.2 重量优先左高树WBLT3.3.3 最大HBLT的插入3.3.4 最大HBLT的删除3.3.5 合并两棵最大HBLT3.…

京东商品详情API返回值:商品ID与标题解析

京东商品详情API是京东电商平台提供的一个接口&#xff0c;用于获取商品的详细信息&#xff0c;包括商品ID、商品标题、价格、库存等。然而&#xff0c;需要注意的是&#xff0c;直接访问和使用京东的商品详情API通常需要符合京东的开放平台规则&#xff0c;并可能需要注册成为…

OpenCV 卷积操作 均值,高斯,中值滤波 图片降噪

文章目录 卷积概念卷积的作用1. 图像平滑与去噪2. 边缘检测3. 特征提取4. 图像增强 常见的三种滤波均值滤波均值滤波的步骤优点和缺点使用示例 高斯滤波示例代码 中值滤波中值滤波的基本原理数学表达式中值滤波的步骤示例优点和缺点使用示例 三种滤波 图片降噪 Python实现 卷积…

redis高可用之主从复制、哨兵以及Cluster集群

目录 一、Redis主从复制 1&#xff09;主从复制的作用 2&#xff09;主从复制流程 3&#xff09;搭建Redis主从复制 1、部署redis服务器 2、修改Redis配置文件&#xff08;所有节点操作&#xff09; 3、验证主从复制结果 二、哨兵模式 1&#xff09;哨兵的作用 2&…

设计模式-领域逻辑模式-SQL的分离

尽管SQL已经在商业软件中广泛应用&#xff0c;但它在使用中还存在一定缺陷 许多应用程序开发者不能充分理解SQL&#xff0c;同时很多习惯用SQL的开发人员又可能组织不好程序代码。尽管现在有很多技术可以把SQL封装在程序里&#xff0c;但大多封装的还很牵强。 SQL分离的思路&…

谷粒商城实战笔记-46-商品服务-API-三级分类-配置网关路由与路径重写

文章目录 一&#xff0c;准备工作1&#xff0c;新增一级菜单2&#xff0c;新增二级菜单 二&#xff0c;前端树形界面开发1&#xff0c;开发分类展示组件 三&#xff0c;远程调用接口获取商品分类数据1&#xff0c;远程调用2&#xff0c;路由配置 错误记录 本节的主要内容&#…

Cisco ISR 2代路由器,1900,2900,3900系列RTU License使用方法

1 情况说明 客户处的2台Cisco 2911要开启ip sla ,但发现无法支持&#xff0c;查询得知需要有data license才可以。可以通过开启RTU license激活。开启RTU后正常. 2 操作方法 License种类如下&#xff1a;  ipbase ipbasek9 Permanent ipbasek9  security securityk9 Eva…

C++【new delete】【operator new operator delete】

目录 数据段存储位置的小复习 new 和 delete operator new 和 operator delete new和delete调用operator new和operator delete new [ ] 和 delete [ ]的原理 数据段存储位置的小复习 全局变量和静态变量都在静态区&#xff0c;也称数据段 全局变量int x 0 和 全局静态变…

全网最实用--神经网络各个组件以及效率指标 (含代码助理解,粘贴即用)

文章目录 一、神经网络相关组件0.前奏1.全连接层&#xff08;Fully Connected Layer, FC&#xff09;/密集层&#xff08;Dense Layer&#xff09;:2.卷积层&#xff08;Convolutional Layer, Conv&#xff09;:a.一维卷积b.二维卷积c.分组卷积 3.池化层&#xff08;Pooling La…

JavaWeb系列二十三: web 应用常用功能(文件上传下载)

文件上传下载 基本介绍文件上传基本原理文件上传应用实例文件上传注意事项和细节 文件下载基本原理文件下载应用实例文件下载注意事项 ⬅️ 上一篇: JavaWeb系列二十二: 线程数据共享和安全(ThreadLocal) &#x1f389; 欢迎来到 JavaWeb系列二十三: web 应用常用功能(文件上传…

数据结构:基础概念

一、相关概念 概念 相互之间存在一种或多种特定关系的数据元素的集合。 逻辑结构 集合&#xff1a;所有数据在同一个集合中&#xff0c;关系平等。 线性&#xff1a;数据和数据之间是一对一的关系 树&#xff1a; 一对多 图&#xff1a;多对多 物理结构(在内存当中的存储关系)…

堆的相关特点

一.建堆的两种方法 给定一个数组&#xff0c;其中数组里面的元素个数是n个如何能够把这个数组建立成为一个堆&#xff0c;今天探讨两种方法&#xff0c;分别是向上调整法和向下调整法&#xff0c;分别探讨他们的时间复杂度 向上调整法&#xff08;以小堆为例&#xff09; 回…

Spring系列-04-事件机制,监听器,模块/条件装配

事件机制&监听器 SpringFramework中设计的观察者模式-掌握 SpringFramework 中, 体现观察者模式的特性就是事件驱动和监听器。监听器充当订阅者, 监听特定的事件&#xff1b;事件源充当被观察的主题, 用来发布事件&#xff1b;IOC 容器本身也是事件广播器, 可以理解成观察…

create-vue源码学习之 gradient-string 渐变色打印

效果 在使用 create-vue 脚手架时&#xff0c;想实现如下的打印效果。 探究过程 翻到源码里看到这一行 没错&#xff0c;绿色部分就是告诉我们如何生成的。可以看到引入了 gradient-string 包 于是乎&#xff0c;我来试试 pnpm i gradient-string pnpm i --save-dev …