Implementar Fallback con API Gateway
November 10, 2023
La resiliencia de la API es la capacidad de una API para fallar rápidamente o asegurarse de que continúe funcionando después de un fallo cuando se enfrenta a errores, tráfico elevado o fallos parciales del sistema. Esto implica implementar patrones comunes de diseño de resiliencia de API como reintentos, tiempos de espera, cortacircuitos, conmutación por error y respaldos. Un respaldo utilizando una puerta de enlace de API es un plan B para una API: cuando el servicio principal de la API falla, la puerta de enlace de API puede redirigir el tráfico a un servicio secundario o devolver una respuesta predefinida. En este artículo, exploraremos los desafíos de las técnicas de respaldo existentes y cómo implementarlas de manera eficiente utilizando la puerta de enlace de API APISIX.
Implementación de respaldos con APISIX
Para implementar un mecanismo de respaldo con APISIX, puedes utilizar su función incorporada de prioridades de upstream o usar un plugin de reescritura de respuesta para devolver una respuesta predefinida cuando falla una llamada de servicio. Aquí tienes un ejemplo paso a paso de cómo configurar ambos métodos de respaldo.
Requisito(s)
Esta guía asume que las siguientes herramientas están instaladas localmente:
- Antes de comenzar, es bueno tener un conocimiento básico de APISIX. Familiarizarse con puerta de enlace de API, y sus conceptos clave como rutas, upstream, API de administración, plugins, y el protocolo HTTP también será beneficioso.
- Docker se utiliza para instalar etcd y APISIX en contenedores.
- Instala cURL para enviar solicitudes a los servicios para su validación.
Iniciar el proyecto Docker de APISIX
Este proyecto aprovecha el archivo de configuración predefinido de Docker Compose para configurar, implementar y ejecutar APISIX, etcd, Prometheus y otros servicios con un solo comando. Primero, clona el repositorio apisix-docker en GitHub, ábrelo en tu editor favorito, navega a la carpeta example
e inicia el proyecto simplemente ejecutando el comando docker compose up
en una terminal desde la carpeta raíz del proyecto.
Cuando inicias el proyecto, Docker descarga las imágenes necesarias para ejecutarlo. También ejecuta dos servicios backend de ejemplo web1
y web2
. Puedes ver la lista completa de servicios en el archivo docker-compose.yaml.
Respaldo con prioridades de upstream de APISIX habilitadas
Puedes configurar cada nodo de upstream con un cierto nivel de prioridad. Cuando el nodo con la prioridad más alta falla, la puerta de enlace de API puede redirigir el tráfico a un nodo secundario con una prioridad más baja. La prioridad predeterminada para todos los nodos es 0, y los nodos con prioridad negativa pueden configurarse como respaldo.
Crea una ruta a los dos servicios y configura el atributo de prioridad para cada nodo de servicio de upstream:
curl "http://127.0.0.1:9180/apisix/admin/routes" -H "X-API-KEY: edd1c9f034335f136f87ad84b625c8f1" -X PUT -d '
{
"id":"backend-service-route",
"methods":[
"GET"
],
"uri":"/get",
"upstream":{
"nodes":[
{
"host":"web1",
"port":80,
"weight":1,
"priority":0
},
{
"host":"web2",
"port":80,
"weight":1,
"priority":-1
}
]
}
}'
methods
: Especifica el método HTTP que coincidirá con esta ruta. En este caso, está configurado para coincidir con solicitudesGET
.uri
: Es la ruta que coincidirá con la ruta. Por lo tanto, cualquier solicitudGET
a/get
será manejada por esta ruta.nodes
: Es una matriz de servidores backend. Cada objeto en la matriz representa un servidor con suhost
,port
yweight
. Elweight
se utiliza para el balanceo de carga; en este caso, ambos servidores tienen un peso de1
, lo que normalmente significa que compartirán el tráfico por igual.priority
: Es una configuración adicional para los dos nodos (web1
,web2
). El campopriority
se utiliza para determinar el orden en que se seleccionan los nodos. Un nodo con una prioridad más baja (un número negativo más alto) se utilizará solo si todos los nodos con prioridad más alta (números negativos más bajos o números positivos) no están disponibles.
Verifica si obtienes solo una respuesta del servicio web1
cuando envías una solicitud a la ruta:
curl "http://127.0.0.1:9080/get"
Deberías ver una respuesta similar a la siguiente:
hello web1
Esto significa que web1
se ha ejecutado primero ya que está funcionando. Ahora detén el contenedor del servicio web1
para verificar si APISIX recurre al servicio web2
.
docker container stop example-web1-1
Ahora, si envías otra solicitud a la ruta, obtendrás una respuesta del servicio de respaldo que especificamos.
curl "http://127.0.0.1:9080/get"
hello web2
Por defecto, tarda 60 segundos mientras la solicitud va primero al servicio uno y recurre al servicio dos si no está disponible. También puedes cambiar este tiempo configurando el atributo timeout
del objeto Upstream. Otra estrategia de respaldo podría ser durante las versiones. Si una nueva versión de la API tiene errores, puedes redirigir el tráfico a la versión anterior que está en espera utilizando la función de división de tráfico de APISIX. Recurre a la versión anterior si la nueva versión tiene problemas. Este método de respaldo también funciona bien con comprobaciones de salud de Upstream.
Respaldo con el plugin de reescritura de respuesta de APISIX
El plugin de reescritura de respuesta de APISIX te permite modificar el código de estado de la respuesta, el cuerpo y los encabezados antes de devolverla al cliente. Esto puede ser particularmente útil para implementar un mecanismo de respaldo al proporcionar una respuesta predeterminada cuando el servicio de upstream falla.
Si seguiste el primer enfoque, vuelve a ejecutar el contenedor del servicio web1
en Docker:
docker container start example-web1-1
Para usar el plugin de reescritura de respuesta para un respaldo, debes configurarlo en una ruta. Aquí tienes un ejemplo de cómo podrías habilitar el plugin usando un comando curl
:
curl "http://127.0.0.1:9180/apisix/admin/routes" -H "X-API-KEY: edd1c9f034335f136f87ad84b625c8f1" -X PUT -d '
{
"id":"backend-service-route",
"methods":[
"GET"
],
"uri":"/get",
"plugins":{
"response-rewrite":{
"status_code":200,
"body":"{\"message\":\"This is a fallback response when the service is unavailable\"}",
"vars":[
[
"status",
"==",
503
]
]
}
},
"upstream":{
"nodes":{
"web1:80":1
}
}
}'
En el ejemplo anterior, definimos un solo servicio backend (web1:80
) al que se debe dirigir el tráfico cuando se coincide con esta ruta. Si el servicio de upstream (web1:80
) responde con un estado 503 Servicio no disponible
, el plugin de response-rewrite
modificará la respuesta para tener un estado 200 OK
con un cuerpo JSON personalizado. Esto crea efectivamente una respuesta de respaldo cuando el servicio de upstream no está disponible.
"vars": [["status", "==", 503]]
: Esta condición le dice al plugin que aplique la reescritura solo cuando el código de estado original de la respuesta sea503 Servicio no disponible
.
Ahora, si envías una solicitud a la ruta, deberías obtener una respuesta modificada:
curl "http://127.0.0.1:9080/get"
{"message":"This is a fallback response when the service is unavailable"}
Desafíos de implementar un mecanismo de respaldo
Los respaldos son un componente crítico de un diseño de sistema resiliente. Sin embargo, pueden introducir más problemas cuando se implementan incorrectamente. Al discutir estrategias de respaldo, los desafíos pueden diferir entre entornos de una sola máquina y sistemas distribuidos. Revisémoslos para entenderlos con ejemplos y aprender cómo evitarlos utilizando APISIX.
Dificultad para probar la lógica de respaldo
Es difícil simular con precisión condiciones de fallo de la aplicación, como un fallo de la base de datos, en un contexto de una sola máquina. Probar estrategias de respaldo en sistemas distribuidos se vuelve aún más complejo debido a la participación de múltiples máquinas y servicios, lo que dificulta replicar todos los modos de fallo posibles. Por ejemplo, una API en un servidor local tiene un respaldo a una respuesta en caché cuando la base de datos no está disponible. Probar este escenario requiere simular un tiempo de inactividad de la base de datos, lo que podría no ser parte de las pruebas regulares, lo que lleva a un código de respaldo no probado bajo la carga real de producción.
APISIX puede configurarse para enrutar el tráfico y simular varios escenarios, incluyendo condiciones de respaldo. Esto permite pruebas más realistas de la lógica de respaldo en condiciones controladas, asegurando que los servicios de respaldo puedan manejar el tráfico de producción.
Los respaldos mismos pueden fallar
Si una solución de respaldo no es tan resiliente como se esperaba, podría fallar bajo la carga aumentada que ocurre cuando se activa, causando un fallo en cascada. Además, un respaldo a un servicio menos eficiente puede aumentar los tiempos de respuesta y la carga, lo que podría llevar a una ralentización o interrupción del sistema. Por ejemplo, una API podría tener un respaldo para escribir registros en un sistema de archivos local cuando un servicio de registro remoto no está disponible. Esto podría llevar a un rendimiento más lento debido a operaciones de E/S de archivos sincrónicas.
Con APISIX, puedes priorizar el tráfico para asegurarte de que las solicitudes críticas se procesen primero. Esto puede evitar que un servicio de respaldo se sature y empeore el rendimiento del sistema.
Los respaldos tienen riesgos operativos
Implementar un respaldo podría introducir nuevos puntos de fallo, como una base de datos secundaria que no se mantiene sincronizada con la principal, lo que lleva a inconsistencias de datos.
Las características de observabilidad de APISIX, como el registro, las métricas y el seguimiento, pueden monitorear la salud y el rendimiento de los servicios principales y de respaldo. Este monitoreo en tiempo real puede ayudar a identificar y mitigar los riesgos asociados con las estrategias de respaldo.
Los respaldos tienen errores latentes y amplificados
Las rutas de código de respaldo pueden contener errores inactivos que solo ocurren bajo condiciones de fallo específicas, lo que podría no suceder a menudo y es difícil de predecir, lo que podría no descubrirse durante meses o años. Por ejemplo, un mecanismo de respaldo en una API que cambia a un método de autenticación diferente durante una interrupción del servicio de identidad podría contener un error que solo aparece cuando se activa el respaldo, lo que podría ser un evento raro.
APISIX admite pruebas A/B continuas y lanzamientos canarios, lo que permite a los equipos probar rutas de respaldo en producción con un pequeño porcentaje de tráfico. Esta exposición continua puede ayudar a descubrir errores latentes antes de que se vuelvan críticos.
Los respaldos se ejercen con poca frecuencia
Los mecanismos de respaldo se usan con poca frecuencia, por lo que cuando se activan, podrían no funcionar como se espera debido a la falta de pruebas y actualizaciones regulares. Por ejemplo, una API que sirve datos geográficos podría tener un respaldo a un conjunto de datos estáticos cuando la fuente de datos dinámicos no está disponible. Si este respaldo se usa raramente, podría servir información obsoleta cuando se active porque no se actualiza ni prueba regularmente.
Por otro lado, APISIX permite la configuración de enrutamiento dinámico y puede usarse para redirigir periódicamente una porción del tráfico a los servicios de respaldo. Esto asegura que las rutas de respaldo se ejerciten regularmente y permanezcan listas para su uso.
Conclusión
Los respaldos son una red de seguridad para cuando las cosas salen mal con las API. Al utilizar la configuración de upstream de APISIX o el plugin de reescritura de respuesta, los desarrolladores pueden proporcionar respuestas reflexivas y amigables para el usuario que mantengan el sistema funcional y mantengan la confianza con los usuarios. La clave es anticipar posibles puntos de fallo y diseñar respaldos que proporcionen la mejor experiencia posible bajo las circunstancias.