Desbloqueando soluciones personalizadas: aprovechando Apache APISIX y Node-RED en armonía
March 6, 2024
Antecedentes
Muchos usuarios de Apache APISIX han requerido personalización en entornos empresariales para cumplir con requisitos específicos en ciertos escenarios, a pesar de que cuenta con muchas funciones potentes integradas en sus plugins.
Los usuarios a menudo eligen escribir plugins en Lua y montarlos en instancias de APISIX para su uso. Sin embargo, Lua tiene una audiencia relativamente limitada. Aunque es fácil comenzar a usarlo, dominarlo no es sencillo, y la implementación de lógicas complejas de transformación de datos en Lua puede volverse bastante intrincada. Actualmente, solo una parte de los hooks está expuesta por el Java Plugin Runner para que los desarrolladores los llamen, lo que requiere modificaciones en el código fuente del Java Plugin Runner para funcionalidades que no están directamente soportadas.
El siguiente diagrama ilustra tres patrones comunes de uso de plugins: los plugins de Lua se ejecutan directamente integrados en el núcleo de APISIX; el Plugin Runner se comunica mediante RPC con Plugin Runners en lenguajes como Java, Golang, etc.; y los plugins WASM se convierten en bytecode y se ejecutan internamente en el núcleo de APISIX.
Se ha observado a partir de los comentarios de los usuarios que la necesidad de plugins personalizados a menudo está relacionada con la transformación de datos, como los parámetros de solicitud HTTP y la llamada a APIs externas para el procesamiento de datos.
Para abordar esto, se ha propuesto un nuevo enfoque para mejorar las capacidades de Apache APISIX. Este enfoque implica usar solo plugins integrados en Apache APISIX para configurar capacidades comunes como autenticación y limitación de tasa, mientras que la lógica personalizada se coloca en servicios externos. El servicio externo puede ser un programa binario o otro servicio API, que se tratará como un upstream para APISIX. Este servicio externo manejará solicitudes y respuestas como un middleware. Este enfoque también se puede aplicar a otras puertas de enlace API o servicios de proxy.
Descripción del Escenario
Tenemos una serie de servicios que proporcionan servicios de consulta de datos upstream (este artículo utiliza https://api-ninjas.com/api como ejemplo). Por ejemplo, se puede recuperar la información meteorológica más reciente y la información sobre el país (como el PIB del país, el nombre de la capital y la unidad monetaria) basándose en el nombre de la ciudad.
Nuestro objetivo principal es proporcionar una interfaz de solicitud genérica a los desarrolladores, mientras que aún podemos determinar el contenido de los datos que desean recuperar basándonos en el nombre de la ciudad y los parámetros del alcance de los datos. Además, para proteger los servicios upstream del abuso, debemos agregar un servicio de autenticación a la interfaz del desarrollador, permitiendo que solo las solicitudes con el API Key correcto se reenvíen al servicio upstream.
Análisis del Problema
Antes de introducir Node-Red, para cumplir con los requisitos anteriores, los desarrolladores de APISIX considerarían usar plugins de Lua. Aunque Apache APISIX proporciona documentación detallada sobre el desarrollo de plugins, los desarrolladores de negocios necesitan aprender la sintaxis de Lua y técnicas de ajuste, comprender los diferentes hooks de solicitud expuestos por APISIX, y recargar continuamente los plugins para validación mientras escriben la lógica para la extracción y validación de parámetros. Después de completar las pruebas, también necesitan empaquetar los plugins de Lua en el programa APISIX o distribuirlos a todas las instancias de APISIX para su montaje.
Los requisitos de ejemplo que proporcionamos en este blog implican analizar parámetros específicos de las solicitudes de los clientes y luego construir solicitudes para recuperar datos de diferentes servicios upstream. Sin embargo, pasamos mucho tiempo tratando con transacciones fuera de la escritura del negocio. Por lo tanto, para lógicas que involucran conversión de parámetros, conversión de formato o llamadas externas, podemos adoptar un enfoque más ligero e intuitivo, que es precisamente el problema que Node-Red puede resolver.
Introducción a Node-Red
Node-RED es una herramienta de programación basada en flujos potente y fácil de usar, adecuada para tareas de automatización y procesamiento de flujos de datos en varios dominios. Su interfaz de programación, su rica biblioteca de nodos y su flexible extensibilidad nos permiten construir rápidamente flujos complejos e implementar varios escenarios de aplicación. Aquí hay algunos de los nodos proporcionados por Node-RED:
-
Nodo HTTP_IN: Expone un endpoint para la invocación de servicios externos, que usaremos como un servicio upstream para APISIX.
-
Nodo Function: Permite a los desarrolladores escribir funciones de código en JavaScript para modificar, eliminar, etc., la entrada/salida.
-
Nodo Switch: Permite a los desarrolladores establecer un conjunto de condiciones para ingresar al siguiente nodo especificado cuando se cumple una condición.
-
Nodo HTTP_Request: Puede establecer URL, etc., para enviar datos a este endpoint a través de Node-RED cuando se ejecuta todo el flujo de trabajo.
-
Nodo Change: Puede agregar, modificar o eliminar valores especificados de un objeto especificado.
-
Nodo HTTP_Response: Se utiliza para devolver respuestas a los clientes.
Además de los nodos enumerados anteriormente, Node-RED también proporciona muchos otros nodos integrados. Este artículo mostrará a los lectores cómo implementar los requisitos anteriores a través de Node-RED.
Demostración del Ejemplo
Configuración del Entorno
Implementaremos los componentes necesarios mediante contenedores, utilizando un Droplet de DigitalOcean como recurso del servidor.
$ doctl compute ssh-key list
ID Name FingerPrint
25621060 Zhiyuan Ju 2c:84:b7:d8:14:0a:a0:0f:ca:fe:ca:24:06:a4:fe:39
$ doctl compute droplet create \
--image docker-20-04 \
--size s-2vcpu-4gb-amd \
--region sgp1 \
--vpc-uuid 646cf2b8-03d8-4f48-b7c8-57cdee60ad27 \
--ssh-keys 25621060 \
apisix-nodered-docker-ubuntu-s-2vcpu-4gb-amd-sgp1-01
$ doctl compute droplet list
ID Name Public IPv4 Private IPv4 Public IPv6 Memory VCPUs Disk Region Image VPC UUID Status Tags Features Volumes
404094941 apisix-nodered-docker-ubuntu-s-2vcpu-4gb-amd-sgp1-01 143.198.192.64 10.104.0.3 4096 2 80 sgp1 Ubuntu Docker 25.0.3 on Ubuntu 22.04 646cf2b8-03d8-4f48-b7c8-57cdee60ad27 active droplet_agent,private_networking
Implementación de Apache APISIX
Usaremos el APISIX Quickstart para iniciar una nueva instancia de APISIX, para la documentación específica, consulte https://docs.api7.ai/apisix/getting-started/.
$ curl -sL https://run.api7.ai/apisix/quickstart | sh
Implementación de Node-RED
Node-RED proporciona múltiples métodos de implementación, y lo implementaremos rápidamente a través de Docker y lo integraremos con el entorno existente. Para más detalles de implementación, consulte la documentación oficial: https://nodered.org/docs/getting-started/.
Al implementar Node-RED, asegúrese de que el contenedor se agregue a la red de APISIX para garantizar que pueda comunicarse con APISIX y manejar solicitudes.
$ docker run -d -it -p 1880:1880 -v $PWD/configs/nodered/data:/data --network=apisix-quickstart-net --name mynodered -u Node-Red:dialout nodered/Node-Red
Configuración de Node-RED
- Para manejar las solicitudes que ingresan a Node-RED desde APISIX, Node-Red necesita verificar si los parámetros en la solicitud existen y son válidos. Si los parámetros faltan o son inválidos, se devolverá un mensaje de error. Si son válidos, se ejecutará el siguiente nodo. En este escenario específico, solo permitimos consultas de datos para dos ciudades, Estocolmo (city=stockholm) y Berlín (city=berlin).
- Una vez que la solicitud ingresa al siguiente nodo, Node-RED necesita determinar el tipo de datos solicitados. En este escenario, hay tres tipos: información meteorológica (
scope=weather
), información sobre el país donde se encuentra la ciudad (scope=country
), y el PIB del país donde se encuentra la ciudad (scope=gdp
).
- Si tanto el parámetro
City
comoScope
son válidos, Node-RED determinará de qué API recuperar los datos basándose en el valor de scope. Después de establecer la URL, el Método, el Payload, el X-API-Key, etc., el nodo de Node-RED accederá al endpoint correspondiente para recuperar los datos cuando se active.
- Al recuperar datos para
scope=gdp
, Node-RED necesita extraer el valor del PIB del cuerpo de la respuesta de la API externa. Esto se puede hacer usando el nodo Change para la extracción o el nodo Function.
- Finalmente, Node-RED devolverá los datos procesados a APISIX, que los pasará al cliente. El diagrama final de Node-RED se muestra a continuación.
Creación de Rutas en APISIX
Para hacer que el servicio Node-Red esté disponible para los clientes, necesitamos usar APISIX como un proxy inverso para los endpoints expuestos por Node-Red. Aquí están los pasos que debe seguir:
-
Crear una Ruta en APISIX y establecer
mynodered:1880
como el upstream para esta Ruta. Al hacer esto, todas las solicitudes enviadas a este endpoint se reenviarán al servicio Node-Red. -
Habilitar la Autenticación por Clave para asegurar que solo las solicitudes que llevan un API Key válido puedan pasar la autenticación y acceder al servicio Node-Red.
Siguiendo los pasos anteriores, podemos exponer de manera segura el servicio Node-Red a los clientes y asegurar que solo los usuarios autorizados puedan acceder a él.
$ curl -i "http://127.0.0.1:9180/apisix/admin/routes" -X PUT -d '
{
"id": "proxy-global-data-endpoint",
"uri": "/global-data",
"upstream": {
"type": "roundrobin",
"nodes": {
"mynodered:1880": 1
}
},
"plugins": {
"key-auth": {}
}
}'
$ curl -i "http://127.0.0.1:9180/apisix/admin/consumers" -X PUT -d '
{
"username": "tom",
"plugins": {
"key-auth": {
"key": "secret-key"
}
}
}'
Validación de Solicitudes
Intentaremos varios escenarios por separado para verificar si APISIX y Node-Red se comportan como se espera:
Escenario 1
-
Descripción del Escenario: Acceder a la API con una Key incorrecta.
-
Resultado Esperado: Dado que el API Key proporcionado es incorrecto, la solicitud debe ser rechazada y se debe devolver el mensaje de error correspondiente.
$ curl http://143.198.192.64:9080/global-data -H "apikey: invalid-key" -i
HTTP/1.1 401 Unauthorized
Date: Mon, 04 Mar 2024 07:47:24 GMT
Content-Type: text/plain; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Server: APISIX/3.8.0
{"message":"Invalid API key in request"}
Escenario 2
-
Descripción del Escenario: Acceder a la API con la Key correcta, pero un campo
City
inválido. -
Resultado Esperado: Dado que los parámetros de la solicitud no cumplen con los requisitos, se debe devolver el mensaje de error correspondiente, indicando que el campo
City
es inválido.
$ curl "http://143.198.192.64:9080/global-data?city=singapore&scope=country" -H "apikey: secret-key" -i
HTTP/1.1 400 Bad Request
Content-Type: application/json; charset=utf-8
Content-Length: 69
Connection: keep-alive
Access-Control-Allow-Origin: *
X-Content-Type-Options: nosniff
ETag: W/"45-IOhgB2XkDHi2Kt4PP42n1xa8Gys"
Date: Mon, 04 Mar 2024 07:48:02 GMT
Server: APISIX/3.8.0
{"errorCode":400,"message":"Allowed city Options: Stockholm, Berlin"}
Escenario 3
-
Descripción del Escenario: Acceder a la API con la Key correcta, y campos
City
yScope
válidos para recuperar datos del país. -
Resultado Esperado: La solicitud debe ser exitosa y se debe devolver información relevante sobre el país donde se encuentra la
City
.
$ curl "http://143.198.192.64:9080/global-data?city=stockholm&scope=country" -H "apikey: secret-key" -i
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 947
Connection: keep-alive
Access-Control-Allow-Origin: *
X-Content-Type-Options: nosniff
ETag: W/"3b3-XDlm9OHfuUrWH+g42q8L1F2uu/o"
Date: Mon, 04 Mar 2024 07:48:26 GMT
Server: APISIX/3.8.0
[{"gdp":556086,"sex_ratio":100.4,"surface_area":438574,"life_expectancy_male":80.8,"unemployment":6.7,"imports":158710,"homicide_rate":1.1,"currency":{"code":"SEK","name":"Swedish Krona"},"iso2":"SE","employment_services":80.7,"employment_industry":17.7,"urban_population_growth":1.1,"secondary_school_enrollment_female":157.9,"employment_agriculture":1.6,"capital":"Stockholm","co2_emissions":37.6,"forested_area":68.9,"tourists":7440,"exports":160538,"life_expectancy_female":84.4,"post_secondary_enrollment_female":82.1,"post_secondary_enrollment_male":52.7,"primary_school_enrollment_female":127.4,"infant_mortality":2,"gdp_growth":2.2,"threatened_species":98,"population":10099,"urban_population":87.7,"secondary_school_enrollment_male":148.1,"name":"Sweden","pop_growth":0.7,"region":"Northern Europe","pop_density":24.6,"internet_users":92.1,"gdp_per_capita":55766.8,"fertility":1.8,"refugees":310.4,"primary_school_enrollment_male":125.8}]
Escenario 4
-
Descripción del Escenario: Acceder a la API con la Key correcta, y campos
City
yScope
válidos para recuperar datos deGDP
. -
Resultado Esperado: La solicitud debe ser exitosa y se debe devolver los datos de
GDP
del país donde se encuentra laCity
.
$ curl "http://143.198.192.64:9080/global-data?city=stockholm&scope=gdp" -H "apikey: secret-key" -i
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 6
Connection: keep-alive
Access-Control-Allow-Origin: *
ETag: W/"6-j8I5kokycgWjCeKC1c2UfJW7AQY"
Date: Mon, 04 Mar 2024 07:48:48 GMT
Server: APISIX/3.8.0
556086
Al verificar estos cuatro escenarios, podemos confirmar que APISIX y Node-Red están funcionando como se espera y pueden manejar correctamente varios tipos de solicitudes.
Resumen
Proporcionamos un nuevo enfoque para abordar de manera más inteligente el problema del desarrollo de capacidades personalizadas, demostrado por un ejemplo detallado.
-
Enrutamiento de Solicitudes API y Verificación de Identidad: Primero, utilizando la funcionalidad de enrutamiento de Apache APISIX y los plugins de autenticación, APISIX reenvía las solicitudes de los clientes al servicio Node-Red cuando las credenciales proporcionadas son válidas.
-
Procesamiento y Transformación de Solicitudes: En Node-Red, creamos un flujo para procesar las solicitudes API entrantes. Usando el nodo HTTP de entrada para recibir solicitudes de APISIX, analizamos y validamos los parámetros de la solicitud para asegurar que cumplen con los requisitos del negocio.
-
Procesamiento de Lógica de Negocio: Una vez que se recibe una solicitud válida, podemos ejecutar la lógica de negocio en Node-Red. Por ejemplo, enviar solicitudes a diferentes APIs de negocio basadas en los parámetros para recuperar datos y extraer los campos requeridos de la respuesta. Después de completar estas operaciones, los datos finales se devuelven a APISIX.
-
Manejo de Errores y Registro: Durante el procesamiento, si ocurren errores o excepciones, podemos agregar nodos de manejo de errores en Node-Red para capturar y manejar situaciones excepcionales. Además, podemos usar nodos de registro para registrar información clave durante el procesamiento para la posterior resolución de problemas y análisis, lo cual no se demuestra en este ejemplo.
Al combinar APISIX y Node-Red, podemos implementar visualmente un proceso completo de manejo de solicitudes, incluyendo enrutamiento de solicitudes, procesamiento de datos, lógica de negocio, etc., sin la necesidad de escribir código complejo o plugins. Esta solución flexible y personalizable puede ayudarnos a construir y ajustar la funcionalidad del sistema más rápidamente, mejorar la eficiencia del desarrollo, reducir los costos de desarrollo y garantizar la estabilidad y escalabilidad del sistema.