限流消峰的三种办法

风来了 风来了     2022-09-17     483

关键词:

互联网服务赖以生存的根本是流量, 产品和运营会经常通过各种方式来为应用倒流,比如淘宝的双十一等,如何让系统在处理高并发的同时还是保证自身系统的稳定,

通常在最短时间内提高并发的做法就是加机器, 但是如果机器不够怎么办? 那就需要做业务降级或系统限流。

流量控制中用的比较多的三个算法就是令牌桶、漏桶、计数器。

一、令牌桶限流(TokenBucket)
令牌桶算法的基本过程如下:

  1. 每秒会有 r 个令牌放入桶中,或者说,每过 1/r 秒桶中增加一个令牌。
  2. 桶中最多存放 b 个令牌,如果桶满了,新放入的令牌会被丢弃。
  3. 当一个 n 字节的数据包到达时,消耗 n 个令牌,然后发送该数据包。
  4. 如果桶中可用令牌小于 n,则该数据包将被缓存或丢弃。

     

 

二、漏桶限流(LeakBucket)

漏桶算法强制一个常量的输出速率而不管输入数据流的突发性

(1)当输入空闲时,该算法不执行任何动作.就像用一个底部开了个洞的漏桶接水一样, 水进入到漏桶里, 桶里的水通过下面的孔以固定的速率流出

(2)当水流入速度过大会直接溢出,可以看出漏桶算法能强行限制数据的传输速率.如下图所示:


 

漏桶和令牌桶比较

“漏桶算法”能够强行限制数据的传输速率,而“令牌桶算法”在能够限制数据的平均传输数据外,还允许某种程度的突发传输。

在“令牌桶算法”中,只要令牌桶中存在令牌,那么就允许突发地传输数据直到达到用户配置的上限,因此它适合于具有突发特性的流量。

三、计数器限流

有时我们还会使用计数器来进行限流,主要用来限制一定时间内的总并发数。计数器限流方法可以通过缓存实现计数器,假如以秒为单位进行限流,

过期时间为1秒,每次请求计数加1,超过每秒允许的最大请求数请求数阀值将被丢弃。

 

四、nginx 实现漏桶

    #以用户二进制IP地址,定义三个漏桶,滴落速率1-3req/sec,桶空间1m,1M能保持大约16000个(IP)状态
    limit_req_zone  $binary_remote_addr  zone=qps1:1m   rate=1r/s;
    limit_req_zone  $binary_remote_addr  zone=qps2:1m   rate=2r/s;
    limit_req_zone  $binary_remote_addr  zone=qps3:1m   rate=3r/s;
     
    server {
     
        #速率qps=1,峰值burst=5,延迟请求
        #严格按照漏桶速率qps=1处理每秒请求
        #在峰值burst=5以内的并发请求,会被挂起,延迟处理
        #超出请求数限制则直接返回503
        #客户端只要控制并发在峰值[burst]内,就不会触发limit_req_error_log
        # 例1:发起一个并发请求=6,拒绝1个,处理1个,进入延迟队列4个:
        #time    request    refuse    sucess    delay
        #00:01        6        1        1            4
        #00:02        0        0        1            3
        #00:03        0        0        1            2
        #00:04        0        0        1            1
        #00:05        0        0        1            0
        location /delay {
            limit_req   zone=qps1  burst=5;
        }
         
        #速率qps=1,峰值burst=5,不延迟请求
        #加了nodelay之后,漏桶控制一段时长内的平均qps = 漏桶速率,允许瞬时的峰值qps > 漏桶qps
        #所以峰值时的最高qps=(brust+qps-1)=5
        #请求不会被delay,要么处理,要么直接返回503
        #客户端需要控制qps每秒请求数,才不会触发limit_req_error_log
        # 例2:每隔5秒发起一次达到峰值的并发请求,由于时间段内平均qps=1 所以仍然符合漏桶速率:
        #time    request     refuse    sucess
        #00:01         5         0          5
        #00:05         5         0          5
        #00:10         5         0          5
        # 例3:连续每秒发起并发请求=5,由于时间段内平均qps>>1,超出的请求被拒绝:
        #time    request     refuse     sucess
        #00:01         5         0           5
        #00:02         5         4           1
        #00:03         5         4           1
         
        location /nodelay {
            limit_req   zone=qps1  burst=5 nodelay;
        }
     
    }

 

 limit_req_module

五、redis

local key = KEYS[1] --限流KEY(一秒一个)
local limit = tonumber(ARGV[1])        --限流大小
local current = tonumber(redis.call('get', key) or "0")
if current + 1 > limit then --如果超出限流大小
    redis.call("INCRBY", key,"1") -- 如果不需要统计真是访问量可以不加这行
    return 0
else  --请求数+1,并设置2秒过期
    redis.call("INCRBY", key,"1")
    if tonumber(ARGV[2]) > -1 then
        redis.call("expire", key,tonumber(ARGV[2])) --时间窗口最大时间后销毁键
    end
    return 1
end

lua脚本返回值比较奇怪,用java客户端接受返回值,只能使用Long,没有去深究。这个脚本只需要传入key(url+时间戳/预设时间窗口大小),便可以实现限流。 

java调用lua脚本

/**
 * Created by xujingfeng on 2017/3/13.
 * <p>
 * 基于redis lua脚本的线程安全的计数器限流方案
 * </p>
 */
public class RedisRateLimiter {

    /**
     * 限流访问的url
     */
    private String url;

    /**
     * 单位时间的大小,最大值为 Long.MAX_VALUE - 1,以秒为单位
     */
    final Long timeUnit;

    /**
     * 单位时间窗口内允许的访问次数
     */
    final Integer limit;

    /**
     * 需要传入一个lua script,莫名其妙redisTemplate返回值永远是个Long
     */
    private RedisScript<Long> redisScript;

    private RedisTemplate redisTemplate;

    /**
     * 配置键是否会过期,
     * true:可以用来做接口流量统计,用定时器去删除
     * false:过期自动删除,时间窗口过小的话会导致键过多
     */
    private boolean isDurable = false;

    public void setRedisScript(RedisScript<Long> redisScript) {
        this.redisScript = redisScript;
    }

    public void setRedisTemplate(RedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public boolean isDurable() {
        return isDurable;
    }

    public void setDurable(boolean durable) {
        isDurable = durable;
    }

    public RedisRateLimiter(Integer limit, Long timeUnit) {
        this.timeUnit = timeUnit;
        Assert.isTrue(timeUnit < Long.MAX_VALUE - 1);
        this.limit = limit;
    }

    public RedisRateLimiter(Integer limit, Long timeUnit, boolean isDurable) {
        this(limit, timeUnit);
        this.isDurable = isDurable;
    }

    public boolean acquire() {
        return this.acquire(this.url);
    }

    public boolean acquire(String url) {
        StringBuffer key = new StringBuffer();
        key.append("rateLimiter").append(":")
                .append(url).append(":")
                .append(System.currentTimeMillis() / 1000 / timeUnit);
        Integer expire = limit + 1;
        String convertExpire = isDurable ? "-1" : expire.toString();
        return redisTemplate.execute(redisScript, Arrays.asList(key.toString()), limit.toString(), convertExpire).equals(1l);
    }

}

 

5.spring mvc

RateLimiter

我们可以使用 Guava 的 RateLimiter 来实现基于令牌桶的流量控制。RateLimiter 令牌桶算法的单桶实现,RateLimiter 对简单的令牌桶算法做了一

些工程上的优化,具体的实现是 SmoothBursty。需要注意的是,RateLimiter 的另一个实现 SmoothWarmingUp,就不是令牌桶了,而是漏桶算法。

SmoothBursty 有一个可以放 N 个时间窗口产生的令牌的桶,系统空闲的时候令牌就一直攒着,最好情况下可以扛 N 倍于限流值的高峰而不影响

后续请求,就像三峡大坝一样能扛千年一遇的洪水.

 

 

 



作者:Lewe
链接:http://www.jianshu.com/p/7170edcd9239
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 

eclipse项目报错解决的三种办法

项目报错解决的三种办法系列文章目录文章目录项目报错解决的三种办法系列文章目录问题解决问题比如这种解决1、导入项目之前,请确认工作空间编码已设置为utf-8window->Preferences->General->Wrokspace->Textfileencoding->Ot... 查看详情

java中outofmemoryerror(内存溢出)的三种情况及解决办法

相信有一定java开发经验的人或多或少都会遇到OutOfMemoryError的问题,这个问题曾困扰了我很长时间,随着解决各类问题经验的积累以及对问题根源的探索,终于有了一个比较深入的认识。在解决java内存溢出问题之前,需要对jvm(... 查看详情

java中outofmemoryerror(内存溢出)的三种情况及解决办法

相信有一定java开发经验的人或多或少都会遇到OutOfMemoryError的问题,这个问题曾困扰了我很长时间,随着解决各类问题经验的积累以及对问题根源的探索,终于有了一个比较深入的认识。在解决java内存溢出问题之前&#x... 查看详情

防止表单重复提交的三种方法

防止重复提交的办法-----------------------------------------------------------------jquery的方法$("#btn").one("click",function()//只触发一次  YouMethod(););    ---------------------------------------------- 查看详情

mysql创建索引的三种办法

参考技术A一、CREATEINDEX方法CREATEINDEX<索引名>ON<表名>(<列名>[<长度>][ASC|DESC])限制:只能增加普通索引INDEX和UNIQUEINDEX索引这两种;不能创建PRIMARYKEY索引二、ALTERTABLE方法ALTERTABLE<表名>ADDINDEX[<索引名>](<... 查看详情

js中常用的方法-json.xxx/js的三种判断一个值的类型的办法(代码片段)

JSON. parse()字符串转对象.conststr=‘"name":"phoebe","age":20‘;constobj=JSON.parse(str);//name:"phoebe",age:20(object类型)JSON.stringify()用于把对象转化为字符串。typeof123//numbertypeof‘123‘//stringtypeoftrue//bo 查看详情

windows系统桌面壁纸切换的三种csharp办法,兼容win10及旧版,还有一个现成桌面小程序

我自己用这些代码做的小app如下:第一种,user32.dll///<summary>///调用外部切换壁纸的方法///</summary>///<paramname="uAction"></param>///<paramname="uParam"></param>/// 查看详情

centos软件安装的三种方式

Linux下面安装软件的常见方法:1.yum替你下载软件替你安装替你解决依赖关系点外卖缺少的东西外卖解决1).方便简单2)没有办法深入修改yuminstall-ytree2.rpm自己下载软件包自己安装自己解决依赖半成品缺少的东西自己解决1)安装解决依... 查看详情

html中正确设置表格table边框border的三种办法

参考技术Ahtml中如果设置table的border为1px,实际会产生双线,十分不美观,那么在HTML中如何正确设置表格table边框border呢?第一种方法:1、将table的属性设置为:BORDER=0、cellspacing=1;2、设置table的背景色为即你要设置的table的边框... 查看详情

三种常见的限流算法(代码片段)

...高并发系统时,有三把利器用来保护系统:缓存、降级和限流。那么何为限流呢?顾名思义,限流就是限制流量,就像你宽带包了1个G的流量,用完了就没了。通过限流,我们可以很好地控制系统的qps,从而达到保护系统的目的... 查看详情

2021-09-06el-tree懒加载load手动触发load更新的三种方法

参考技术A解决办法:繁琐容易出错,做过各种尝试都不太理想,不再深究。添加节点方法解决办法:可以与后台协商,在添加成功对接口里返回该节点对ID,然后append节点的时候,把此节点的ID也添加进去,就OK了。 查看详情

vc操作image的三种方法(收集)

忘记从哪来收集过来的资料了,暂且不管是哪位老兄写的,只道一声谢谢。仅管VC有提供相应的API和类来操作bmp位图、图标和(增强)元文件,但却不支持jpg、gif和png等格式的图片,而这几种格式却是常常要用到的。这里我给大... 查看详情

解决ajax发送post请求出现403forbidden的三种方式(代码片段)

众所周知前端向后台发送post请求时,必须验证csrf,否则会报错403Forbidden。使用DjangoForm表单可以直接在表单里面添加%csrf_token%即可,要是通过Ajax发送请求又该怎么办?下面提供三种解决办法:<ulid="ddd"><li>1</li&... 查看详情

html5结合css的三种方法+结合js的三种方法

HTML5+CSS:HTML中应用CSS的三种方法一、内联内联样式通过style属性直接套进HTML中去。示例代码<pstylepstyle="color:red">text</p> 这将会是指定的段落变成红色。我们的建议是,HTML应该是独立的、样式自由的文档,所以内联样... 查看详情

vlan的三种类型及三种属性

查看详情

js的三种使用方式/css的三种使用方式/js中的dom事件模型/js中匿名函数的书写及调用/媒体查询@media的三种使用方式

一、JS的三种使用方式      1、html标签中内嵌JS(不提倡使用。)            <buttononclick="javascript:alert(‘你真点啊。‘)">有本事点我呀!!!!</button>  &nbs 查看详情

秒杀链路兜底方案之限流&降级实战(代码片段)

...下微服务gateway网关和Sentinel相关知识秒杀链路兜底方案之限流&降级实战一、秒杀场景介绍1.1秒杀场景的特点1.2流量消峰1.3兜底方案二、限流实战2.1nginx限流(h 查看详情

swap分区扩展的三种方法

redhat linux swap分区扩展的三种方法 2016-12-26 11:41:08分类: LINUX原文地址:redhat linux swap分区扩展的三种方法 作者:quanshengaaredhat linux swap分区扩展的三种方法swap 介绍:当物理内存占用完了... 查看详情