Imaginemos un escenario en el que uno tiene una aplicación distribuida que interactúa con una API de terceros. Por lo general, las API de terceros tienen un mecanismo de control de límite de velocidad para evitar que sus clientes aumenten las solicitudes y provoquen tiempos de inactividad en sus servicios. En tal escenario, ¿cómo puede la persona que llama controlar la tasa de solicitudes salientes a la API de terceros en un entorno distribuido? Esta publicación analiza una posible estrategia para este problema.
Existen varios algoritmos para controlar la tasa de solicitudes, pero aquí nos centraremos en el algoritmo del depósito de tokens, porque es relativamente fácil de entender e implementar. Este algoritmo establece que: un depósito puede contener un máximo de T tokens, y cuando una aplicación quiere realizar una solicitud a la API de terceros, debe tomar 1 ficha del cubo. Si el depósito está vacío, debe esperar hasta que haya al menos 1 token en el depósito. Además, el depósito se rellena con 1 token a una tasa fija de R tokens/milisegundos.
El algoritmo del depósito de tokens es muy sencillo de entender, pero ¿cómo puede alguien usarlo en un entorno distribuido para controlar la solicitud saliente a API de terceros?
Si uno quiere controlar el límite de velocidad saliente en un entorno distribuido, es necesaria una fuente centralizada de verdad para el límite de velocidad actual. Hay varias formas de implementar la fuente de la verdad y he idealizado el siguiente diagrama con una posible implementación:
En la figura anterior, tenemos una aplicación distribuida en varios pods y cada pod puede realizar solicitudes a una API de terceros. En la infraestructura de la aplicación, hay un servidor TCP que controla el límite de velocidad mediante el algoritmo del depósito de tokens. Antes de realizar una solicitud a la API de terceros, el pod solicita al servidor TCP un nuevo token y espera una respuesta del servidor TCP hasta que haya al menos un token disponible. Una vez que un token está disponible, el pod realiza la solicitud a la API de terceros.
La implementación del servidor TCP se puede encontrar en este repositorio https://github.com/rafaquelhodev/rlimit/ y en la siguiente sección discutiré brevemente la implementación del token bucket en golang.
A continuación, muestro las ideas principales detrás de la implementación del depósito de tokens. Por favor, eche un vistazo al repositorio https://github.com/rafaquelhodev/rlimit/ para comprender la implementación detallada.
El control del límite de tasa está centralizado en la estructura TokenBucket:
type TokenBucket struct { id string mu sync.Mutex tokens int64 maxTokens int64 refillPeriod int64 cron chan bool subs []chan bool }
Puedes notar que hay una propiedad subs en la estructura TokenBucket. Básicamente, se trata de una matriz de suscriptores para un depósito de tokens específico: cada vez que se solicita un token a un cliente, el cliente se agrega a la matriz de subs y se notifica al cliente cuando se agrega un nuevo token al depósito.
Al iniciar el depósito, debemos proporcionar una cantidad máxima de tokens que el depósito puede admitir (maxTokens) y la cantidad de tiempo que se agrega un token al depósito (refillPeriod):
func newTokenBucket(id string, maxTokens int64, refillPeriod int64) *TokenBucket { bucket := &TokenBucket{ id: id, tokens: 0, maxTokens: maxTokens, refillPeriod: refillPeriod, cron: make(chan bool), subs: make([]chan bool, 0), } fmt.Printf("refill period = %d\n", refillPeriod) bucket.startCron() return bucket }
Ahora, quizás te preguntes: "¿Cómo se agrega un token al depósito?". Para eso, cuando se crea un depósito, se inicia un trabajo cron y, en cada milisegundo de refillPeriod, se agrega un nuevo token al depósito:
func (tb *TokenBucket) startCron() { ticker := time.NewTicker(time.Duration(tb.refillPeriod) * time.Millisecond) go func() { for { select { case 0 { sub := tb.subs[0] tb.subs = tb.subs[1:] subFinalmente, cuando un cliente quiere un token del depósito, se debe llamar a la función waitAvailable:
func (tb *TokenBucket) waitAvailable() bool { tb.mu.Lock() if tb.tokens > 0 { fmt.Printf("[CONSUMING TOKEN] - id = %s\n", tb.id) tb.tokens -= 1 tb.mu.Unlock() return true } fmt.Printf("[WAITING TOKEN] - id %s\n", tb.id) ch := tb.tokenSubscribe() tb.mu.Unlock()Inspirado en https://github.com/Mohamed-khattab/Token-bucket-rate-limiter
Descargo de responsabilidad: Todos los recursos proporcionados provienen en parte de Internet. Si existe alguna infracción de sus derechos de autor u otros derechos e intereses, explique los motivos detallados y proporcione pruebas de los derechos de autor o derechos e intereses y luego envíelos al correo electrónico: [email protected]. Lo manejaremos por usted lo antes posible.
Copyright© 2022 湘ICP备2022001581号-3