Outils de développement Node.js. Protocole Mqtt pour travailler avec des sockets Web

La technologie de socket Web permet d'envoyer des messages d'un serveur à un client dans une application Web ou dans une application mobile, ce qui ne peut pas être fait à l'aide de l'API REST. Pour travailler avec des sockets Web, ils utilisent souvent la bibliothèque socket.io, ou les développeurs travaillent avec des objets de socket Web de navigateur natif. Dans cet article, j'essaierai de montrer que les deux façons ne résolvent pas tous les problèmes, et il est préférable d'utiliser des serveurs spécialisés, par exemple, le serveur mqtt (il s'appelait auparavant le courtier mqtt) pour travailler avec les sockets Web.

En toute équité, et pour éviter les litiges inutiles, je note qu'en plus du serveur mqtt, un certain nombre d'autres serveurs peuvent être utilisés, par exemple rabbitmq.

Le développement d'applications à l'aide de sockets Web semble très simple jusqu'à ce que nous rencontrions une réalité dans laquelle des coupures de connexion se produisent souvent. La première tâche à résoudre consiste à suivre les ruptures de connexion et à les restaurer. La situation est compliquée par le fait que pendant la déconnexion et la reconnexion, le client continue d'envoyer de nouveaux messages, ainsi que de nouveaux messages peuvent être envoyés au client qui sont plus susceptibles d'être perdus.

Il est nécessaire au niveau de l'application de surveiller la réception des messages et de mettre en œuvre leur remise répétée. Je voudrais surtout attirer l'attention sur l'expression «au niveau de l'application» (mais j'aimerais qu'elle soit au niveau du protocole).

Dès que nous avons ajouté une logique pour suivre la livraison des messages, tous les messages ont commencé à arriver, mais il est immédiatement devenu évident qu'il y avait des messages en double, car le message aurait pu être reçu, et la confirmation de ce fait a été perdue en raison d'une connexion déconnectée. Et vous devez doubler la quantité de code de programme pour exclure les messages en double.

Parallèlement à la complication du code du programme, son efficacité diminue également, pour laquelle ils critiquent souvent la bibliothèque socket.io. Bien sûr, il est moins efficace que de travailler avec des sockets Web natifs, en particulier, en raison de la présence d'une logique de reconnexion et d'une confirmation de remise de message (je remarque immédiatement que la logique de re-remise n'est pas implémentée dans socket.io).

Un moyen plus fiable et efficace serait d'amener cette logique au niveau du protocole. Et un tel protocole existe - c'est mqtt. La première version du protocole mqtt a été développée par Andy Stanford-Clark (IBM) et Arlene Nipper (Arcom) en 1999. La spécification MQTT 3.1.1 a été normalisée par le consortium OASIS en 2014.

Le protocole mqtt a un paramètre de «qualité de service» (qos) qui peut prendre des valeurs:
0 - le message est délivré si possible;
1 - le message est livré garanti, mais il peut y avoir des doublons;
2 - le message est livré garanti et garanti une fois.

Autrement dit, le protocole mqtt résout le problème de la remise garantie des messages, et ce problème est supprimé de l'agenda. Mais pas seulement cette question.

Performance et évolutivité.

Lorsque vous travaillez avec des sockets Web, tous les clients connectés laissent des connexions ouvertes au serveur, même s'il n'y a pas de véritable messagerie. Cette charge est intrinsèquement différente de la charge dans l'API REST, qui est déterminée par le flux de demandes. La charge des connexions ouvertes sur les sockets Web est difficile à émuler pendant la phase de test. Par conséquent, une hypothèse erronée est souvent émise concernant les performances suffisantes de l'application par le nombre de messages envoyés et reçus, sans tenir compte de la charge de maintenir un grand nombre de connexions ouvertes avec les clients.

Si nous transférons tous les travaux avec des sockets web vers un serveur mqtt spécialisé, notre application nodejs ouvre une seule connexion sur une socket web (ou tcp, car mqtt prend en charge les deux protocoles) avec un serveur mqtt, et nous pouvons faire évoluer notre application en connectant plusieurs instances de nodejs au serveur mqtt.

Si les ressources d'un serveur mqtt sont épuisées, vous pouvez organiser un cluster de serveurs mqtt sans affecter les applications sur nodejs.

Passons maintenant à un exemple.

Le serveur ou courtier mqtt, comme on l'appelait dans les spécifications précédentes, fonctionne selon le modèle d'envoi de messages / d'abonnement aux messages. Chaque message est envoyé au sujet. Le destinataire s'abonne au fil de messages. L'expéditeur et le destinataire ont tous deux deux identifiants: clientId (identifiant de périphérique) et userName (nom d'utilisateur).

L'identifiant de l'appareil est important, car il est associé à l'abonnement et des messages lui seront envoyés. Le nom d'utilisateur, contrairement à l'identifiant de l'appareil, ne joue pas un rôle décisif dans la remise des messages et est utilisé pour différencier l'accès aux rubriques.

Pour travailler avec le protocole mqtt côté client, la bibliothèque github.com/eclipse/paho.mqtt.javascript est utilisée. Il existe plusieurs implémentations de serveur, y compris gratuites. Dans cet exemple, nous utiliserons le serveur emqx, qui s'exécute via docker-compose (voir github.com/apapacy/tut-mqtt ).

Pour les tests, créez un document dans lequel nous définirons clientId, userName et le texte du message:

<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> 


L'envoi de messages est implémenté dans le fichier 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); } 


Pour vérifier, ouvrez le fichier index.html dans le navigateur, définissez clientId, userName, texte du message et envoyez plusieurs messages (ils peuvent être lus dans la console, car le client envoie des messages à la rubrique de test et est abonné à cette rubrique elle-même).

Ouvrez maintenant un autre navigateur ou un autre onglet de navigateur et joignez-vous à un autre (c'est important) clientId. Envoyez quelques messages supplémentaires à partir du premier navigateur et assurez-vous qu'ils parviennent aux deux clients, car ils ont différents ID client et ils sont tous les deux abonnés au test de rubrique.

Fermez maintenant le deuxième navigateur (ou le deuxième onglet du navigateur) et envoyez quelques messages supplémentaires. Après cela, rouvrez le deuxième navigateur et rejoignez le même clientId. Assurez-vous que dans les journaux de la console, vous avez reçu tous les messages qui ont été envoyés pendant la période de fermeture du deuxième navigateur (deuxième onglet). Cela est arrivé parce que:

  • Lors de l'envoi d'un message, le niveau de qualité qos = 2 a été défini;
  • Vous avez précédemment rejoint le même sujet avec le même clientId en définissant qos = 2;
  • Les options de connexion sont définies sur cleanSession: false.

Un exemple de code peut être téléchargé à partir du référentiel .

apapacy@gmail.com
29 septembre 2019

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


All Articles