El año pasado, el equipo de ingeniería de Uber publicó un artículo sobre su nuevo mecanismo de deslastre de carga diseñado para su arquitectura de microservicios.
Este artículo es muy interesante desde varias perspectivas. Entonces, tomé algunas notas mientras lo leía para captar mi comprensión y escribir cosas en las que me gustaría profundizar más adelante si no obtengo las respuestas al final. He descubierto varias veces que esta es la mejor manera de aprender cosas nuevas.
Lo que me atrapó desde el principio fue la referencia a principios centenarios utilizados para construir esta solución. Eso es algo que me encanta: tomar prestados conceptos/ideas de diferentes campos y adaptarlos para resolver un problema en un dominio diferente.
Si le interesan la resiliencia y la estabilidad del sistema, le recomiendo leer también el excelente libro 'Release It!' por Michael T. Nygard.
Es un libro antiguo pero bueno: un libro que profundiza en estrategias, patrones y orientación práctica para construir sistemas de software resistentes y estables, enfatizando cómo manejar las fallas de manera efectiva.
Uber ha implementado una nueva solución de deslastre de carga llamada Cinnamon que aprovecha un controlador PID (el mecanismo centenario) para decidir qué solicitudes deben ser procesadas o descartadas por un servicio en función de la carga actual del servicio y la prioridad de la solicitud.
No implica ningún ajuste a nivel de servicio (aunque tenía una pregunta al respecto), se adapta automáticamente y es mucho más eficiente que su solución anterior QALM. Recuerde también que la arquitectura de microservicios de Uber no es para los débiles de corazón…
Un controlador PID es un instrumento utilizado en aplicaciones de control industrial para regular la temperatura, el flujo, la presión, la velocidad y otras variables del proceso. Los controladores PID (derivado integral proporcional) utilizan un mecanismo de retroalimentación de bucle de control para controlar las variables del proceso y son los controladores más precisos y estables.
Si desea obtener más información sobre este concepto centenario, diríjase a Wikipedia.
Ahora, volvamos al artículo. PID significa Proporcional, Integral y Derivada. En su caso, utilizan un componente conocido como controlador PID para monitorear el estado de un servicio (solicitudes de entrada) en función de tres componentes (o medidas).
El término "proporcional" indica que la acción tomada es proporcional al error actual. En términos simples, esto significa que la corrección aplicada es directamente proporcional a la diferencia entre el estado deseado y el estado real. Si el error es grande, la acción correctiva es proporcionalmente grande.
Cuando un punto final está sobrecargado, la rutina en segundo plano comienza a monitorear la entrada y salida de solicitudes en la cola de prioridad.
Por lo tanto, el componente Proporcional (P) en el deslastre de carga ajusta la tasa de deslastre en función de qué tan lejos está el tamaño de la cola actual del tamaño de cola objetivo o deseado. Si la cola es mayor de lo deseado, se produce más desprendimiento; si es más pequeño, se reduce la muda.
Esa es mi comprensión.
El trabajo del controlador PID es minimizar la cantidad de solicitudes en cola, mientras que el trabajo del sintonizador automático es maximizar el rendimiento del servicio, sin sacrificar (demasiado) las latencias de respuesta.
Si bien el texto no menciona explícitamente "Integral (I)" en el contexto del tamaño de la cola, indica que la función del controlador PID es minimizar la cantidad de solicitudes en cola. La minimización de solicitudes en cola se alinea con el objetivo del componente Integral de abordar los errores acumulados a lo largo del tiempo.
Para determinar si un punto final está sobrecargado, realizamos un seguimiento de la última vez que la cola de solicitudes estuvo vacía y, si no se ha vaciado en los últimos 10 segundos, consideramos que el punto final está sobrecargado (inspirado en Facebook).
En el deslastre de carga, puede estar asociado con decisiones relacionadas con el comportamiento histórico de la cola de solicitudes, como el tiempo desde la última vez que estuvo vacía.
Sinceramente, eso no lo tengo del todo claro. Es un poco frustrante, debo decir. Si bien mencionan aprovechar un mecanismo centenario, hubiera sido útil si indicaran explícitamente qué parte corresponde a qué o cómo funciona. No quiero disminuir el valor de su increíble artículo. Esa es solo mi queja aquí... Después de todo, soy francés...;)
Creo que éste es más fácil de identificar.
En un controlador PID (Proporcional-Integral-Derivativo) clásico, la acción "Derivada (D)" es particularmente útil cuando se desea que el controlador anticipe el comportamiento futuro del sistema en función de la tasa actual de cambio del error. Ayuda a amortiguar las oscilaciones y mejorar la estabilidad del sistema.
En el contexto del deslastre de carga y el controlador PID mencionados en el artículo, es probable que se emplee el componente derivado para evaluar qué tan rápido se está llenando la cola de solicitudes. Al hacerlo, ayuda a tomar decisiones destinadas a mantener un sistema estable y evitar cambios repentinos o impredecibles.
El componente rechazador tiene dos responsabilidades: a) determinar si un punto final está sobrecargado y b), si un punto final está sobrecargado, descartar un porcentaje de las solicitudes para asegurarse de que la cola de solicitudes sea lo más pequeña posible. Cuando un punto final está sobrecargado, la rutina en segundo plano comienza a monitorear la entrada y salida de solicitudes en la cola de prioridad. Con base en estos números, utiliza un controlador PID para determinar una proporción de solicitudes a descartar. El controlador PID es muy rápido (ya que se necesitan muy pocas iteraciones) para encontrar el nivel correcto y una vez que se ha vaciado la cola de solicitudes, el PID garantiza que solo reduzcamos lentamente la relación.
En el contexto mencionado, el controlador PID se utiliza para determinar la proporción de solicitudes que se deben descartar cuando un punto final está sobrecargado y monitorea la entrada y salida de solicitudes. El componente derivado del controlador PID, que responde a la tasa de cambio, está implícitamente involucrado en la evaluación de qué tan rápido se llena o vacía la cola de solicitudes. Esto ayuda a tomar decisiones dinámicas para mantener la estabilidad del sistema.
En el contexto de determinar la sobrecarga, el componente integral podría estar asociado con el seguimiento de cuánto tiempo ha estado la cola de solicitudes en un estado no vacío. Esto se alinea con la idea de acumular la integral de la señal de error a lo largo del tiempo.
"Integral: según el tiempo que la solicitud ha estado en la cola..."
El componente derivado, por otro lado, está relacionado con la tasa de cambio. Responde a la rapidez con la que cambia el estado de la cola de solicitudes.
“Derivada: rechazo basado en qué tan rápido se llena la cola…”
El componente Integral enfatiza la duración del estado no vacío, mientras que el componente Derivado considera la velocidad a la que cambia la cola.
Al final del juego, utilizan estas tres medidas para determinar el curso de acción para una solicitud.
La pregunta que tengo es cómo combinan estos tres componentes, en todo caso. También tengo curiosidad por saber cómo los monitorean.
Sin embargo, creo que tengo la idea...
El punto final en el borde está anotado con la prioridad de la solicitud y esto se propaga desde el borde a todas las dependencias posteriores a través de Jaeger . Al propagar esta información, todos los servicios en la cadena de solicitud conocerán la importancia de la solicitud y cuán crítica es para nuestros usuarios.
El primer pensamiento que me viene a la mente es que se integraría perfectamente en una arquitectura de malla de servicios.
Aprecio el concepto de emplear encabezados y seguimiento de servicios distribuidos para propagar la prioridad de las solicitudes. En este sentido, ¿por qué optar por una biblioteca compartida con esta dependencia agregada a cada microservicio, en lugar de colocarla fuera del servicio, tal vez como un complemento de Istio? Teniendo en cuenta los beneficios que ofrece: ciclos de lanzamiento/implementación independientes, soporte políglota, etc.
Aquí hay algunas ideas adicionales:
Bueno, soy parcial, ya que no soy un gran admirador de las bibliotecas compartidas, aunque sólo sea porque creo que complican el proceso de lanzamiento/implementación. Sin embargo, no estoy seguro de si hay que considerar un aspecto de configuración específico del servicio. ¿Quizás configuran cuánto tiempo debe esperar el servicio para comenzar a procesar una consulta y completarla?
Quizás un aspecto que valga la pena probar es el proceso de toma de decisiones del eyector.
Por lo que tengo entendido, determina si se rechaza una solicitud según el controlador PID, que está localizado en el servicio. ¿Existe una opción para un enfoque más global? Por ejemplo, si se sabe que uno de los servicios descendentes en la tubería está sobrecargado (debido a su propio controlador PID), ¿podría cualquier servicio ascendente decidir rechazar la solicitud antes de que llegue a este servicio sobrecargado (que podría estar n pasos más abajo en el proceso)? camino)?
Esta decisión podría basarse en el valor devuelto por el controlador PID o el sintonizador automático del servicio descendente.
Ahora, estoy reflexionando sobre varios aspectos mencionados mientras concluyen el artículo y brindan algunos números para mostrar la eficiencia de su sistema, lo cual es bastante impresionante.
Mencionan en algún momento que "Cada solicitud tiene un tiempo de espera de 1 segundo".
Realizamos pruebas de 5 minutos, donde enviamos una cantidad fija de RPS (por ejemplo, 1000), donde el 50% del tráfico es de nivel 1 y el 50% es de nivel 5. Cada solicitud tiene un tiempo de espera de 1 segundo.
Es común en los sistemas distribuidos asociar una solicitud con un tiempo de vencimiento o fecha límite específica, siendo cada servicio a lo largo de la ruta de procesamiento responsable de hacer cumplir este límite de tiempo. Si se alcanza el tiempo de vencimiento antes de que se complete la solicitud, cualquier servicio de la cadena tiene la opción de cancelar o rechazar la solicitud.
Supongo que este tiempo de espera de 1 segundo está adjunto a la solicitud y que cada servicio, dependiendo de dónde nos encontremos en este plazo, puede decidir cancelar la solicitud. Esta es una medida que es global porque se agrega a través de los servicios. Creo que resuena con el punto que estaba planteando antes acerca de tener una visión global del estado completo del sistema o de las dependencias para decidir cancelar la solicitud lo antes posible si no tiene la posibilidad de completarse debido a uno de los servicios de la lista. camino.
¿Podría devolverse el "estado" de los servicios posteriores (que comprenden datos de sus controladores PID locales) como encabezados adjuntos a las respuestas y usarse para construir un disyuntor/mecanismo de desconexión preventiva temprana más evolucionado?
Finalmente, tengo curiosidad por saber más sobre el enfoque anterior porque, según la descripción dada en este artículo, parece sólido.
Cuando se examinan las medidas de goodput y latencias, no hay duda de cuál, QALM o Cinnamon, funciona mejor. Tenga en cuenta que mencionan un enlace al enfoque QALM en el artículo. Probablemente debería empezar desde allí ;)
Como siempre, estos enfoques no son para todos. La arquitectura y la carga de Uber son propias. De hecho, estoy impaciente por leer los próximos artículos de esta serie, específicamente para aprender más sobre el controlador PID y el sintonizador automático.
Con Cinnamon hemos creado un deslastre de carga eficiente que utiliza técnicas centenarias para establecer dinámicamente umbrales para rechazar y estimar la capacidad de los servicios. Resuelve los problemas que notamos con QALM (y, por lo tanto, con cualquier deslastre de carga basado en CoDel), es decir, que Cinnamon es capaz de:
- Encuentre rápidamente una tasa de rechazo estable
- Ajustar automáticamente la capacidad del servicio.
- Ser utilizado sin establecer ningún parámetro de configuración.
- Incurrir en gastos generales muy bajos
Lo interesante de este enfoque es que consideran todas las solicitudes a procesar para decidir qué hacer con cada nueva solicitud de entrada, ya que utilizan una cola (prioritaria). Como se mencionó, tengo curiosidad por saber si el mecanismo también podría tener en cuenta la salud de todos los servicios dependientes en función de las mismas medidas PID...
Hay otros aspectos interesantes en este artículo, como cómo miden el efecto de sus estrategias y la comparación con el enfoque anterior. Sin embargo, no requiere de mi parte notas más detalladas que las ya presentadas. Por lo tanto, le recomiendo encarecidamente que lea el artículo original .
¿Encontró útil este artículo? ¡Sígueme en Linkedin , Hackernoon y Medium ! ¡Por favor 👏 este artículo para compartirlo!
También publicado aquí.