35

Spring Cloud 分布式服务限流实战,已经为你排好了

 4 years ago
source link: http://mp.weixin.qq.com/s?__biz=MzA3MTUzOTcxOQ%3D%3D&%3Bmid=2452968649&%3Bidx=1&%3Bsn=1f777b4c737cb3b14008420be8b3a942
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

点击上方“ 搜云库技术团队 ”关注,选择“ 设为星标

回复“ 1024 ”或 面试题 获取 4T架构师 资料

前言

在一个分布式高并发的系统设计中,限流是一个不可忽视的功能点。如果不对系统进行有效的流量访问限制,在双十一和抢票这种流量洪峰的场景下,很容易就会把我们的系统打垮。而作为系统服务的卫兵的网关组件,作为系统服务的统一入口,更需要考虑流量的限制,直接在网关层阻断流量比在各个系统中实现更合适。Spring Cloud Gateway的实现中,就提供了限流的功能,下面主要分析下Spring Cloud Gateway中是如何通过一段lua脚本实现限流功能的。

回顾限流算法

限流的实现方式有多种,下面先回顾下几种常见的实现算法

计数器/时间窗口法

这种限流算法最简单,也是最容易实现的,通过在单位时间内设置最大访问数就可以达到限流的目的。比如某个系统能够承载的一般qps为60,那我们就可以使用计算器法,在单位时间一秒内,限制接口只能被访问60次即可。但是这个算法实现,正如其功能描述一样,有个缺陷,假如在时间窗的前1%的时间内流量就达到顶峰了,那么在时间窗内还有99%的时间系统即使能够继续提供服务,还是会被限流算法的这种缺陷阻断在门外,这种缺陷也被称为“突刺效应“

nmEzyaJ.png!web

漏桶法

漏桶法不同于计算器法,它有效的避免了计数器法限流的“突刺效应”缺陷,实现也不复杂,通过固定大小的队列+定时取队列元素的方式即可实现。如其名漏桶,就像一个盛水的容器,漏桶法只限制容器出水的速率,当进水的速率过大时,将会填满容器造成溢出,溢出部分的流量也就是拒绝的流量。比如,容器大小为100,出水速率为每秒10/s,当桶为空时,最大的流量可以到达100/s,但是即使这样,受限于固定的流出速率,后端处理的也只能是最大每秒10个,其余的流量都会被缓冲在漏桶中。这个也这是漏桶法的缺陷,没法真正处理突发的流量洪峰,效率不高。

7N7rQbn.png!web

令牌桶法

令牌桶法也是基于桶的原型,但是和漏桶算法截然不同的时,没有出水口。令牌桶通过令牌的产生速率+令牌桶的容积来控制流量,有效的解决了漏桶效率不高的问题。如,容积为100的桶,令牌产生速率为50/s,那么就代表当桶中令牌已满的时候,最大能够承载100的流量,后面如果流量一直居高不下,也会以每秒50个流量的速度恒速处理请求。令牌桶的这种特性有效的处理了洪峰流量也能做到不被洪峰压垮,是目前限流比较常见的实现方法。比较著名的实现有谷歌guava中的RateLimiter。然后下面将要分析的Spring Cloud Gateway中也是使用的令牌桶算法实现的限流

guava的文档:https://github.com/google/guava/wiki

MZzuYrB.png!web

Spring Cloud Gateway中的令牌桶

Spring网关中是基于令牌桶+redis实现的网关分布式限流,具体的实现见下面两个代码:

lua脚本地址:resources/META-INF/scripts/requestratelimiter.lua

RedisRateLimiter:gateway/filter/ratelimit/RedisRateLimiter.java

上面博主截取了Spring网关限流部分的关键代码,可以看到,最关键的地方在于,使用reids执行了一段lua脚本,然后通过返回值【0】是否等于1来判断本次流量是否通过,返回值【1】为令牌桶中剩余的令牌数。就上面这段代码没有看到任何令牌桶算法的影子对吧,所有的精华实现都在requestratelimiter.lua脚本里面,这个脚本最初是由Paul Tarjan分享出来的

脚本如下:

下面逐行分析下这段脚本。首先解释下,从应用中入参进来的这几个属性的具体含义:

1、tokens key: 当前限流的标识,可以是ip,或者在spring cloud系统中,可以是一个服务的serviceID

2、timestamp key: 令牌桶刷新的时间戳,后面会被用来计算当前产生的令牌数 

3、rate :令牌生产的速率,如每秒产生50个令牌

4、capacity :令牌桶的容积大小,比如最大100个,那么系统最大可承载100个并发请求

5、now :当前时间戳

6、requested:当前请求的令牌数量,Spring Cloud Gateway中默认是1,也就是当前请求

下面是主要逻辑分析:

-- 计算填满桶需要多长时间 -- 得到填满桶的2倍时间作为redis中key时效的时间,避免冗余太多无用的key -- 这里和令牌桶的实现没有太大的关系 -- 获取桶中剩余的令牌,如果桶是空的,就将他填满 -- 获取当前令牌桶最后的刷新时间,如果为空,则设置为0 -- 计算最后一次刷新令牌到当前时间的时间差 -- 计算当前令牌数量,这个地方是最关键的地方,通过剩余令牌数 + 时间差内产生的令牌得到当前总令牌数量 -- 设置标识allowad接收当前令牌桶中的令牌数是否大于请求的令牌结果 -- 设置当前令牌数量 -- 如果allowed为true,则将当前令牌数量重置为通中的令牌数 - 请求的令牌数,并且设置allowed_num标识为1 -- 将当前令牌数量写回到redis中,并重置令牌桶的最后刷新时间 -- 返回当前是否申请到了令牌,以及当前桶中剩余多少令牌

来源:http://suo.im/4BkER7

近期技术热文

1、 面试官: Redis功能强大,那也顶不住被滥用啊!  

2、 公司Linux服务器被非法入侵,秒变“矿机”,挖挖挖...  

3、 SpringBoot 整合 Spring-Kafka 深度踩坑实战  

4、 消息队列中: 消息可靠性、重复消息、消息积压  

5、 Redis缓存雪崩、缓存击穿、缓存穿透及缓存模式  

6、 牛逼! 96秒100亿,阿里到底做了什么?

BVnmAvj.png!web


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK