8.3.1 限流算法
说到限流算法,脑袋中不自觉就想到了“漏 桶”与“令牌桶”。诚然,两种限流的祖师级算法确有其 独到之处,其他实现比如滑动时间窗或者三色速率标记 法等,其实质还是“漏桶”与“令牌桶”的变种,要么是 将“漏桶”容积换成了单位时间,要么是按规则将请求标
记颜色进行处理,底层还是“令牌”的思想。所以,掌 握“漏桶”与“令牌桶”算法原理,对理解其他限流算法有 一定帮助。
1.漏桶(Leaky Bucket)
漏桶的原型是一个底部有漏孔的桶,桶上方有一个 入水口,水不断地流进桶内,桶下方的漏孔就会以一个 相对恒定的速率漏水,在入大于出的情况下,桶在一段 时间之后就会被装满,这时候多余的水就会溢出;而在 入小于出的情况下,漏桶则不起任何作用。后来人们将 这个经典模型运用在网络流量整形上面,通过漏桶算法的约束,突发流量可以被整形为一个规整的流量,
如图 8-24所示。
当我们的请求或者具有一定体量的 数据流涌来的时候,在漏桶的作用下,流量被整形,不 能满足要求的部分被削减掉,所以,漏桶算法能够强制 限定流量速率。注意,在我们的应用中,这部分溢出的 流量是可以被利用起来的,并非完全丢弃,我们可以把 它们收集到一个队列里面,做流量排队,尽量做到合理 利用所有资源
2.令牌桶(Token Bucket)
令牌桶算法和漏桶算法有点不一样,桶里面存放令 牌,而令牌又是以一个恒定的速率被加入桶内,可以积 压,可以溢出。当我们的数据流涌来时,量化请求用于获取令牌,如果取到令牌则放行,同时桶内丢弃掉这个令牌;如果不能取到令牌,请求则被丢弃,如图所示。
由于令牌桶内可以存在一定数量的令牌,那么就可 能存在一定程度的流量突发,这也是决定漏桶算法与令 牌桶算法适用于不同应用场景的主要原因。
8.3.2 限流实战
在Zuul中实现限流最简单的方式是使用定义Filter加 上相关限流算法,其中可能会考虑到Zuul的多节点部 署,因为算法的原因,这时候需要一个K/V存储工具 (推荐使用Redis,充分利用Redis单线程的特性,可以 有效避免多节点带来的一些问题)。当然如果Zuul是单 节点应用,限流方式的选择就会广得多,完全可以将相 关prefix放在内存之中,方便又快捷。
这里介绍一个开箱即用的工具,spring-cloud-zuul- ratelimit(https://github.com/marcosbarbero/spring-cloud-
zuul-ratelimit),它是一位国外友人专门针对Zuul编写 的限流库,提供多种细粒度策略:
·user:认证用户名或匿名,针对某个用户粒度进行 限流。
·origin:客户机ip,针对请求客户机ip粒度进行限 流。
·url:特定url,针对某个请求url粒度进行限流。
·serviceId:特定服务,针对某个服务id粒度进行限流。
以及多种粒度临时变量存储方式:
·IN_MEMEORY:基于本地内存,底层是
ConcurrentHashMap。
·REDIS:Redis的K/V存储。
·CONSUL:Consul的K/V存储。
·JPA:Spring Data JPA,基于数据库。
·BUKET4J:一个使用Java编写的基于令牌桶算法 的限流库,其有四种模式,JCache、Hazelcast、Apache Ignite Inifinispan,其中后面三种支持异步。
在选择粒度策略与存储方式时,应该结合自己的应 用客观选取,比如Zuul如果需要多节点部署,那粒度临 时变量存储方式就不能选择MEMEORY。选择最适合 自己应用的搭配,快速构建起自己的应用。
1.zuul-server编写说明
编写配置文件bootstrap.yml
示例采用本地内存的存储方式,在3秒的时间窗口 内不能有超过2次的接口调用。到这里,它的整合就算 完成了,我们需要做的只是修改配置文件,定制我们需 要的限流策略。eureka-server与client-a服务不需要做任 何修改。