>백엔드 개발 >Golang >Golang 공식 Current Limiter 사용법에 대한 자세한 설명

Golang 공식 Current Limiter 사용법에 대한 자세한 설명

Go语言进阶学习
Go语言进阶学习앞으로
2023-07-25 15:08:441782검색

전류 제한기는 서비스 안정성을 향상시키는 데 매우 중요한 구성 요소로, 요청 속도를 제한하고 서비스 과부하를 방지하는 데 사용할 수 있습니다. 전류 제한기를 구현하는 방법에는 여러 가지가 있습니다. 일반적인 전류 제한 알고리즘에는 고정 창, 슬라이딩 창, 누출 버킷 및 토큰 버킷이 포함됩니다. 이전 기사 "일반적으로 사용되는 전류 제한 알고리즘의 응용 시나리오 및 구현 원리" 이 기사에서는 설명합니다. 이러한 전류 제한 방법의 특성 및 응용 시나리오 토큰 버킷은 전류를 제한하면서 특정 트래픽 버스트를 처리할 수도 있습니다. 이는 인터넷 응용 프로그램이 뜨거운 이벤트로 인해 갑자기 트래픽이 급증하는 경향이 있다는 사실과 유사합니다.

간단히 말하면 토큰 버킷은 고정된 크기의 버킷입니다. 시스템은 버킷이 가득 차면 일시적으로 토큰을 버킷에 넣지 않습니다. 상대적으로 요청이 적은 경우 버킷은 갑작스러운 트래픽에 대처하기 위해 일부 토큰을 먼저 "저장"할 수 있습니다. 버킷에 남은 토큰이 있으면 지속적으로 인출할 수 있습니다. 남은 토큰이 없으면 토큰이 버킷에 들어갈 때까지 기다려야 합니다.

토큰 버킷 전류 제한에 대한 자세한 설명은 다음 기사를 참조하세요. 일반적으로 사용되는 전류 제한 알고리즘의 응용 시나리오 및 구현 원리

토큰 버킷의 원리를 이해한 후 실제로 직접 구현하고 싶어하는 학생들도 있습니다. 자신의 프로젝트에 전류 리미터를 적용해 보세요... 어떻게 표현하면 휠을 만드는 것이 자신의 레벨을 향상시키는 데 실제로 도움이 되지만 상용 프로젝트에 적용하면 실제로 휠을 빌드할 필요가 없습니다. 휠을 직접 제작했습니다. Golang 관계자가 이미 우리를 위해 작업을 완료했습니다. 휠이 만들어졌습니다...~!

Golang에서 공식적으로 제공하는 확장 라이브러리는 현재 제한 알고리즘, 즉 golang.org/x/time/rate. 이 전류 제한기는 토큰 버킷을 기반으로 구현됩니다. golang.org/x/time/rate。该限流器也是基于 Token Bucket(令牌桶) 实现的。

限流器的内部结构

time/rate包的Limiter类型对限流器进行了定义,所有限流功能都是通过基于Limiter

전류 제한기의 내부 구조

시간/속도 패키지Limiter 유형은 현재 제한기를 정의합니다. 모든 현재 제한 기능은 Limiter 유형 구현, 내부 구조는 다음과 같습니다: 🎜
type Limiter struct {
 mu     sync.Mutex
 limit  Limit
 burst  int // 令牌桶的大小
 tokens float64
 last time.Time // 上次更新tokens的时间
 lastEvent time.Time // 上次发生限速器事件的时间(通过或者限制都是限速器事件)
}
🎜주요 필드의 기능은 다음과 같습니다. 🎜
  • limit:limit字段表示往桶里放Token的速率,它的类型是Limit,是int64的类型别名。设置limit时既可以用数字指定每秒向桶中放多少个Token,也可以指定向桶中放Token的时间间隔,其实指定了每秒放Token的个数后就能计算出放每个Token的时间间隔了。
  • burst: 令牌桶的大小。
  • tokens: 桶中的令牌。
  • last: 上次往桶中放 Token 的时间。
  • lastEvent:上次发生限速器事件的时间(通过或者限制都是限速器事件)

可以看到在 timer/rate 的限流器实现中,并没有单独维护一个 Timer 和队列去真的每隔一段时间向桶中放令牌,而是仅仅通过计数的方式表示桶中剩余的令牌。每次消费取 Token 之前会先根据上次更新令牌数的时间差更新桶中Token数

大概了解了time/rate限流器的内部实现后,下面的内容我们会集中介绍下该组件的具体使用方法:

构造限流器

我们可以使用以下方法构造一个限流器对象:

limiter := rate.NewLimiter(10, 100);

这里有两个参数:

  1. 第一个参数是 r Limit,设置的是限流器Limiter的limit字段,代表每秒可以向 Token 桶中产生多少 token。Limit 实际上是 float64 的别名。
  2. 第二个参数是 b int,b 代表 Token 桶的容量大小,也就是设置的限流器 Limiter 的burst字段。

那么,对于以上例子来说,其构造出的限流器的令牌桶大小为 100, 以每秒 10 个 Token 的速率向桶中放置 Token。

除了给r Limit参数直接指定每秒产生的 Token 个数外,还可以用 Every 方法来指定向桶中放置 Token 的间隔,例如:

limit := rate.Every(100 * time.Millisecond);
limiter := rate.NewLimiter(limit, 100);

以上就表示每 100ms 往桶中放一个 Token。本质上也是一秒钟往桶里放 10 个。

使用限流器

Limiter 提供了三类方法供程序消费 Token,可以每次消费一个 Token,也可以一次性消费多个 Token。每种方法代表了当 Token 不足时,各自不同的对应手段,可以阻塞等待桶中Token补充,也可以直接返回取Token失败。

Wait/WaitN

func (lim *Limiter) Wait(ctx context.Context) (err error)
func (lim *Limiter) WaitN(ctx context.Context, n int) (err error)

Wait 实际上就是 WaitN(ctx,1)

当使用 Wait 方法消费 Token 时,如果此时桶内 Token 数组不足 (小于 N),那么 Wait 方法将会阻塞一段时间,直至 Token 满足条件。如果充足则直接返回。

这里可以看到,Wait 方法有一个 context 参数。我们可以设置 context 的 Deadline 或者 Timeout,来决定此次 Wait 的最长时间。

// 一直等到获取到桶中的令牌
err := limiter.Wait(context.Background())
if err != nil {
 fmt.Println("Error: ", err)
}

// 设置一秒的等待超时时间
ctx, _ := context.WithTimeout(context.Background(), time.Second * 1)
err := limiter.Wait(ctx)
if err != nil {
 fmt.Println("Error: ", err)
}

Allow/AllowN

func (lim *Limiter) Allow() bool
func (lim *Limiter) AllowN(now time.Time, n int) bool

Allow 实际上就是对 AllowN(time.Now(),1) 进行简化的函数。

AllowN 方法表示,截止到某一时刻,目前桶中数目是否至少为 n 个,满足则返回 true,同时从桶中消费 n 个 token。反之不消费桶中的Token,返回false。

对应线上的使用场景是,如果请求速率超过限制,就直接丢弃超频后的请求。

if limiter.AllowN(time.Now(), 2) {
    fmt.Println("event allowed")
} else {
    fmt.Println("event not allowed")
}

Reserve/ReserveN

func (lim *Limiter) Reserve() *Reservation
func (lim *Limiter) ReserveN(now time.Time, n int) *Reservation

Reserve 相当于 ReserveN(time.Now(), 1)

ReserveN 的用法就相对来说复杂一些,当调用完成后,无论 Token 是否充足,都会返回一个 *Reservation 对象。你可以调用该对象的Delay()方法,该方法返回的参数类型为time.Duration,反映了需要等待的时间,必须等到等待时间之后,才能进行接下来的工作。如果不想等待,可以调用Cancel()方法,该方法会将 Token 归还。

举一个简单的例子,我们可以这么使用 Reserve 方法。

r := limiter.Reserve()
f !r.OK() {
    // Not allowed to act! Did you remember to set lim.burst to be > 0 ?
    return
}
time.Sleep(r.Delay())
Act() // 执行相关逻辑

动态调整速率和桶大小

Limiter 支持创建后动态调整速率和桶大小:

  1. SetLimit(Limit) 改变放入 Token 的速率
  2. SetBurst(int) 改变 Token 桶大小

有了这两个方法,可以根据现有环境和条件以及我们的需求,动态地改变 Token 桶大小和速率。

总结

今天我们总结了 Golang 官方限流器的使用方法,它是一种令牌桶算实现的限流器。其中 Wait/WaitNAllow/AllowN 这两组方法在平时用的比较多,前者是消费Token时如果桶中Token不足可以让程序等待桶中新Token的放入(最好设置上等待时长)后者则是在桶中的Token不足时选择直接丢弃请求。

Golang에서 공식적으로 제공하는 Current Limiter 구현 외에도 Uber의 오픈 소스 Current Limiteruber-go/ratelimit도 좋은 선택입니다. Golang의 공식 Current Limiter와 달리 Uber의 Current Limiter는 Leaky Bucket 알고리즘을 통해 구현되지만 기존의 Leaky Bucket 알고리즘에는 적합합니다. 개선되었으며, 관심 있는 학생들이 직접 체험해 볼 수 있습니다.

위 내용은 Golang 공식 Current Limiter 사용법에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 Go语言进阶学习에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제