En lo que respecta a los sitios web con más tráfico de Internet, NGINX y NGINX Plus dominan el mercado. De hecho, NGINX está presente en más sitios del millón con más tráfico del mundo que cualquier otro servidor web. Su capacidad para gestionar más de un millón de conexiones simultáneas en un único servidor ha impulsado su adopción por parte de sitios y aplicaciones «a hiperescala» como Airbnb, Netflix y Uber.
Aunque NGINX Plus es conocido principalmente como servidor web, proxy inverso HTTP y equilibrador de carga, también es un controlador de entrega de aplicaciones (ADC) con todas las funciones y compatible con aplicaciones TCP y UDP. Su arquitectura basada en eventos y todos los demás atributos que han propiciado su éxito en casos de uso de HTTP son igualmente aplicables al Internet de las cosas (IoT).
En este artículo mostramos cómo se puede utilizar NGINX Plus para equilibrar la carga del tráfico MQTT. MQTT se publicó originalmente en 1999 para la comunicación con yacimientos petrolíferos remotos. En 2013 se actualizó para casos de uso del IoT y, desde entonces, se ha convertido en el protocolo preferido para muchas implementaciones de IoT. Las implementaciones de IoT en producción con millones de dispositivos exigen un alto rendimiento y funcionalidades avanzadas de un equilibrador de carga. En esta serie de dos entradas de blog analizaremos los siguientes casos de uso avanzados.
- Balanceo de carga del tráfico MQTT (este artículo)
- Cifrado y autenticación del tráfico MQTT
El entorno de pruebas
Para explorar las funciones de NGINX Plus, utilizaremos un entorno de prueba sencillo que representa los componentes clave de un entorno de IoT con un clúster de brókeres MQTT. Los brókeres MQTT de este entorno son instancias de HiveMQ que se ejecutan dentro de contenedores Docker.
Entorno de prueba para el equilibrio de carga MQTT y la persistencia de sesiones
NGINX Plus actúa como proxy inverso y equilibrador de carga para el bróker MQTT y escucha en el puerto MQTT predeterminado 1883. Esto proporciona al cliente una interfaz sencilla y coherente, mientras que los nodos MQTT del backend pueden ampliarse (e incluso desconectarse) sin afectar en modo alguno al cliente. Utilizamos la Mosquitto herramienta de línea de comandos como cliente, que representa los dispositivos IoT del entorno de pruebas.
Todos los casos de uso de esta entrada y de la parte 2 utilizan este entorno de pruebas, y todas las configuraciones se aplican directamente a la arquitectura que se muestra en la figura. Para obtener instrucciones completas sobre cómo crear el entorno de pruebas, consulte el Apéndice 1.
Equilibrado de carga de MQTT con comprobaciones de estado activas para una alta disponibilidad
Una función principal de un balanceador de carga es proporcionar alta disponibilidad a la aplicación, de modo que se puedan añadir o eliminar servidores backend, o que estos puedan quedar fuera de servicio, sin afectar al cliente. Para garantizar un funcionamiento fiable, es fundamental realizar comprobaciones de estado que verifiquen de forma proactiva la disponibilidad de cada servidor backend. Gracias a las comprobaciones de estado activas, NGINX Plus puede retirar los servidores que fallen del grupo de balanceo de carga antes de que reciban solicitudes reales de los clientes.
La utilidad de una comprobación de estado depende de la precisión con la que simule el tráfico real de la aplicación y analice la respuesta. Las comprobaciones sencillas de disponibilidad del servidor, como un ping, no garantizan que el servicio de backend esté en funcionamiento. Las comprobaciones de puertos TCP abiertos tampoco garantizan que la propia aplicación funcione correctamente. Aquí configuramos un balanceo de carga básico para el entorno de pruebas con una comprobación de estado que garantiza que cada servidor de backend pueda aceptar nuevas conexiones MQTT.
Vamos a realizar cambios en dos archivos de configuración.
En el archivo principal nginx.conf, incluimos el siguiente bloque stream y la directiva include para que NGINX Plus lea la configuración del balanceo de carga TCP desde uno o varios archivos del subdirectorio stream_conf.d, que se encuentra en el mismo directorio que nginx.conf. Hacemos esto en lugar de incluir la configuración propiamente dicha en nginx.conf.
Cargando ejemplo de código…
A continuación, en el mismo directorio que nginx.conf creamos el directorio stream_conf.d para almacenar nuestros archivos de configuración de TCP y UDP. Tenga en cuenta que no utilizamos el directorio preexistente conf.d porque, de forma predeterminada, está reservado para el contexto de configuración http, por lo que añadir allí una configuración de stream provocará un error.
Cargando ejemplo de código…
En stream_mqtt_healthcheck.conf definimos primero el formato del registro de acceso para el tráfico MQTT (líneas 1–2). Este formato es deliberadamente similar al formato de registro común de HTTP, de modo que los registros resultantes puedan importarse en herramientas de análisis de registros.
A continuación, definimos el grupo upstream denominado hive_mq (líneas 4–9), que contiene tres servidores MQTT. En nuestro entorno de pruebas, se puede acceder a cada uno de ellos en localhost mediante un número de puerto único. La directiva zone define una cantidad de memoria que se comparte entre todos los procesos de trabajo de NGINX Plus para mantener el estado del balanceo de carga y la información de estado.
El bloque match (líneas 11–15) define la comprobación de estado utilizada para verificar la disponibilidad de los servidores MQTT. La directiva send es una representación hexadecimal de un paquete MQTT CONNECT completo con un identificador de cliente (ClientId) de nginx health check. Este se envía a cada uno de los servidores definidos en el grupo upstream cada vez que se ejecuta la comprobación de estado. La directiva expect correspondiente describe la respuesta que debe devolver el servidor para que NGINX Plus lo considere en buen estado. En este caso, la cadena hexadecimal de 4 bytes 20 02 00 00 es un paquete MQTT CONNACK completo. La recepción de este paquete demuestra que el servidor MQTT puede aceptar nuevas conexiones de clientes.
El bloque server (líneas 17–25) configura cómo gestiona NGINX Plus los clientes. NGINX Plus escucha en el puerto MQTT predeterminado, el 1883, y reenvía todo el tráfico al grupo ascendente hive_mq (línea 19). La directiva health_check especifica que se realizan comprobaciones de estado en el grupo ascendente (con la frecuencia predeterminada de cinco segundos) y que se utiliza la comprobación definida por el bloque mqtt_conn match.
Verificación de la configuración
Para comprobar que esta configuración básica funciona, podemos utilizar el cliente Mosquitto para publicar algunos datos de prueba en nuestro entorno de pruebas.
$ mosquitto_pub -d -h mqtt.example.com -t "topic/test" -m "test123" -i "thing001"Client thing001 sending CONNECT
Client thing001 received CONNACK
Client thing001 sending PUBLISH (d0, q0, r0, m1, 'topic/test', ... (7 bytes))
Client thing001 sending DISCONNECT
$ tail --lines=1 /var/log/nginx/mqtt_access.log
192.168.91.1 [23/Mar/2017:11:41:56 +0000] TCP 200 23 4 127.0.0.1:18831La línea del registro de acceso muestra que NGINX Plus recibió un total de 23 bytes y que se enviaron 4 bytes al cliente (el paquete CONNACK). También podemos ver que se eligió el nodo MQTT node1 (puerto 18831). Como muestran las siguientes líneas del registro de acceso, al repetir la prueba, el algoritmo de balanceo de carga Round Robin predeterminado selecciona sucesivamente node1, node2 y node3.
$ tail --lines=4 /var/log/nginx/mqtt_access.log192.168.91.1 [23/Mar/2017:11:41:56 +0000] TCP 200 23 4 127.0.0.1:18831
192.168.91.1 [23/Mar/2017:11:42:26 +0000] TCP 200 23 4 127.0.0.1:18832
192.168.91.1 [23/Mar/2017:11:42:27 +0000] TCP 200 23 4 127.0.0.1:18833
192.168.91.1 [23/Mar/2017:11:42:28 +0000] TCP 200 23 4 127.0.0.1:18831Equilibrado de carga de MQTT para la persistencia de sesiones con el módulo JavaScript de NGINX
[Editor – El siguiente caso de uso es solo uno de los muchos disponibles para el módulo JavaScript de NGINX. Para consultar la lista completa, véase Casos de uso del módulo JavaScript de NGINX.]
El código de esta sección se ha actualizado de la siguiente manera para reflejar los cambios realizados en la implementación de NGINX JavaScript desde la publicación original del blog:
- Para utilizar el objeto de sesión refactorizado (
s) del módulo Stream, que se introdujo en NGINX JavaScript 0.2.4. - Para utilizar la directiva
js_import, que sustituye a la directivajs_includeen NGINX Plus R23 y versiones posteriores. Para obtener más información, consulte la documentación de referencia del módulo JavaScript de NGINX ; en la sección Configuración de ejemplo se muestra la sintaxis correcta para los archivos de configuración de NGINX y JavaScript.
El balanceo de carga Round Robin es un mecanismo eficaz para distribuir las conexiones de los clientes entre un grupo de servidores. Sin embargo, hay varias razones por las que no resulta idóneo para las conexiones MQTT.
Los servidores MQTT suelen requerir una conexión de larga duración entre el cliente y el servidor, y en el servidor puede acumularse una gran cantidad de información sobre el estado de la sesión. Lamentablemente, debido a la naturaleza de los dispositivos IoT y de las redes IP que utilizan, las conexiones se interrumpen, lo que obliga a algunos clientes a volver a conectarse con frecuencia. NGINX Plus puede utilizar su algoritmo de equilibrio de carga Hash para seleccionar un servidor MQTT en función de la dirección IP del cliente. Basta con añadir hash $remote_addr; al bloque upstream para habilitar la persistencia de sesión, de modo que cada vez que se establezca una nueva conexión desde una dirección IP de cliente determinada, se seleccione el mismo servidor MQTT.
Pero no podemos confiar en que los dispositivos IoT vuelvan a conectarse desde la misma dirección IP, especialmente si utilizan redes móviles (por ejemplo, GSM o LTE). Para garantizar que el mismo cliente vuelva a conectarse al mismo servidor MQTT, debemos utilizar el identificador de cliente MQTT como clave del algoritmo hash.
Persistencia de sesión basada en el ClientId de MQTT
El ClientId de MQTT es un elemento obligatorio del paquete CONNECT inicial, lo que significa que está disponible para NGINX Plus antes de que el paquete se reenvíe mediante proxy al servidor ascendente. Podemos usar NGINX JavaScript para analizar el paquete CONNECT y extraer el ClientId como una variable que después puede utilizar la directiva hash para implementar la persistencia de sesión específica de MQTT.
NGINX JavaScript es el lenguaje de configuración programática «nativo de NGINX». Es una implementación única de JavaScript para NGINX y NGINX Plus, diseñada específicamente para casos de uso del lado del servidor y el procesamiento de cada solicitud. Tiene tres características clave que la hacen adecuada para implementar la persistencia de sesiones para MQTT:
- NGINX JavaScript está estrechamente integrado con las fases de procesamiento de NGINX Plus, lo que nos permite inspeccionar los paquetes de los clientes antes de que se distribuya su carga entre el grupo de servidores upstream.
- NGINX JavaScript utiliza métodos integrados de JavaScript para procesar cadenas y valores numéricos, lo que permite analizar de forma eficiente los protocolos de capa 4. El análisis propiamente dicho del paquete
CONNECTde MQTT requiere menos de 20 líneas de código. - NGINX JavaScript puede crear variables que están disponibles para la configuración de NGINX Plus.
Para obtener instrucciones sobre cómo habilitar NGINX JavaScript, consulte el Apéndice 2.
Configuración de NGINX Plus para la persistencia de sesiones
La configuración de NGINX Plus para este caso de uso sigue siendo relativamente sencilla. La siguiente configuración es una versión modificada del ejemplo de «Equilibrio de carga con comprobaciones de estado activas», en la que se han omitido las comprobaciones de estado para mayor brevedad.
Cargando ejemplo de código…
Comenzamos especificando la ubicación del código JavaScript de NGINX mediante la directiva js_import. La directiva js_set indica a NGINX Plus que llame a la función setClientId cuando necesite evaluar la variable $mqtt_client_id. Añadimos más información al registro de acceso incorporando esta variable al mqtt formato de registro en la línea 5.
Habilitamos la persistencia de sesión en la línea 12 mediante la directiva hash, que especifica $mqtt_client_id como clave. Observe que utilizamos el parámetro consistent para que, si falla un servidor ascendente, su parte del tráfico se distribuya uniformemente entre los servidores restantes sin afectar a las sesiones ya establecidas en ellos. El hash coherente se analiza con más detalle en nuestra entrada de blog sobre la fragmentación de una caché web; sus principios y ventajas también son aplicables en este caso.
La directiva js_preread (línea 18) especifica la función JavaScript de NGINX que se ejecuta durante la fase de lectura previa del procesamiento de solicitudes. Esta fase se activa para cada paquete (en ambas direcciones) y tiene lugar antes de que se realice el proxy, de modo que el valor de $mqtt_client_id esté disponible cuando se necesite en el bloque upstream.
Código JavaScript de NGINX para la persistencia de sesiones
Definimos el código JavaScript para extraer el ClientId de MQTT en el archivo mqtt.js, que se carga mediante la directiva js_import en el archivo de configuración de NGINX Plus (stream_mqtt_session_persistence.conf).
Cargando ejemplo de código…
La función principal, getClientId(), se declara en la línea 4. Se le pasa el objeto denominado s, que representa la sesión TCP actual. El objeto de sesión tiene numerosas propiedades, varias de las cuales se utilizan en esta función.
Las líneas 5–9 garantizan que el paquete actual sea el primero que se recibe del cliente. Los mensajes posteriores del cliente y las respuestas del servidor se ignoran para que, una vez establecida la conexión, no se añada ninguna sobrecarga al flujo de tráfico.
Cargando ejemplo de código…
Las líneas 10–24 examinan la cabecera MQTT para comprobar que el paquete sea de tipo «CONNECT» y determinar dónde comienza la carga útil MQTT.
Cargando ejemplo de código…
Las líneas 27–32 extraen el ClientId de la carga útil y almacenan el valor en la variable global de JavaScript client_id_str. A continuación, esta variable se exporta a la configuración de NGINX mediante la función setClientId (líneas 43–45).
Verificación de la persistencia de la sesión
Ahora podemos volver a utilizar el cliente Mosquitto para comprobar la persistencia de la sesión enviando una serie de solicitudes de publicación MQTT con tres valores de ClientId diferentes (la opción «-i»).
$ mosquitto_pub -h mqtt.example.com -t "topic/test" -m "test123" -i "foo"$ mosquitto_pub -h mqtt.example.com -t "topic/test" -m "test123" -i "bar"
$ mosquitto_pub -h mqtt.example.com -t "topic/test" -m "test123" -i "baz"
$ mosquitto_pub -h mqtt.example.com -t "topic/test" -m "test123" -i "foo"
$ mosquitto_pub -h mqtt.example.com -t "topic/test" -m "test123" -i "foo"
$ mosquitto_pub -h mqtt.example.com -t "topic/test" -m "test123" -i "foo"
$ mosquitto_pub -h mqtt.example.com -t "topic/test" -m "test123" -i "bar"
$ mosquitto_pub -h mqtt.example.com -t "topic/test" -m "test123" -i "bar"
$ mosquitto_pub -h mqtt.example.com -t "topic/test" -m "test123" -i "baz"
$ mosquitto_pub -h mqtt.example.com -t "topic/test" -m "test123" -i "baz"Al examinar el registro de acceso, se observa que el ClientId foo siempre se conecta a node1 (puerto 18831), el ClientId bar siempre se conecta a node2 (puerto 18832) y el ClientId baz siempre se conecta a node3 (puerto 18833).
$ tail /var/log/nginx/mqtt_access.log192.168.91.1 [23/Mar/2017:12:24:24 +0000] TCP 200 23 4 127.0.0.1:18831 foo
192.168.91.1 [23/Mar/2017:12:24:28 +0000] TCP 200 23 4 127.0.0.1:18832 bar
192.168.91.1 [23/Mar/2017:12:24:32 +0000] TCP 200 23 4 127.0.0.1:18833 baz
192.168.91.1 [23/Mar/2017:12:24:35 +0000] TCP 200 23 4 127.0.0.1:18831 foo
192.168.91.1 [23/Mar/2017:12:24:37 +0000] TCP 200 23 4 127.0.0.1:18831 foo
192.168.91.1 [23/Mar/2017:12:24:38 +0000] TCP 200 23 4 127.0.0.1:18831 foo
192.168.91.1 [23/Mar/2017:12:24:42 +0000] TCP 200 23 4 127.0.0.1:18832 bar
192.168.91.1 [23/Mar/2017:12:24:44 +0000] TCP 200 23 4 127.0.0.1:18832 bar
192.168.91.1 [23/Mar/2017:12:24:47 +0000] TCP 200 23 4 127.0.0.1:18833 baz
192.168.91.1 [23/Mar/2017:12:24:48 +0000] TCP 200 23 4 127.0.0.1:18833 bazTenga en cuenta que también contamos con la ventaja de que el ClientId de MQTT aparece en las líneas del registro de acceso, independientemente de si utilizamos persistencia de sesión o cualquier otro algoritmo de balanceo de carga.
Conclusión
En esta primera entrada de una serie de dos partes, hemos mostrado cómo NGINX Plus utiliza comprobaciones de estado activas para mejorar la disponibilidad y la fiabilidad de las aplicaciones de IoT, y cómo NGINX JavaScript puede ampliar NGINX Plus al proporcionar una función de equilibrio de carga de capa 7, como la persistencia de sesión para el tráfico TCP. En la segunda parte, analizamos cómo NGINX Plus puede reforzar la seguridad de sus aplicaciones de IoT descargando la terminación TLS y autenticando a los clientes.
Ya sea en combinación con NGINX JavaScript o por sí solo, el alto rendimiento y la eficiencia inherentes de NGINX Plus lo convierten en una solución ideal como balanceador de carga de software para su infraestructura de IoT.
Para probar NGINX JavaScript con NGINX Plus, inicia tu prueba gratuita de 30 días o ponte en contacto con nosotros para hablar sobre tus casos de uso.
Apéndices
Creación del entorno de pruebas
Instalamos el entorno de pruebas en una máquina virtual para que estuviera aislado y fuera reproducible. Sin embargo, no hay ningún motivo por el que no pueda instalarlo en un servidor físico, es decir, «bare metal».
Instalación de NGINX Plus
Consulte las instrucciones de la Guía de administración de NGINX Plus.
Instalación de HiveMQ
Se puede utilizar cualquier servidor MQTT, pero este entorno de pruebas se basa en HiveMQ (descargar aquí). En este ejemplo, instalamos HiveMQ en un único host utilizando contenedores Docker para cada nodo. Las siguientes instrucciones son una adaptación de Implementación de HiveMQ con Docker.
- Cree un Dockerfile para HiveMQ en el mismo directorio que hivemq.zip.
Cargando ejemplo de código…
- Desde el directorio que contiene hivemq.zip y el Dockerfile, cree la imagen de Docker.
$ docker build -t hivemq:latest .- Crea tres nodos de HiveMQ, cada uno expuesto en un puerto diferente.
$ docker run -p 18831:1883 -d --name node1 hivemq:latest
ff2c012c595a
$ docker run -p 18832:1883 -d --name node2 hivemq:latest
47992b1f4910
$ docker run -p 18833:1883 -d --name node3 hivemq:latest
17303b900b64- Compruebe que los tres nodos de HiveMQ estén en ejecución. (En el siguiente ejemplo de salida, se han omitido las columnas
COMMAND,CREATEDySTATUSpara facilitar la lectura).
$ docker psCONTAINER ID IMAGE ... PORTS NAMES
17303b900b64 hivemq:latest ... 0.0.0.0:18833->1883/tcp node3
47992b1f4910 hivemq:latest ... 0.0.0.0:18832->1883/tcp node2
ff2c012c595a hivemq:latest ... 0.0.0.0:18831->1883/tcp node1Instalación de Mosquitto
El cliente de línea de comandos de Mosquitto se puede descargar desde el sitio web del proyecto. Los usuarios de Mac que tengan Homebrew instalado pueden ejecutar el siguiente comando.
$ brew install mosquittoPruebe el cliente Mosquitto y la instalación de HiveMQ enviando un mensaje de publicación sencillo a una de las imágenes de Docker.
$ mosquitto_pub -d -h mqtt.example.com -t "topic/test" -m "test123" -i "thing001" -p 18831
Client thing001 sending CONNECT
Client thing001 received CONNACK
Client thing001 sending PUBLISH (d0, q0, r0, m1, 'topic/test', ... (7 bytes))
Client thing001 sending DISCONNECT[ngx_snippet name=’njs-enable-instructions’]
Sobre el autor

Artículos relacionados del blog
Únete a F5 NGINX en AppWorld 2024
Descubre las numerosas razones para asistir al evento insignia de F5, que incluirá un emocionante anuncio sobre la cartera de productos NGINX.
Rotación de certificados SSL/TLS sin reinicios en NGINX Open Source
La función de diccionario compartido permite configurar NGINX sin necesidad de reiniciarlo al renovar los certificados SSL/TLS.
Exposición de APIs en Kubernetes
La unidad 2 de Microservices March 2022 se centra en cómo exponer tus API en Kubernetes. Aquí tienes enlaces a los recursos que necesitas: la retransmisión en directo del 14 de marzo, artículos de blog y otros recursos de preparación, y el laboratorio práctico «Protege las API de Kubernetes mediante la limitación de solicitudes».
NGINX Sprint 2.0: visión clara, código renovado y nuevos compromisos con el código abierto
Hoy, en NGINX Sprint 2.0, anunciamos cómo respaldaremos nuestra visión del futuro de las aplicaciones modernas con una mayor presencia en la comunidad de código abierto, incluida la participación en el proyecto comunitario Kubernetes, nuevos proyectos de software de código abierto y la arquitectura de referencia para aplicaciones modernas.
Presentamos NGINX Plus R22
NGINX Plus R22 incorpora nuevas funcionalidades, como la compatibilidad con OCSP para verificar la revocación de certificados en TLS mutuo, el uso de varios proveedores de identidad OIDC en una configuración, el seguimiento en tiempo real de la limitación de solicitudes y conexiones en el panel de NGINX Plus y mejoras en NGINX JavaScript.
Presentamos NGINX Plus R17
NGINX Plus R17 incorpora compatibilidad con la limitación de velocidad en dos fases y con TLS 1.3, la versión más reciente del protocolo Transport Layer Security. La configuración de OpenID Connect es más sencilla y NGINX ModSecurity WAF es el doble de rápido que antes. También se ha actualizado el módulo JavaScript de NGINX.