`
m635674608
  • 浏览: 5055264 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

接口限流算法总结

 
阅读更多

背景

曾经在一个大神的博客里看到这样一句话:在开发高并发系统时,有三把利器用来保护系统:缓存、降级和限流。那么何为限流呢?顾名思义,限流就是限制流量,就像你宽带包了1个G的流量,用完了就没了。通过限流,我们可以很好地控制系统的qps,从而达到保护系统的目的。本篇文章将会介绍一下常用的限流算法以及他们各自的特点。

算法介绍

计数器法

计 数器法是限流算法里最简单也是最容易实现的一种算法。比如我们规定,对于A接口来说,我们1分钟的访问次数不能超过100个。那么我们可以这么做:在一开 始的时候,我们可以设置一个计数器counter,每当一个请求过来的时候,counter就加1,如果counter的值大于100并且该请求与第一个 请求的间隔时间还在1分钟之内,那么说明请求数过多;如果该请求与第一个请求的间隔时间大于1分钟,且counter的值还在限流范围内,那么就重置 counter,具体算法的示意图如下:

2016-09-01_20:31:28.jpg

具体的伪代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class CounterDemo {
public long timeStamp = getNowTime();
public int reqCount = 0;
public final int limit = 100; // 时间窗口内最大请求数
public final long interval = 1000; // 时间窗口ms
public boolean grant() {
long now = getNowTime();
if (now < timeStamp + interval) {
// 在时间窗口内
reqCount++;
// 判断当前时间窗口内是否超过最大请求控制数
return reqCount <= limit;
}
else {
timeStamp = now;
// 超时后重置
reqCount = 1;
return true;
}
}
}

这个算法虽然简单,但是有一个十分致命的问题,那就是临界问题,我们看下图:

2016-09-01_20:35:21.jpg

从上图中我们可以看到,假设有一个恶意用户,他在0:59时,瞬间发送了100个请求,并且1:00又瞬间发送了100个请求,那么其实这个用户在 1秒里面,瞬间发送了200个请求。我们刚才规定的是1分钟最多100个请求,也就是每秒钟最多1.7个请求,用户通过在时间窗口的重置节点处突发请求, 可以瞬间超过我们的速率限制。用户有可能通过算法的这个漏洞,瞬间压垮我们的应用。

聪明的朋友可能已经看出来了,刚才的问题其实是因为我们统计的精度太低。那么如何很好地处理这个问题呢?或者说,如何将临界问题的影响降低呢?我们可以看下面的滑动窗口算法。

滑动窗口

滑动窗口,又称rolling window。为了解决这个问题,我们引入了滑动窗口算法。如果学过TCP网络协议的话,那么一定对滑动窗口这个名词不会陌生。下面这张图,很好地解释了滑动窗口算法:

2016-09-01_20:42:46.jpg

在上图中,整个红色的矩形框表示一个时间窗口,在我们的例子中,一个时间窗口就是一分钟。然后我们将时间窗口进行划分,比如图中,我们就将滑动窗口 划成了6格,所以每格代表的是10秒钟。每过10秒钟,我们的时间窗口就会往右滑动一格。每一个格子都有自己独立的计数器counter,比如当一个请求 在0:35秒的时候到达,那么0:30~0:39对应的counter就会加1。

那么滑动窗口怎么解决刚才的临界问题的呢?我们可以看上图,0:59到达的100个请求会落在灰色的格子中,而1:00到达的请求会落在橘黄色的格 子中。当时间到达1:00时,我们的窗口会往右移动一格,那么此时时间窗口内的总请求数量一共是200个,超过了限定的100个,所以此时能够检测出来触 发了限流。

我再来回顾一下刚才的计数器算法,我们可以发现,计数器算法其实就是滑动窗口算法。只是它没有对时间窗口做进一步地划分,所以只有1格。

由此可见,当滑动窗口的格子划分的越多,那么滑动窗口的滚动就越平滑,限流的统计就会越精确。

漏桶算法

漏桶算法,又称leaky bucket。为了理解漏桶算法,我们看一下维基百科上的对于该算法的示意图:

2016-09-02_09:57:32.jpg

从图中我们可以看到,整个算法其实十分简单。首先,我们有一个固定容量的桶,有水流进来,也有水流出去。对于流进来的水来说,我们无法预计一共有多 少水会流进来,也无法预计水流的速度。但是对于流出去的水来说,这个桶可以固定水流出的速率。而且,当桶满了之后,多余的水将会溢出。

我们将算法中的水换成实际应用中的请求,我们可以看到漏桶算法天生就限制了请求的速度。当使用了漏桶算法,我们可以保证接口会以一个常速速率来处理请求。所以漏桶算法天生不会出现临界问题。具体的伪代码实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class LeakyDemo {
public long timeStamp = getNowTime();
public int capacity; // 桶的容量
public int rate; // 水漏出的速度
public int water; // 当前水量(当前累积请求数)
public boolean grant() {
long now = getNowTime();
water = max( 0, water - (now - timeStamp) * rate); // 先执行漏水,计算剩余水量
timeStamp = now;
if ((water + 1) < capacity) {
// 尝试加水,并且水还未满
water += 1;
return true;
}
else {
// 水满,拒绝加水
return false;
}
}
}

令牌桶算法

令牌桶算法,又称token bucket。为了理解该算法,我们再来看一下维基百科上对该算法的示意图:

2016-09-02_10:10:24.jpg

从图中我们可以看到,令牌桶算法比漏桶算法稍显复杂。首先,我们有一个固定容量的桶,桶里存放着令牌(token)。桶一开始是空的,token以 一个固定的速率r往桶里填充,直到达到桶的容量,多余的令牌将会被丢弃。每当一个请求过来时,就会尝试从桶里移除一个令牌,如果没有令牌的话,请求无法通 过。

具体的伪代码实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class TokenBucketDemo {
public long timeStamp = getNowTime();
public int capacity; // 桶的容量
public int rate; // 令牌放入速度
public int tokens; // 当前令牌数量
public boolean grant() {
long now = getNowTime();
// 先添加令牌
tokens = min(capacity, tokens + (now - timeStamp) * rate);
timeStamp = now;
if (tokens < 1) {
// 若不到1个令牌,则拒绝
return false;
}
else {
// 还有令牌,领取令牌
tokens -= 1;
return true;
}
}
}

相关变种

若仔细研究算法,我们会发现我们默认从桶里移除令牌是不需要耗费时间的。如果给移除令牌设置一个延时时间,那么实际上又采用了漏桶算法的思路。Google的guava库下的SmoothWarmingUp类就采用了这个思路。

临界问题

我 们再来考虑一下临界问题的场景。在0:59秒的时候,由于桶内积满了100个token,所以这100个请求可以瞬间通过。但是由于token是以较低的 速率填充的,所以在1:00的时候,桶内的token数量不可能达到100个,那么此时不可能再有100个请求通过。所以令牌桶算法可以很好地解决临界问 题。下图比较了计数器(左)和令牌桶算法(右)在临界点的速率变化。我们可以看到虽然令牌桶算法允许突发速率,但是下一个突发速率必须要等桶内有足够的 token后才能发生:

2016-09-02_14:40:58.jpg

总结

计数器 VS 滑动窗口

计数器算法是最简单的算法,可以看成是滑动窗口的低精度实现。滑动窗口由于需要存储多份的计数器(每一个格子存一份),所以滑动窗口在实现上需要更多的存储空间。也就是说,如果滑动窗口的精度越高,需要的存储空间就越大。

漏桶算法 VS 令牌桶算法

漏桶算法和令牌桶算法最明显的区别是令牌桶算法允许流量一定程度的突发。因为默认的令牌桶算法,取走token是不需要耗费时间的,也就是说,假设桶内有100个token时,那么可以瞬间允许100个请求通过。

令牌桶算法由于实现简单,且允许某些流量的突发,对用户友好,所以被业界采用地较多。当然我们需要具体情况具体分析,只有最合适的算法,没有最优的算法。

 

https://my.oschina.net/sbcagf/blog/783082

分享到:
评论

相关推荐

    SpringBoot如何使用AOP+Redis实现接口限流实现全过程(值得珍藏)

    总结:通过 Spring Boot 结合 AOP 和 Redis,我们可以轻松实现接口限流,保护系统免受过量请求的影响。这种方式既灵活又高效,能够适应各种不同的限流需求。在实践中,根据实际情况调整限流策略,结合监控工具,可以...

    接口限流——令牌桶算法2

    **接口限流与令牌桶算法** 在高并发的分布式系统中,为了保护系统稳定性和服务质量,接口限流是一种常见的策略。它主要用于防止恶意用户或异常情况导致系统资源被过度消耗,确保关键服务的可用性。令牌桶算法是实现...

    基于令牌桶算法实现的SpringBoot无锁限流插件

    总结来说,基于令牌桶算法的SpringBoot无锁限流插件为Java Web开发提供了一种高效且灵活的流量控制解决方案。通过方法和系统级别的限流策略,以及快速失败和CAS阻塞模式,它能够帮助我们构建更健壮、响应更快的应用...

    详解Java分布式IP限流和防止恶意IP攻击方案

    同时,也可以使用其他技术和工具来实现限流和防止恶意IP攻击,例如使用机器学习算法来识别恶意IP。 本文提供了一种可行的Java分布式IP限流和防止恶意IP攻击方案,希望能够帮助开发者和架构师更好地解决分布式系统中...

    网关 gateway 动态路由 及 redis 集成限流

    常见的限流算法有固定窗口、滑动窗口和令牌桶等。为了实现动态限流,我们可以结合Redis的分布式锁或者原子操作来控制并发请求的数量。 集成Redis实现限流,可以创建一个限流策略,例如基于每个服务实例的QPS(每秒...

    基于RateLimiter和Lua脚本限量控制实现分布式限流.docx

    本文主要探讨了两种限流方式:基于`RateLimiter`令牌桶算法的限速控制以及基于Lua脚本的限量控制,并结合Redis进行分布式环境下的限流处理。 #### 二、基于RateLimiter令牌桶算法的限速控制 ##### 2.1 令牌桶模型...

    分布式环境下限流方案的实现redis RateLimiter Guava,Token Bucket, Leaky Bucket1

    总结起来,分布式环境下的限流涉及到多个层面的技术选择和设计考虑。Guava RateLimiter在单节点环境中表现出色,但在分布式系统中需要配合Redis等分布式协调工具来确保全局限流的准确性。Redis限流方案通过全局共享...

    7应用 6:断尾求生 —— 简单限流(3).md

    限流算法可以帮助系统在高负载情况下存活下来,这就是所谓的“断尾求生”。在本文中,老钱用“断尾求生”来形容限流背后的思想,其实还有其他成语可以表达类似的意思,比如“弃卒保车”和“壮士断腕”。 #### 2. 限...

    SpringMVC 限流的示例代码

    通过 Guava 的 `RateLimiter` 类和 SpringMVC 的拦截器机制,我们可以轻松地在应用中实现接口限流。这有助于防止系统过载,确保服务的稳定性和响应速度。然而,需要注意的是,限流策略的选择应根据实际业务需求来...

    three.js算法寻路示例

    总结来说,这个"three.js算法寻路示例"是一个综合项目,涉及到了3D图形编程、前端框架的应用以及路径规划算法的实现。通过学习和理解这个示例,开发者可以提升在WebGL、three.js、Vue和路径寻找算法方面的技能,这在...

    linux平台下运行的跟踪算法

    总结来说,Linux平台上的跟踪算法实现涉及多个层次,包括硬件接口的驱动开发、跟踪算法的选择与优化、以及软件工程实践。这个过程既需要深入理解计算机视觉原理,也要掌握嵌入式系统的开发技巧,确保在资源受限的...

    Golang实现请求限流的几种办法(小结)

    Token Bucket是一种常用的限流算法,它维护一个令牌桶,桶内有一定容量的令牌,每次处理请求需要消耗一个令牌。当令牌用完时,新的请求会被限制。Golang可以通过定时器或Channel实现这种机制。例如,可以定期向桶中...

    Symja计算机代数语言符号数学库一个用纯Java实现的流行算法集合.zip

    总结来说,Symja是一个强大且灵活的Java和Android符号数学库,提供了丰富的数学算法,对于需要进行复杂数学计算的开发者来说是一个宝贵的工具。通过深入理解和应用Symja,开发者可以提升其应用程序的计算能力和解决...

    令牌桶算法的基本过程

    - **API接口限流**:防止恶意请求导致服务过载。 - **CDN流量控制**:确保视频流媒体等大数据量传输的稳定性。 - **路由器带宽管理**:合理分配网络资源,避免关键业务受到非关键流量的影响。 #### 六、总结 令牌...

    爬虫 算法 Java描述

    Java提供了多种数据库接口,如JDBC,可以连接MySQL、Oracle等关系型数据库,或者使用NoSQL数据库如MongoDB来存储非结构化数据。此外,文件系统的操作(如I/O流)也能用于保存文本或CSV格式的数据。 总结,Java爬虫...

    当当网高可用移动入口与搜索技术架构.docx

    常见的限流算法包括漏桶和令牌桶,前者控制回调洪峰,后者适用于用户洪峰。为了优化用户体验,当当网通过防刷算法拒绝疑似攻击请求,并优先服务会员和正在进行重要操作的用户。 2. **限流引擎子系统**: - 限流...

    基于.net的分布式系统限流组件示例详解

    令牌桶算法是限流策略中常见的一种,它通过模拟令牌的生成和消耗来控制请求的处理速度。算法的核心包括以下三个阶段: 1. **产生令牌**:系统按照预设的平均速率(r)定时向令牌桶中添加令牌。桶的最大容量为b,当...

    当当api接口商家文档

    #### 总结 当当商家API操作手册是商家接入当当平台的重要指南,涵盖了从接口申请到功能使用、从系统级参数到具体业务逻辑的全面内容,旨在提升商家运营效率,优化用户体验。通过对本手册的深入学习,商家可以充分...

    路由器体系结构及路由算法研究进展3

    本文将对近年来高性能路由器所采用的主要体系结构和路由算法研究成果进行概括总结和分析,并系统地阐述路由器体系结构的关键问题,讨论几种典型的路由算法,并对它们的性能进行比较。 #### 二、路由器体系结构 ###...

    MUSIC算法的研究及DSP实现.pdf

    总结来说,MUSIC算法的DSP实现涉及信号处理、实时计算、硬件设计等多个层面的知识点。它不仅需要深厚的理论基础,还需要在算法优化、系统设计和硬件实现方面具备丰富的实践经验。MUSIC算法的研究及DSP实现是一门综合...

Global site tag (gtag.js) - Google Analytics