Cuando se trata de los sitios web más concurridos de Internet, NGINX y NGINX Plus dominan el mercado. De hecho, NGINX impulsa más de un millón de sitios con mucho tráfico en el mundo que cualquier otro servidor web. Su capacidad para gestionar más de un millón de conexiones simultáneas en un solo servidor ha impulsado su adopción por sitios y aplicaciones de “hiperescala” como Airbnb, Netflix y Uber.
Aunque NGINX Plus es más comúnmente conocido como servidor web, proxy inverso HTTP y equilibrador de carga, también es un controlador de entrega de aplicação (ADC) con todas las funciones y soporte para aplicações TCP y UDP. Su arquitectura basada en eventos y todos los demás atributos que la han hecho exitosa para los casos de uso de HTTP son igualmente aplicables a la 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 campos petrolíferos remotos. Se actualizó para casos de uso de IoT en 2013 y desde entonces se ha convertido en el protocolo elegido para muchas implementaciones de IoT. Las implementaciones de IoT de producción con millones de dispositivos exigen alto rendimiento y funcionalidad avanzada de un balanceador de carga, y en esta serie de dos partes de publicaciones de blog analizaremos los siguientes casos de uso avanzados.
Para explorar las características de NGINX Plus, utilizaremos un entorno de prueba simple que representa los componentes clave de un entorno de IoT con un clúster de brokers MQTT. Los agentes MQTT en este entorno son instancias de HiveMQ que se ejecutan dentro de contenedores Docker.
NGINX Plus actúa como un proxy inverso y balanceador de carga para el bróker MQTT, escuchando en el puerto MQTT predeterminado de 1883. Esto proporciona una interfaz simple y consistente para el cliente, mientras que los nodos MQTT del backend pueden escalarse (e incluso desconectarse) sin afectar al cliente de ninguna manera. Utilizamos la herramienta de línea de comandos Mosquitto como cliente, que representa los dispositivos IoT en el entorno de prueba.
Todos los casos de uso de esta publicación y la Parte 2 utilizan este entorno de prueba, 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 prueba, consulte el Apéndice 1 .
Una función principal de un balanceador de carga es proporcionar alta disponibilidad para la aplicação , de modo que se puedan agregar, quitar o desconectar servidores back-end sin afectar al cliente. Para hacer esto de manera confiable es necesario realizar controles de estado que prueben de manera proactiva la disponibilidad de cada uno de los servidores back-end. Con controles de estado activos, NGINX Plus puede eliminar servidores fallidos del grupo de equilibrio de carga antes de que las solicitudes reales de los clientes lleguen a ellos.
La utilidad de una comprobación de estado depende de la precisión con la que simula el tráfico real de la aplicação y analiza la respuesta. Las comprobaciones simples de actividad del servidor, como un ping, no garantizan que el servicio backend esté funcionando. Las comprobaciones de apertura del puerto TCP no garantizan que la aplicação en sí se encuentre en buen estado. Aquí configuramos el equilibrio de carga básico para el entorno de prueba con una verificación de estado que garantiza que cada servidor backend pueda aceptar nuevas conexiones MQTT.
Estamos realizando cambios en dos archivos de configuración.
En el archivo principal nginx.conf , incluimos el siguiente bloque de flujo
y la
directiva para que NGINX Plus lea la configuración para el equilibrio de carga TCP desde uno o más archivos en el subdirectorio stream_conf.d , que está en el mismo directorio que nginx.conf . Hacemos esto en lugar de incluir la configuración real en nginx.conf .
Luego, en el mismo directorio que nginx.conf creamos el directorio stream_conf.d para contener nuestros archivos de configuración TCP y UDP. Tenga en cuenta que no utilizamos el directorio conf.d preexistente porque de manera predeterminada está reservado para el contexto de configuración http
y, por lo tanto, agregar allí la configuración de transmisión
fallará.
En stream_mqtt_healthcheck.conf primero definimos el formato del registro de acceso para el tráfico MQTT (líneas 1-2). Esto es deliberadamente similar al formato de registro común HTTP para que los registros resultantes se puedan importar a herramientas de análisis de registros.
A continuación definimos el grupo ascendente
llamado hive_mq (líneas 4 a 9) que contiene tres servidores MQTT. En nuestro entorno de prueba, cada uno de ellos es accesible en localhost con un número de puerto único. La directiva de zona
define una cantidad de memoria que se comparte entre todos los procesos de trabajo de NGINX Plus para mantener el estado de equilibrio de carga y la información de salud.
El bloque de coincidencia
(líneas 11 a 15) define la comprobación de estado utilizada para probar la disponibilidad de los servidores MQTT. La directiva de envío
es una representación hexadecimal de un paquete MQTT CONNECT
completo con un identificador de cliente (ClientId) de nginx
health
check
. Esto se envía a cada uno de los servidores definidos en el grupo ascendente cada vez que se activa la verificación de estado. La directiva expect
correspondiente describe la respuesta que el servidor debe devolver para que NGINX Plus lo considere saludable. Aquí, la cadena hexadecimal de 4 bytes20
02
00
00
es un paquete CONNACK
MQTT completo. La recepción de este paquete demuestra que el servidor MQTT es capaz de recibir nuevas conexiones de cliente.
El bloque de servidor
(líneas 17 a 25) configura cómo NGINX Plus trata con los clientes. NGINX Plus escucha en el puerto MQTT predeterminado, 1883, y reenvía todo el tráfico al grupo ascendente hive_mq (línea 19). La directiva health_check
especifica que se realizan verificaciones de estado contra el grupo ascendente (con la frecuencia predeterminada de cinco segundos) y que se utiliza la verificación definida por el bloque de coincidencia
mqtt_conn .
Para probar que esta configuración básica funciona, podemos usar el cliente Mosquitto para publicar algunos datos de prueba en nuestro entorno de prueba.
$ mosquitto_pub -d -h mqtt.example.com -t "topic/test" -m "test123" -i "thing001" Cliente thing001 enviando CONNECT Cliente thing001 recibió CONNACK Cliente thing001 enviando PUBLISH (d0, q0, r0, m1, 'topic/test', ... (7 bytes)) Cliente thing001 enviando DESCONECTAR $ 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:18831
La línea del registro de acceso muestra que NGINX Plus recibió un total de 23 bytes y se enviaron 4 bytes al cliente (el paquete CONNACK
). También podemos ver que se eligió el nodo MQTT 1 (puerto 18831). Como lo muestran las siguientes líneas del registro de acceso, cuando repetimos la prueba, el algoritmo de equilibrio de carga Round Robin predeterminado selecciona node1 , node2 y node3 por turno.
$ tail --lines=4 /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: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:18831
[ Editor : el siguiente caso de uso es solo uno de muchos para el módulo JavaScript de NGINX. Para obtener una lista completa, consulte Casos de uso del módulo JavaScript NGINX .]
El código de esta sección se actualiza de la siguiente manera para reflejar los cambios en la implementación de JavaScript de NGINX desde que se publicó originalmente el blog:
s
) para el módulo Stream, que se introdujo en NGINX JavaScript 0.2.4 .js_import
, que reemplaza la directiva js_include
en NGINX Plus R23 y posteriores. Para obtener más información, consulte la documentación de referencia del módulo JavaScript de NGINX : la sección Configuración de ejemplo muestra la sintaxis correcta para la configuración de NGINX y los archivos JavaScript.El equilibrio de carga Round Robin es un mecanismo eficaz para distribuir las conexiones de clientes entre un grupo de servidores. Sin embargo, existen varias razones por las que no es ideal para conexiones MQTT.
Los servidores MQTT a menudo esperan una conexión de larga duración entre el cliente y el servidor y se puede generar una gran cantidad de estado de sesión en el servidor. Desafortunadamente, la naturaleza de los dispositivos IoT y las redes IP que utilizan implican que las conexiones se interrumpan, lo que obliga a algunos clientes a reconectarse con frecuencia. NGINX Plus puede usar su algoritmo de equilibrio de carga Hash para seleccionar un servidor MQTT en función de la dirección IP del cliente. Simplemente agregando el hash
$remote_addr;
al bloque ascendente se habilita la persistencia de la sesión de manera que cada vez que una nueva conexión ingresa desde una dirección IP de cliente determinada, se selecciona el mismo servidor MQTT.
Pero no podemos confiar en que los dispositivos IoT se reconecten desde la misma dirección IP, especialmente si utilizan redes celulares (por ejemplo, GSM o LTE). Para garantizar que el mismo cliente se vuelva a conectar al mismo servidor MQTT, debemos utilizar el identificador del cliente MQTT como clave para el algoritmo hash.
El ClientId MQTT es un elemento obligatorio del paquete CONNECT
inicial, lo que significa que está disponible para NGINX Plus antes de que el paquete sea enviado al servidor ascendente. Podemos usar JavaScript NGINX para analizar el paquete CONNECT
y extraer el ClientId como una variable que luego puede ser utilizada por 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 de JavaScript única para NGINX y NGINX Plus, diseñada específicamente para casos de uso del lado del servidor y procesamiento por solicitud. Tiene tres características clave que lo hacen adecuado para una implementación de persistencia de sesión para MQTT:
CONNECT
requiere menos de 20 líneas de código.Para obtener instrucciones sobre cómo habilitar JavaScript en NGINX, consulte el Apéndice 2 .
La configuración de NGINX Plus para este caso de uso sigue siendo relativamente simple. La siguiente configuración es una versión modificada del ejemplo en Equilibrio de carga con controles de estado activos , con los controles de estado eliminados para mayor brevedad.
Comenzamos especificando la ubicación del código JavaScript de NGINX con la directiva js_import
. La directiva js_set
le dice a NGINX Plus que llame a la función setClientId
cuando necesite evaluar la variable $mqtt_client_id
. Agregamos más detalles al registro de acceso agregando esta variable al formato de registro mqtt en la línea 5.
Habilitamos la persistencia de la sesión en la línea 12 con la directiva hash
especificando $mqtt_client_id
como clave. Tenga en cuenta que utilizamos el parámetro consistente
para que, si un servidor ascendente falla, su parte del tráfico se distribuya de manera uniforme entre los servidores restantes sin afectar las sesiones que ya están establecidas en esos servidores. El hash consistente se analiza con más detalle en nuestra publicación de blog sobre la fragmentación de un caché web : los principios y beneficios se aplican por igual aquí.
La directiva js_preread
(línea 18) especifica la función JavaScript NGINX que se ejecuta en la fase de prelectura del procesamiento de la solicitud. La fase de prelectura se activa para cada paquete (en ambas direcciones) y ocurre antes de la transmisión por proxy para que el valor de $mqtt_client_id
esté disponible cuando se necesita en el bloque ascendente
.
Definimos el JavaScript para extraer el MQTT ClientId 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 ).
La función principal, getClientId()
, se declara en la línea 4. Se pasa el objeto llamado 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 a 9 garantizan que el paquete actual sea el primero que se reciba del cliente. Los mensajes posteriores del cliente y las respuestas del servidor se ignoran de modo que, una vez que se establece una conexión, no hay sobrecarga adicional en el flujo de tráfico.
Las líneas 10 a 24 examinan el encabezado MQTT para garantizar que el paquete sea de tipo CONNECT
y para determinar dónde comienza la carga útil MQTT.
Las líneas 27 a 32 extraen el ClientId de la carga útil y almacenan el valor en la variable global de JavaScript client_id_str
. Luego, esta variable se exporta a la configuración de NGINX con la función setClientId
(líneas 43 a 45).
Ahora podemos usar nuevamente el cliente Mosquitto para probar 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 "tema/prueba" -m "prueba123" -i "foo" $ mosquitto_pub -h mqtt.example.com -t "tema/prueba" -m "prueba123" -i "bar" $ mosquitto_pub -h mqtt.example.com -t "tema/prueba" -m "prueba123" -i "baz" $ mosquitto_pub -h mqtt.example.com -t "tema/prueba" -m "prueba123" -i "foo" $ mosquitto_pub -h mqtt.example.com -t "tema/prueba" -m "prueba123" -i "foo" $ mosquitto_pub -h mqtt.example.com -t "tema/prueba" -m "prueba123" -i "foo" $ mosquitto_pub -h mqtt.example.com -t "tema/prueba" -m "prueba123" -i "bar" $ mosquitto_pub -h mqtt.example.com -t "tema/prueba" -m "prueba123" -i "bar" $ mosquitto_pub -h mqtt.example.com -t "tema/prueba" -m "prueba123" -i "baz" $ mosquitto_pub -h mqtt.example.com -t "tema/prueba" -m "prueba123" -i "baz"
Al examinar el registro de acceso se muestra que ClientId foo siempre se conecta al nodo1 (puerto 18831), ClientId bar siempre se conecta al nodo2 (puerto 18832) y ClientId baz siempre se conecta al nodo3 (puerto 18833).
$ tail /var/log/nginx/mqtt_access.log 192.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 barra 192.168.91.1 [23/Mar/2017:12:24:44 +0000] TCP 200 23 4 127.0.0.1:18832 barra 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 baz
Tenga en cuenta que también tenemos el beneficio de que MQTT ClientId aparece en las líneas del registro de acceso independientemente de si usamos persistencia de sesión o cualquier otro algoritmo de equilibrio de carga.
En esta primera publicación de una serie de dos partes, demostramos cómo NGINX Plus usa controles de estado activos para mejorar la disponibilidad y confiabilidad de las aplicações de IoT, y cómo NGINX JavaScript puede extender NGINX Plus al proporcionar una capacidad de equilibrio de carga de capa 7 como persistencia de sesión para tráfico TCP. En la segunda parte , exploramos cómo NGINX Plus puede hacer que sus aplicações de IoT sean más seguras al descargar la terminación TLS y la autenticación de los clientes.
En combinación con NGINX JavaScript o por sí solo, el alto rendimiento y la eficiencia inherentes de NGINX Plus lo hacen ideal como equilibrador de carga de software para su infraestructura de IoT.
Para probar NGINX JavaScript con NGINX Plus, comience su prueba gratuita de 30 días o contáctenos para analizar sus casos de uso .
Apéndices
Instalamos el entorno de prueba en una máquina virtual para que esté aislado y sea repetible. Sin embargo, no hay ninguna razón por la cual no puedas instalarlo en un servidor físico.
Consulte las instrucciones en la Guía de administración de NGINX Plus .
Se puede utilizar cualquier servidor MQTT, pero este entorno de prueba se basa en HiveMQ ( descárgalo aquí ). En este ejemplo instalamos HiveMQ en un solo host usando contenedores Docker para cada nodo. Las siguientes instrucciones están adaptadas de Implementación de HiveMQ con Docker .
Cree un Dockerfile para HiveMQ en el mismo directorio que hivemq.zip .
Trabajando en el directorio que contiene hivemq.zip y el Dockerfile, cree la imagen de Docker.
$ docker build -t hivemq:latest .
Cree tres nodos HiveMQ, cada uno expuesto en un puerto diferente.
$ docker run -p 18831:1883 -d --name nodo1 hivemq:latest ff2c012c595a $ docker run -p 18832:1883 -d --name nodo2 hivemq:latest 47992b1f4910 $ docker run -p 18833:1883 -d --name nodo3 hivemq:latest 17303b900b64
Compruebe que los tres nodos HiveMQ estén en ejecución. (En el siguiente ejemplo de salida, se han omitido las columnas COMMAND
, CREATED
y STATUS
para facilitar la lectura).
$ docker ps ID DEL CONTENEDOR IMAGEN ... NOMBRES DE PUERTOS 17303b900b64 hivemq:latest ... 0.0.0.0:18833->1883/tcp nodo3 47992b1f4910 hivemq:último ... 0.0.0.0:18832->1883/tcp nodo2 ff2c012c595a hivemq:último ... 0.0.0.0:18831->1883/tcp nodo1
El cliente de línea de comandos Mosquitto se puede descargar desde el sitio web del proyecto . Los usuarios de Mac con Homebrew instalado pueden ejecutar el siguiente comando.
$ brew install mosquitto
Pruebe el cliente Mosquitto y la instalación de HiveMQ enviando un mensaje de publicación simple a una de las imágenes de Docker.
$ mosquitto_pub -d -h mqtt.example.com -t "topic/test" -m "test123" -i "thing001" -p 18831 Cliente thing001 enviando CONNECT Cliente thing001 recibió CONNACK Cliente thing001 enviando PUBLISH (d0, q0, r0, m1, 'topic/test', ... (7 bytes)) Cliente thing001 enviando DESCONEXIÓN
[ngx_snippet name=’njs-enable-instructions’]
"Esta publicación de blog puede hacer referencia a productos que ya no están disponibles o que ya no reciben soporte. Para obtener la información más actualizada sobre los productos y soluciones F5 NGINX disponibles, explore nuestra familia de productos NGINX . NGINX ahora es parte de F5. Todos los enlaces anteriores de NGINX.com redirigirán a contenido similar de NGINX en F5.com.