Uma estratégia de balde de token golang (juju/ratelimit)

Um guia para usar o token token golang (juju/ratelimit)

cenas a serem usadas

Um dos principais cenários de uso de token buckets é a limitação.
O programa produz tokens em uma determinada taxa e os adiciona ao token bucket.
Quando cada solicitação chegar, ela tentará obter um token do token bucket.Se o token não for obtido (o token bucket está vazio), a solicitação não será processada, de modo a atingir o objetivo de limitação atual.

balde de fichas

juju/ratelimit usa

juju/ratelimit é um token bucket de código aberto e de alta eficiência implementado em linguagem golang, com código conciso. No momento da redação deste artigo, o projeto tinha um início de 2,4k.
O endereço do projeto é github.com/juju/ratelimit

introdução api

Criar bloqueio de token

Primeiro, veja os três métodos de criação de bloqueios de token e suas diferenças:

  1. NewBucket
    cria um token bucket, define a frequência de preenchimento (fillInterval) e a capacidade inicial (capacity) e adiciona 1 bloco de tokens ao token bucket toda vez que a frequência de preenchimento é preenchida.
    func NewBucket(fillInterval time.Duration, capacity int64) *Bucket
//令牌桶容量为1,每10ms填充一块令牌
bucket := ratelimit.NewBucket(10 * time.Millisecond, 1)
  1. NewBucketWithQuantum
    cria um token bucket, define a frequência de preenchimento (fillInterval), a capacidade inicial (capacity) e o número de tokens preenchidos por segundo (quantum) e adiciona tokens de bloco quântico ao token bucket a cada frequência de preenchimento.
    func NewBucketWithQuantum(fillInterval time.Duration, capacity, quantum int64) *Bucket
//令牌桶容量为3, 每10ms填充3块令牌
bucket := ratelimit.NewBucketWithQuantum(10 * time.Millisecond, 3, 3)
  1. NewBucketWithRate
    cria um token bucket e define a taxa por segundo (taxa) e a capacidade inicial (capacidade).
    func NewBucketWithRate(rate float64, capacity int64) *Bucket
//令牌桶容量为1, 每秒限速100次
bucket := ratelimit.NewBucketWithRate(100, 1)

obter token

obter token

  1. Toma sem bloqueio, retorna o tempo de espera
    func (tb *Bucket) Take(count int64) time.Duration

  2. TakeAvailable é non-blocking, quando o número de tokens não atende a demanda, retorna o número de tokens disponíveis
    func (tb *Bucket) TakeAvailable(count int64) int64

  3. TakeMaxDuration não bloqueia, retorna o tempo de espera e retorna 0 se o tempo máximo for excedido, falso
    func (tb *Bucket) TakeMaxDuration(count int64, maxWait time.Duration) (time.Duration, bool)

  4. Aguarde os blocos até que um token seja obtido
    func (tb *Bucket) Wait(count int64)

  5. Bloqueio WaitMaxDuration Se o token puder ser obtido dentro do tempo máximo de espera, ele será bloqueado, caso contrário, retornará falso imediatamente
    func (tb *Bucket) WaitMaxDuration(count int64, maxWait time.Duration) bool

Outros métodos

  1. Disponível retorna o número de tokens atualmente disponíveis
    func (tb *Bucket) Available() int64

  2. Rate retorna a taxa de limite atual por segundo
    func (tb *Bucket) Rate() float64

Análise de código-fonte chave

Como é a estrutura do token bucket?

type Bucket struct {
    //Clock提供了获取当前时间、sleep指定时间的方法
	clock Clock

	//桶被创建的时间, 当前属于第几个tick也是基于这个起始时间来计算
	startTime time.Time

	// 桶容量
	capacity int64

	// 每个tick向桶填充的令牌数
	quantum int64

	// 填充间隔
	fillInterval time.Duration

	// 桶方法使用的互斥锁, 保障线程安全 
	mu sync.Mutex

	// 桶内可用的令牌数
	// 当有操作等待令牌的时候, 这个数值会变成负数
	availableTokens int64

	// 上一个填充tick
	// 计算当前tick与上一个tick的差值 得出需要填充的令牌数
	latestTick int64
}

Como é possível encher o balde com fichas a uma certa taxa?

Juju/ratelimit não permite que threads adicionais preencham tokens regularmente no balde, mas aciona um método de preenchimento quando o método de bloqueio de token é chamado externamente e calcula se ele precisa ser preenchido de acordo com a diferença entre o horário atual e o horário de criação do bloqueio de token , a quantidade a ser preenchida.

O método de preenchimento do token, que será acionado quando o método de bloqueio do token for chamado externamente

func (tb *Bucket) adjustavailableTokens(tick int64) {
    //令牌桶结构中记录了上一个填充周期的值
    lastTick := tb.latestTick
    tb.latestTick = tick
    //如果桶是满的直接返回
    if tb.availableTokens >= tb.capacity {
        return
    }
    //需要填充的数量是 (本周期数 - 上次填充周期数) * 单周期填充数
    tb.availableTokens += (tick - lastTick) * tb.quantum
    //填充数量不得超过桶的容量
    if tb.availableTokens > tb.capacity {
        tb.availableTokens = tb.capacity
    }
    return
}

Obtenha o código-chave do token

Take(), TakeMaxDuration(), Wait(), WaitMaxDuration() são todos implementados chamando o método interno de take()

func (tb *Bucket) take(now time.Time, count int64, maxWait time.Duration) (time.Duration, bool) {
    if count <= 0 {
        return 0, true
    }

    //计算当前tick, 调用adjustavailableTokens填充令牌
    tick := tb.currentTick(now)
    tb.adjustavailableTokens(tick)
    
    //用可用令牌数(availableTokens)减去需要获取的令牌数(count),这里计算出的avail可能为负值
    avail := tb.availableTokens - count
    //令牌充足, 返回0(不需要等待), true(获取令牌成功)
    if avail >= 0 {
        tb.availableTokens = avail
        return 0, true
    }
    //计算出一个endTick, 在未来的endTick到达时,令牌数将不再是负的
    endTick := tick + (-avail+tb.quantum-1)/tb.quantum
    //计算endTick的时间点
    endTime := tb.startTime.Add(time.Duration(endTick) * tb.fillInterval)
    //需要等待的时间时endTime - now
    waitTime := endTime.Sub(now)
    if waitTime > maxWait {
        return 0, false
    }
    //更新availableTokens, 可能为负值
    tb.availableTokens = avail
    //返回等待时间, 获取成功
    return waitTime, true
}

Exemplo simples usando bloqueio de token

código:

import (
	"github.com/juju/ratelimit"
	"time"
)

func main() {
    //创建一个令牌桶初始容量为1, 每10ms填充3个令牌
    bucket := ratelimit.NewBucketWithQuantum(10 * time.Millisecond, 1, 3)
    //程序运行长3秒
    endTime := time.Now().Add(3 * time.Second)
    //打印桶的每秒限速频率(预期300/s)
    println("bucket rate:" , bucket.Rate(), "/s")
    //使用一个变量记录获取令牌的总数
    var tockensCount int64 = 0
    for {
        //每次拿1块令牌, 成功返回1, 失败返回0
        tocken := bucket.TakeAvailable(1)
        tockensCount += tocken
        if(time.Now().After(endTime)) {
            println("tockensCount: ", tockensCount)
            return;
        }
        time.Sleep(5 * time.Millisecond)
    }
	
}

Resultado da execução do programa:

bucket rate: +3.000000e+002 /s
tockensCount:  301

Por que tokenensCount é 301 em vez de 300:
porque a capacidade inicial do token bucket criado é 1. Depois que o balde é inicializado, já existe um token nele e você pode obter esse token imediatamente sem esperar pelo preenchimento.
Um total de 300 tokens são preenchidos nos próximos 3000ms.

Ao usar o balde de token, preste atenção. Como o balde de token tem uma " capacidade ", uma certa quantidade de tráfego instantâneo é permitida. Quando houver requisitos estritos sobre o limite de taxa, você deve definir cuidadosamente a capacidade e a velocidade de preenchimento e conduzir verificação de teste real.

Acho que você gosta

Origin blog.csdn.net/m0_52528053/article/details/127294249
Recomendado
Clasificación