Ferramentas de desenvolvedor do Node.js. Protocolo Mqtt para trabalhar com soquetes da Web

A tecnologia de soquete da Web permite enviar mensagens de um servidor para um cliente em um aplicativo da Web ou em um aplicativo móvel, o que não pode ser feito usando a API REST. Para trabalhar com soquetes da Web, eles costumam usar a biblioteca socket.io ou os desenvolvedores trabalham com objetos de soquete da Web do navegador nativo. Nesta postagem, tentarei mostrar que ambas as formas não resolvem todos os problemas, e é muito melhor usar servidores especializados, por exemplo, o servidor mqtt (anteriormente chamado de mqtt broker) para trabalhar com soquetes da web.

Para ser justo, e para evitar disputas desnecessárias, observo que, além do servidor mqtt, vários outros servidores podem ser usados, por exemplo, rabbitmq.

O desenvolvimento de aplicativos usando soquetes da Web parece muito simples até encontrarmos uma realidade na qual as quebras de conexão ocorrem com frequência. A primeira tarefa a ser resolvida é rastrear as quebras de conexão e restaurá-las. A situação é complicada pelo fato de que durante a desconexão e reconexão, o cliente continua a enviar novas mensagens, assim como novas mensagens podem ser enviadas ao cliente com maior probabilidade de serem perdidas.

É necessário, no nível do aplicativo, monitorar o recebimento de mensagens e implementar a entrega repetida. Gostaria especialmente de chamar a atenção para a frase "no nível do aplicativo" (mas gostaria que fosse no nível do protocolo).

Assim que adicionamos lógica para rastrear a entrega de mensagens, todas as mensagens começaram a chegar, mas ficou claro imediatamente que havia mensagens duplicadas, pois a mensagem poderia ter sido recebida e a confirmação desse fato foi perdida devido a uma conexão desconectada. E você precisa dobrar a quantidade de código do programa para excluir mensagens duplicadas.

Juntamente com a complicação do código do programa, sua eficiência também diminui, pela qual eles frequentemente criticam a biblioteca socket.io. Obviamente, é menos eficaz do que trabalhar com soquetes da Web nativos, em particular, devido à presença de lógica de reconexão e confirmação de entrega de mensagens (noto imediatamente que a lógica de re-entrega não é implementada no socket.io).

Uma maneira mais confiável e eficiente seria levar essa lógica ao nível do protocolo. E esse protocolo existe - é mqtt. A primeira versão do protocolo mqtt foi desenvolvida por Andy Stanford-Clark (IBM) e Arlene Nipper (Arcom) em 1999. A especificação MQTT 3.1.1 foi padronizada pelo consórcio OASIS em 2014.

O protocolo mqtt possui um parâmetro "qualidade de serviço" (qos) que pode receber valores:
0 - mensagem é entregue se possível;
1 - a mensagem é entregue garantida, mas pode haver duplicatas;
2 - a mensagem é entregue garantida e garantida uma vez.

Ou seja, o protocolo mqtt resolve o problema com entrega garantida de mensagens e esse problema é removido da agenda. Mas não é só essa questão.

Desempenho e dimensionamento.

Ao trabalhar com soquetes da Web, todos os clientes conectados deixam conexões abertas com o servidor, mesmo se não houver mensagens reais. Essa carga é inerentemente diferente da carga na API REST, que é determinada pelo fluxo de solicitações. O ônus das conexões abertas nos soquetes da Web é difícil de imitar durante a fase de teste. Portanto, muitas vezes é feita uma suposição errônea sobre o desempenho suficiente do aplicativo pelo número de mensagens enviadas e recebidas, sem levar em consideração o ônus de manter um grande número de conexões abertas com os clientes.

Se transferirmos todo o trabalho com soquetes da web para um servidor mqtt especializado, nosso aplicativo nodejs abrirá apenas uma conexão em um soquete da web (ou tcp, pois o mqtt suporta os dois protocolos) com um servidor mqtt, e podemos dimensione nosso aplicativo conectando várias instâncias de nodejs ao servidor mqtt.

Se os recursos de um servidor mqtt estiverem esgotados, você poderá organizar um cluster de servidores mqtt sem afetar os aplicativos no nodejs.

Agora vamos passar para um exemplo.

O servidor ou broker mqtt, como foi chamado nas especificações anteriores, funciona de acordo com o modelo de envio de mensagens / assinatura de mensagens. Cada mensagem é enviada ao tópico. O destinatário assina o encadeamento da mensagem. O remetente e o destinatário têm dois identificadores: clientId (identificador de dispositivo) e userName (nome de usuário).

O identificador do dispositivo é importante, pois está associado à assinatura e as mensagens serão enviadas a ele. O nome de usuário, diferentemente do identificador do dispositivo, não desempenha um papel decisivo na entrega da mensagem e é usado para diferenciar o acesso aos tópicos.

Para trabalhar com o protocolo mqtt no lado do cliente, a biblioteca github.com/eclipse/paho.mqtt.javascript é usada. Existem várias implementações de servidor, incluindo as gratuitas. Neste exemplo, usaremos o servidor emqx, que é executado no docker-compose (consulte github.com/apapacy/tut-mqtt ).

Para o teste, crie um documento no qual definiremos clientId, userName e texto da mensagem:

<script src="/paho-mqtt.js"></script> <script src="/messages.js"></script> <form name="sender" onsubmit="return false"> <input type="text" name="user"> <input type="text" name="client"> <input type="text" name="message"> <input type="button" onclick="connect()" value="connect"> <input type="button" onclick="send()" value="send"> </form> 


O envio de mensagens é implementado no arquivo message.js:

 var client; var connectOptions = { timeout: 30, reconnect: true, cleanSession: false, mqttVersion: 4, keepAliveInterval: 10, onSuccess: onConnect, onFailure: onFailure } function connect() { try { client = new Paho.Client('localhost', 8083, '/mqtt', document.forms.sender.client.value); connectOptions.userName = document.forms.sender.user.value; client.connect(connectOptions); } catch (ex) { console.log(ex); } } function onConnect() { console.log('on connect'); client.onMessageArrived = function(message) { console.log("onMessageArrived: " + message.payloadString); } client.subscribe("test", { qos: 2 }); } function onFailure(err) { console.log('on failure', JSON.stringify(err)); } function send() { var message = new Paho.Message(document.forms.sender.message.value); message.destinationName = "test"; message.qos = 2; client.send(message); } 


Para verificar, abra o arquivo index.html no navegador, especifique clientId, userName, texto da mensagem e envie várias mensagens (você pode lê-las no console, pois o cliente envia mensagens para o tópico de teste e é inscrito neste tópico).

Agora abra outro navegador ou outra guia do navegador e junte-se a outro (isso é importante) clientId. Envie mais algumas mensagens do primeiro navegador e verifique se elas chegam aos dois clientes, pois têm clientId diferente e ambas estão inscritas no teste de tópico.

Agora feche o segundo navegador (ou a segunda guia do navegador) e envie mais algumas mensagens. Depois disso, reabra o segundo navegador e junte-se ao mesmo clientId. Verifique se nos logs do console você recebeu todas as mensagens enviadas durante o período em que o segundo navegador (segunda guia) foi fechado. Isso aconteceu porque:

  • Ao enviar uma mensagem, o nível de qualidade qos = 2 foi definido;
  • Você ingressou anteriormente no mesmo tópico com o mesmo clientId, definindo qos = 2;
  • As opções de conexão estão definidas para cleanSession: false.

O código de amostra pode ser baixado do repositório .

apapacy@gmail.com
29 de setembro de 2019

Source: https://habr.com/ru/post/pt469315/


All Articles