Una estrategia de cubeta de fichas de golang (juju/ratelimit)

Una guía para usar el depósito de tokens de golang (juju/ratelimit)

Escenas a utilizar

Uno de los principales escenarios de uso de los cubos de fichas es la limitación.
El programa produce tokens a una cierta tasa y los agrega al depósito de tokens.
Cuando llegue cada solicitud, intentará obtener un token del depósito de tokens. Si no se obtiene el token (el depósito de tokens está vacío), la solicitud no se procesará para lograr el propósito de limitar la corriente.

cubo de fichas

usos juju/ratelimit

juju/ratelimit es un token bucket de código abierto y alta eficiencia implementado en lenguaje golang, con código conciso. Al momento de escribir este artículo, el proyecto tiene un inicio de 2.4k.
La dirección del proyecto es github.com/juju/ratelimit

introducción a la API

Crear bloqueo de token

Primero observe los tres métodos para crear bloqueos de fichas y sus diferencias:

  1. NewBucket
    crea un depósito de tokens, establece la frecuencia de llenado (fillInterval) y la capacidad inicial (capacidad) y agrega 1 bloque de tokens al depósito de tokens cada vez que se llena la frecuencia de llenado.
    func NewBucket(fillInterval time.Duration, capacity int64) *Bucket
//令牌桶容量为1,每10ms填充一块令牌
bucket := ratelimit.NewBucket(10 * time.Millisecond, 1)
  1. NewBucketWithQuantum
    crea un depósito de tokens, establece la frecuencia de llenado (fillInterval), la capacidad inicial (capacidad) y la cantidad de tokens llenados por segundo (quantum), y agrega tokens de bloques cuánticos al depósito de tokens cada frecuencia de llenado.
    func NewBucketWithQuantum(fillInterval time.Duration, capacity, quantum int64) *Bucket
//令牌桶容量为3, 每10ms填充3块令牌
bucket := ratelimit.NewBucketWithQuantum(10 * time.Millisecond, 3, 3)
  1. NewBucketWithRate
    crea un cubo de fichas y establece la tasa por segundo (tasa) y la capacidad inicial (capacidad).
    func NewBucketWithRate(rate float64, capacity int64) *Bucket
//令牌桶容量为1, 每秒限速100次
bucket := ratelimit.NewBucketWithRate(100, 1)

obtener token

obtener token

  1. Toma sin bloqueo, devuelve el tiempo de espera
    func (tb *Bucket) Take(count int64) time.Duration

  2. TakeAvailable no bloquea, cuando la cantidad de tokens no satisface la demanda, devuelve la cantidad de tokens disponibles
    func (tb *Bucket) TakeAvailable(count int64) int64

  3. TakeMaxDuration no bloquea, devuelve el tiempo de espera y devuelve 0 si se supera el tiempo máximo, falso
    func (tb *Bucket) TakeMaxDuration(count int64, maxWait time.Duration) (time.Duration, bool)

  4. Bloques de espera hasta que se obtenga un token
    func (tb *Bucket) Wait(count int64)

  5. Bloqueo de WaitMaxDuration Si el token se puede obtener dentro del tiempo máximo de espera, se bloqueará; de lo contrario, devolverá falso inmediatamente
    func (tb *Bucket) WaitMaxDuration(count int64, maxWait time.Duration) bool

otros metodos

  1. Disponible devuelve la cantidad de tokens disponibles actualmente
    func (tb *Bucket) Available() int64

  2. Tasa devuelve la tasa límite actual por segundo
    func (tb *Bucket) Rate() float64

Análisis de código fuente clave

¿Cómo es la estructura del 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
}

¿Cómo es posible llenar el balde con fichas a una determinada tasa?

Juju/ratelimit no habilita subprocesos adicionales para llenar tokens en el cubo con regularidad, pero activa un método de llenado cuando el método de bloqueo de token se llama externamente y calcula si debe llenarse de acuerdo con la diferencia entre la hora actual y la hora de creación . del token lock , la cantidad a llenar.

El método de llenado del token, que se activará cuando se llame externamente al método de bloqueo del token.

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
}

Obtener el código clave del token

Take(), TakeMaxDuration(), Wait(), WaitMaxDuration() se implementan llamando al método interno 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
}

Ejemplo simple usando token lock

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 de la ejecución del programa:

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

Por qué tokenensCount es 301 en lugar de 300:
porque la capacidad inicial del depósito de tokens creado es 1. Después de inicializar el cubo, ya hay un token en él, y puede obtener este token inmediatamente sin esperar a que se llene.
Se llena un total de 300 fichas en los siguientes 3000ms.

Al usar el cubo de fichas, preste atención. Dado que el cubo de fichas tiene una " capacidad ", se permite una cierta cantidad de tráfico instantáneo. Cuando existen requisitos estrictos en el límite de velocidad, debe establecer cuidadosamente la capacidad y la velocidad de llenado , y realizar verificación de prueba real.

Supongo que te gusta

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