注:本文已发布超过一年,请注意您所使用工具的相关版本是否适用
本系列为 Go 进阶训练营 笔记,访问 博客: Go进阶训练营, 即可查看当前更新进度,部分文章篇幅较长,使用 PC 大屏浏览体验更佳。
在前面两篇文章当中我们学习了令牌桶算法的使用和实现,今天我们就一起来看一看另外一种常见的限流算法,漏桶算法
漏桶算法(Leaky Bucket) 是网络世界中流量整形(Traffic Shaping)或速率限制(Rate Limiting)时经常使用的一种算法,它的主要目的是控制数据注入到网络的速率,平滑网络上的突发流量。漏桶算法提供了一种机制,通过它,突发流量可以被整形以便为网络提供一个稳定的流量。 — 百度百科
漏桶算法其实非常形象,如下图所示可以理解为一个漏水的桶,当有突发流量来临的时候,会先到桶里面,桶下有一个洞,可以以固定的速率向外流水,如果水的从桶中外溢了出来,那么这个请求就会被拒绝掉。具体的表现就会向下图右侧的图表一样,突发流量就被整形成了一个平滑的流量。
漏桶算法的主要作用就是避免出现有的时候流量很高,有的时候又很低,导致系统出现旱的旱死,涝的涝死的这种情况。
Go 中比较常用的漏桶算法的实现就是来自 uber 的 ratelimit,下面我们就会看一下这个库的使用方式和源码
1 | |
Clock 是一个接口,计时器的最小实现,有两个方法,分别是当前的时间和睡眠
1 | |
Limiter 也是一个接口,只有一个 Take 方法,执行这个方法的时候如果触发了 rps 限制则会阻塞住
1 | |
NewLimter 和 NewUnlimited 会分别初始化一个无锁的限速器和没有任何限制的限速器
Option 是在初始化的时候的额外参数,这种使用姿势在之前 Go 工程化的文章《Go 工程化(六) 配置管理》当中有讲到,这里我们就不再赘述了
Option 有三个方法
Per 可以修改时间单位,默认是秒所以我们默认限制的是 rps,如果改成分钟那么就是 rpm 了WithClock 可以修改时钟,这个用于在测试的时候可以 mock 掉不使用真实的时间WithSlack 用于修改松弛时间,也就是可以允许的突发流量的大小,默认是 Pre / 10 ,这个后面会讲到案例我们使用和令牌桶类似的案例
1 | |
使用上也是比较简单的
1 | |
我们用 go-stress-testing 进行压测
1 | |
查看结果发现为什么第一秒的时候完成了 13 个请求,不是限制的 3rps 么?不要慌,我们看看它的实现就知道了
这个库有基于互斥锁的实现和基于 CAS 的无锁实现,默认使用的是无锁实现版本,所以我们主要看无锁实现的源码
1 | |
atomicLimiter 结构体
state 是一个状态的指针,用于存储上一次的执行的时间,以及需要 sleep 的时间padding 是一个无意义的填充数据,为了提高性能,避免 cpu 缓存的 false sharing64 - 8 = 56Option 中的三个方法意义对应perRequest 就是单位,默认是秒maxSlack 松弛时间,也就是可以允许的突发流量的大小,默认是 Pre / 10 ,这个后面会讲到clock 时钟,这个用于在测试的时候可以 mock 掉不使用真实的时间接下来看看最主要的 Take 方法
1 | |
今天学习了漏桶的实现原理以及使用方式,漏桶和令牌桶的最大的区别就是,令牌桶是支持突发流量的,但是漏桶是不支持的。但是 uber 的这个库通过引入弹性时间的方式也让漏桶算法有了类似令牌桶能够应对部分突发流量的能力,并且实现上还非常的简单,值得学习。
多看看好的轮子的实现总会学到一些新姿势,今天就学到了使用 padding 填充来避免 false sharing 提高性能的操作
注:本文已发布超过一年,请注意您所使用工具的相关版本是否适用
本系列为 Go 进阶训练营 笔记,访问 博客: Go进阶训练营, 即可查看当前更新进度,部分文章篇幅较长,使用 PC 大屏浏览体验更佳。
在前面两篇文章当中我们学习了令牌桶算法的使用和实现,今天我们就一起来看一看另外一种常见的限流算法,漏桶算法
漏桶算法(Leaky Bucket) 是网络世界中流量整形(Traffic Shaping)或速率限制(Rate Limiting)时经常使用的一种算法,它的主要目的是控制数据注入到网络的速率,平滑网络上的突发流量。漏桶算法提供了一种机制,通过它,突发流量可以被整形以便为网络提供一个稳定的流量。 — 百度百科
漏桶算法其实非常形象,如下图所示可以理解为一个漏水的桶,当有突发流量来临的时候,会先到桶里面,桶下有一个洞,可以以固定的速率向外流水,如果水的从桶中外溢了出来,那么这个请求就会被拒绝掉。具体的表现就会向下图右侧的图表一样,突发流量就被整形成了一个平滑的流量。
漏桶算法的主要作用就是避免出现有的时候流量很高,有的时候又很低,导致系统出现旱的旱死,涝的涝死的这种情况。
Go 中比较常用的漏桶算法的实现就是来自 uber 的 ratelimit,下面我们就会看一下这个库的使用方式和源码
1 | |
Clock 是一个接口,计时器的最小实现,有两个方法,分别是当前的时间和睡眠
1 | |
Limiter 也是一个接口,只有一个 Take 方法,执行这个方法的时候如果触发了 rps 限制则会阻塞住
1 | |
NewLimter 和 NewUnlimited 会分别初始化一个无锁的限速器和没有任何限制的限速器
Option 是在初始化的时候的额外参数,这种使用姿势在之前 Go 工程化的文章《Go 工程化(六) 配置管理》当中有讲到,这里我们就不再赘述了
Option 有三个方法
Per 可以修改时间单位,默认是秒所以我们默认限制的是 rps,如果改成分钟那么就是 rpm 了WithClock 可以修改时钟,这个用于在测试的时候可以 mock 掉不使用真实的时间WithSlack 用于修改松弛时间,也就是可以允许的突发流量的大小,默认是 Pre / 10 ,这个后面会讲到案例我们使用和令牌桶类似的案例
1 | |
使用上也是比较简单的
1 | |
我们用 go-stress-testing 进行压测
1 | |
查看结果发现为什么第一秒的时候完成了 13 个请求,不是限制的 3rps 么?不要慌,我们看看它的实现就知道了
这个库有基于互斥锁的实现和基于 CAS 的无锁实现,默认使用的是无锁实现版本,所以我们主要看无锁实现的源码
1 | |
atomicLimiter 结构体
state 是一个状态的指针,用于存储上一次的执行的时间,以及需要 sleep 的时间padding 是一个无意义的填充数据,为了提高性能,避免 cpu 缓存的 false sharing64 - 8 = 56Option 中的三个方法意义对应perRequest 就是单位,默认是秒maxSlack 松弛时间,也就是可以允许的突发流量的大小,默认是 Pre / 10 ,这个后面会讲到clock 时钟,这个用于在测试的时候可以 mock 掉不使用真实的时间接下来看看最主要的 Take 方法
1 | |
今天学习了漏桶的实现原理以及使用方式,漏桶和令牌桶的最大的区别就是,令牌桶是支持突发流量的,但是漏桶是不支持的。但是 uber 的这个库通过引入弹性时间的方式也让漏桶算法有了类似令牌桶能够应对部分突发流量的能力,并且实现上还非常的简单,值得学习。
多看看好的轮子的实现总会学到一些新姿势,今天就学到了使用 padding 填充来避免 false sharing 提高性能的操作