El patrón Throttling permite controlar la cantidad de recursos que una aplicación puede utilizar antes de estrangular los procesos no esenciales a favor de mantener a flote el barco, evitando una interrupción del servicio o el incumplimiento de los SLA.
Problemática
Hoy en día es bastante común encontrarnos en situaciones donde nuestra aplicación recibe una carga inusual de trabajo, que pone en peligro el funcionamiento de la aplicación, a tal punto que puede ralentizar los tiempos de respuesta, abortar operaciones o finalmente, terminar tumbando la aplicación por completo.
Si bien, este tipo de problemas son fácilmente manejables con estrategias de autoescalado, tiene la limitante de que esto no es inmediato, si no que requiere breve periodo de tiempo para aprovisionar más instancias de un servicio determinado, lo que podrías dar lugar a una breve interrupción del servicio.
Puedes aprender más de escalamiento en mi artículo donde explico que es el escalamiento horizontal y vertical.
Solución
La solución que propone el patrón Throttling es permitir que la aplicación utilice recursos hasta cierto límite, y luego limitar (en realidad es estrangular, pero se escucha muy extraño) el funcionamiento de la aplicación para evitar comprometer los tiempos de respuesta o el funcionamiento total de la aplicación.
El termino estrangular (Throttling) puede resultar un poco confuso, pero se refiere a la capacidad de una aplicación o servicio priorizar el funcionamiento vital de la aplicación, y limitando o denegando el servicio a todos aquellos menos importantes.
Dicho lo anterior, existen varias formas en las que podemos limitar el uso de recursos de una aplicación, entre las que destacan:
1 – Limitar el número de peticiones
Una de las estrategias más ampliamente utilizada es el estrangulamiento por número de peticiones, que consiste en limitar el número de peticiones que un cliente puede realizar al servicio en un tiempo determinado, lo que obliga a la aplicación a medir el numero de solicitudes por cliente, y finalmente denegar el servicio cuando el umbral ha sido alcanzado.
Este enfoque es especialmente útil cuando ofrecemos un API a nuestros clientes, los cuales pueden utilizar a demanda cualquier servicio que proporcionemos, sin embargo, como no tenemos el control de cuantas llamadas realicen, es importante blindas nuestra API para que no hagan un uso irresponsable de los servicios que les proporcionamos y terminen degradando o tumbando el API.
Por ejemplo, hay casos donde un cliente utiliza técnicas irresponsables como el Pooling para consultar con demasiada frecuencia cierta información, lo que puede implicar el uso de una gran cantidad de recursos del servidor para atender cada petición.
Puedes leer más del patrón Pooling en el siguiente artículo de mi libro de arquitectura de software.
Pero el uso excesivo de los recursos del servidor no solo se puede dar por un uso irresponsable, si no que se puede dar el caso de que nuestro cliente presente un crecimiento natural en su operación, lo que implica un mayor número de peticiones al API, lo que implicaría un uso mayor al negociado al comienzo.
Throttling con Nginx
Una de las formas más simples de implementar el patrón Throttling por número de peticiones es utilizar Nginx, un proxy web que permite limitar el número de peticiones por medio de configuración:
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;
server {
location /login/ {
limit_req zone=mylimit;
proxy_pass http://my_upstream;
}
}
El ejemplo muestra como limitar las llamadas al servicio de login (/login/) con una taza de 10 request por segundo (rate=10r/s).
Puedes ver el ejemplo completo en la documentación de Nginx (https://www.nginx.com/blog/rate-limiting-nginx/)
2 – Priorizar servicios esenciales
Otra de las estrategias más utilizadas para implementar el Throttling es limitar o denegar los servicios no esenciales, de esta forma, podemos simplemente denegar el servicio a ciertas operaciones hasta que el sistema se ve restablecido o utilizar una estrategia menos agresiva como degradar la calidad del servicio para permitir liberar recursos para que los procesos esenciales puedan seguir funcionando.
Para implementar esta estrategia es importante poder identificar cuales son los procesos vitales y cuales son prescindibles, de tal forma que podamos apagarlos o degradarlos de forma rápida, evitando que el sistema llegue a un estado de saturación.
Un ejemplo bastante contundente de esta estrategia es la que utilizo Netflix durante la pandemia del COVID-19, en el ancho de banda de Europa se saturó por la masiva cantidad de gente que se quedaba en casa y solo podría ver TV, lo que obligó a Netflix a degradar la calidad del servicio a HD en lugar de ofrecer 4K o Full HD.
3 – Distribución prioritarias de solicitudes
Esta estrategia se utiliza mucho en sistema multitenant, donde varios inquilinos utilizan la misma aplicación, sin embargo, se les ofrece un SLA diferente según el nivel de cada cliente. Mediante esta estrategia es posible priorizar los mensajes mediante de colas de mensaje de prioridad y atender estos mensajes según su prioridad, de esta forma damos prioridad a nuestros clientes premium y dejamos en espera o denegamos el servicio al resto de clientes.
Esta estrategia es muy utiliza por ejemplo en aplicaciones en la nube donde tenemos clientes gratuitos y clientes premium, lo que nos permite garantizar una experiencia constante a los clientes premium y a los clientes gratuitos los dejamos en espera hasta que el sistema se regulariza. Ya que al final, lo más importante es mantener contentos a los clientes que si esta pagando por el servicio.
Consideraciones
A pesar de que Throttling es un excelente patrón, no todo es felicidad, pues hay ciertas cosas que debemos de considerar antes de implementarlo, y que son clave para su éxito.
- Lo primero es más importante, la aplicación debe de a ver sido diseñada desde el comienzo para soportar esta capacidad, pues es necesarios desde el inicio, determinar los servicios vitales y de los que podemos preceder por un breve periodo de tiempo.
- Este patrón debe de actuar rápido, ya que ante un pico de demanda, si el patrón no estrangula rápidamente el servicio, puede que el servicio se degrade por un breve periodo de tiempo o incluso, se caiga antes de que el estrangulamiento entre.
- Es importante distinguir entre un error de la aplicación y uno de estrangulamiento, ya que de esta forma el cliente podrá saber el motivo por el cual su invocación está siendo rechazada.
- El estrangulamiento de puede utilizar como una estrategia temporal en lo que el autoescalado se lleva a cabo.
- Finalmente y no menos importante, el sistema deberá de tener la capacidad de regresar a su estado una vez que la carga de trabajo se ha normalizado, de lo contrario quedarán los servicios estrangulados por tiempo indeterminado.
- El estrangulamiento debe de ser un proceso automático y no manual, lo que quiere decir que este debe de entrar de forma automática por ciertas reglas determinadas y no por un operador que active y desactive componentes.
Cuando deberíamos de usar Throttling
- Cuando necesitamos garantizar el cumplimiento de los SLA a pesar de los picos altos de demanda.
- Evitar que un solo cliente monopolice los recursos del servidor.
- Para permitir la operatividad del servicio a pesar de un pico de demanda no esperado.
- En ciertas ocasiones nos pueden servir como una válvula para impedir que ciertas aplicaciones consuman más recursos de los esperados, sobre pasando un límite de facturación, muy parecido a lo que hacen lo que hace AWS o Azure en sus servicios en la nube.
Conclusiones
Throlling es uno de los patrones más utilizados para servicios que son habilitados para ser utilizados por terceros, donde no tenemos el control sobre la cantidad o demanda que puedan necesitar, logrando protegernos de picos de demanda no esperados o que sobre pasan un umbral esperado.