Golang官方限流器的用法详解

什么是限流器

在高并发的网络服务中,限流是保证系统稳定的一种重要手段。限流器是一种控制流量的机制,其主要功能是限制并发访问的数量,避免系统过载、崩溃等问题。限流器可以根据业务需求,自定义流量控制策略,保证服务的可用性。

什么是Golang官方限流器

Golang官方提供了一套限流器库,在开发中可以直接使用,避免重复造轮子。该限流器库提供了三种限流器的实现:基于计数器的流量控制器、基于漏桶算法的限流器和基于令牌桶算法的限流器。这些限流器提供了流量控制方案,可以节约资源并且提高服务的健壮性。

基于计数器的流量控制器

基本用法

计数器是一种最基本的限流实现方式,Golang中提供的流量控制器基于计数器实现。这种方式可以简单地控制请求次数。下面是示例代码:

import (

"fmt"

"time"

"golang.org/x/time/rate"

)

func main() {

limiter := rate.NewLimiter(5, 1)

for i := 0; i < 10; i++ {

if err := limiter.Wait(context.Background()); err != nil {

fmt.Println("Wait error:", err)

}

fmt.Println("Request Number: ", i+1)

}

}

在上面的代码中,我们创建了一个速率为5的限流器,每秒中产生5个Token。限流器会记录生成Token的时间,并将其添加到令牌桶中。每当请求到达时,限流器会先检查令牌桶中是否还有可用的令牌,如果没有,则等待,直到桶中有可用的令牌。

注意:

如果我们将速率设置为0,该限制器将会是一个毫秒级的睡眠器。

在高并发的情况下,当请求到达速度超出限制速率时,系统可能会因无可用的令牌的出现而发送并发异常。我们需要保护发行方不会因请求速度过快而丢失响应。

令牌桶算法

令牌桶算法是最常用的限流算法之一。通俗地说,这个算法是将若干个令牌放入桶中,其中每个令牌代表可用流量,这个桶会以一定的速率不断注入令牌。每当一个请求过来时,桶中是否有可用令牌,如果有,则返回响应,派发一个令牌;如果没有,则返回错误。

基于令牌桶的限流器

基本用法

基于令牌桶算法实现的限流器,允许在短时间内突发大量请求。它与计数器限流器略有不同,它可以提供缓解短期流量激增的能力。下面是一个基于令牌桶的限流器示例代码:

import (

"context"

"fmt"

"time"

"golang.org/x/time/rate"

)

func main() {

limiter := rate.NewLimiter(5, 10)

for i := 0; i < 20; i++ {

if err := limiter.Wait(context.Background()); err != nil {

fmt.Println("Wait error:", err)

}

fmt.Println("Request Number: ", i+1)

}

}

这个示例代码中,我们设置了一秒钟产生10个令牌,初始时桶内有5个令牌,这样限流器就能够在短时间内快速的处理突发大量请求,并且还能保证业务的流畅性。此外,如果突发流量持续时间超过限流器允许的时间范围,那么将会予以拒绝。

使用预防突发流量的并发安全策略

在使用限流器的并发能力时,我们需要使用并发安全策略来防止并发问题。否则,当多个请求发送到同一个限流器实例时,就有可能出现不一致的情况,我们可以通过加锁的方式解决。

使用并发安全的限流器:

import (

"context"

"fmt"

"sync"

"golang.org/x/time/rate"

)

var locker sync.Mutex

var limiter *rate.Limiter

func GetLimiter() *rate.Limiter {

locker.Lock()

defer locker.Unlock()

if limiter != nil {

return limiter

}

limiter = rate.NewLimiter(5, 50)

return limiter

}

func main() {

for i := 0; i < 20; i++ {

if err := GetLimiter().Wait(context.Background()); err != nil {

fmt.Println("Wait error:", err)

}

fmt.Println("Request Number: ", i+1)

}

}

在上面的代码中,我们使用了sync.Mutex来保护limiter实例。GetLimiter函数用于获取limiter的实例,如果已经存在,则返回之前的实例,否则创建一个新的实例并返回。

基于漏桶算法的限流器

基本用法

漏桶算法是另一种流量控制算法,在漏桶算法中,令牌不是一次性添加进桶中的,而是以恒定速率流出(或者说漏出)至业务系统中。下面是一个使用漏桶算法实现的限流器示例代码:

import (

"context"

"fmt"

"time"

"golang.org/x/time/rate"

)

func main() {

limiter := rate.NewLimiter(2, 20)

now := time.Now()

for i := 0; i < 10; i++ {

if err := limiter.Wait(context.Background()); err != nil {

fmt.Println("wait error:", err)

}

fmt.Println("Request Number: ", i+1, ", elapse:", time.Since(now))

}

}

在上面的代码中,我们使用了漏桶算法实现一个速率为2,容量为20的限流器。在下面的循环中,我们向限流器发起请求并等待响应,当桶中的令牌不足时,请求将会被拒绝。这里我们使用了time.Since()方法来计算一次请求的响应时间。

Golang官方限流器的应用场景

如何选择限流器?

基于计数器的限流器和令牌桶容易理解,也比较好用。如果在业务场景具有一定的规律性和周期性,可以考虑选择基于计数器的限流器。而如果不具有太大的规律性,建议选择基于令牌桶的限流器,因为这样可以最大限度地适应各种压力。

Golang标准库提供了丰富的限流器库,包括基于计数器的流量控制器,基于漏桶算法的限流器和基于令牌桶算法的限流器。使用这些限流器实现的限制器可以根据业务需求定制流量控制策略,帮助我们增强系统的健壮性和稳定性。

适用场景:

接口访问频率限制

爬虫框架对某些网页进行限速

限制数据库或缓存的连接数

限制文件上传速度

并发的连接数限制

Golang官方限流器是一个非常实用的工具,它可以帮助开发者在高并发环境中优化系统性能,避免系统过载问题。我们只需要根据业务需求选择合适的限流器,并设置正确的参数,就可以将性能提升到一个全新的水平。

后端开发标签