Migration réussie vers une solution XMPP personnalisée

Publié: 2019-04-08

Je vais vous parler des défis auxquels nous avons été confrontés lors de la migration d'un chat tiers vers une solution de messagerie personnalisée basée sur XMPP pour notre client, Forward Health, une solution de messagerie médicale basée au Royaume-Uni. Cet article couvrira les raisons de la migration, nos attentes par rapport aux réalités de la mise en œuvre et le défi de créer des fonctionnalités supplémentaires.

Où nous avons commencé

Forward Health, notre client, souhaitait créer une application de communication mobile pour les professionnels de la santé au Royaume-Uni, comprenant une fonctionnalité de chat. En tant que startup, ils voulaient montrer rapidement leur produit fonctionnel. Dans le même temps, la messagerie devait être fiable, robuste et capable d'envoyer les données sensibles des patients en toute sécurité. Pour y parvenir, nous avons décidé d'utiliser l'une des solutions tierces disponibles pour la fonctionnalité de chat .

La fonctionnalité de chat n'est pas une chose triviale, surtout lorsqu'elle vise à soutenir le secteur de la santé. Au fur et à mesure que l'application grandissait, nous avons rencontré plus de cas marginaux et quelques bogues du côté de la bibliothèque sur lesquels le tiers n'était pas disposé à travailler. De plus, Forward Health souhaitait ajouter de nouvelles fonctionnalités qui n'étaient pas prises en charge par la bibliothèque tierce. Le passage à une solution personnalisée était la prochaine étape.

C'est alors que nous avons commencé à travailler avec MongooseIM . MIM est une solution open source basée sur le protocole XMPP bien établi. Nous avons travaillé avec une société externe Erlang Solutions Limited pour configurer notre backend et fournir une assistance pour la mise en œuvre de solutions personnalisées.

Tableau de bord MongooseIM

Au début, tout ce qui concernait la messagerie semblait différent. Auparavant, tous nos besoins étaient satisfaits par le SDK et son API REST. Maintenant, en utilisant MongooseIM, nous avons dû prendre du temps pour comprendre la nature de XMPP et implémenter notre propre SDK . Il s'est avéré que le serveur XMPP "bare bones" ne transmettait que des strophes (messages XML) entre les clients en temps réel. Les strophes peuvent être de différents types, c'est-à-dire des messages de chat normaux, la présence, des demandes et des réponses. Une grande variété de modules peuvent être ajoutés au serveur pour, par exemple, stocker des messages et permettre aux clients de les interroger.

Côté client (Android, iOS), il y avait des SDK de bas niveau. Malheureusement, ils n'agissaient que comme une couche permettant la communication avec MongooseIM et certains de ses modules enfichables appelés XEP (protocole d'extension XMPP responsable, entre autres, de l'envoi de notifications push pour chaque message). Toute l'architecture de gestion, de stockage et d'interrogation des messages devait être mise en œuvre par notre équipe.

Ce qui est venu à notre secours, c'est la bibliothèque tierce que nous avions utilisée auparavant. Il avait une API très bien pensée, nous avons donc fait fonctionner notre solution de la même manière. Nous avons séparé le code spécifique XMPP dans notre SDK interne avec l'interface correspondant à celle de la solution précédente. Cela n'a entraîné que quelques modifications dans notre code d'application après la migration.

Lors de la mise en place de MongooseIM, nous avons été surpris à plusieurs reprises par des éléments que nous pensions standards, mais qui ne nous étaient pas accessibles, même par XEP.

Mise en œuvre des fonctionnalités clés du chat basé sur XMPP

Horodatages

Vous pensez peut-être, comme nous l'avons fait, que les horodatages seraient aussi simples que "Je reçois un message, je l'affiche sur l'interface utilisateur avec un horodatage". Non, pas si facile. Par défaut, les strophes de message n'ont pas de champ d'horodatage. Heureusement pour notre équipe, XMPP est un protocole facilement extensible. Sur le backend, nous avons implémenté une fonctionnalité personnalisée, ajoutant un horodatage à chaque message passant par le serveur MongooseIM. Ensuite, le destinataire aurait l'horodatage attaché au message.

Pourquoi un expéditeur ne peut-il pas ajouter lui-même un horodatage ? Eh bien, nous ne savons pas s'ils ont réglé l'heure correcte sur leur téléphone.

Pourquoi n'y a-t-il pas de XEP pour ça ? Peut-être parce que XMPP est un protocole en temps réel, donc théoriquement, chaque message envoyé est reçu immédiatement.

EDIT : Comme l'a souligné Florian Schmaus : "Il y en a en fait un, bien qu'il puisse facilement être manqué à cause de son nom déroutant : XEP-0203 : Livraison retardée." Il ajoute un horodatage à un message uniquement si sa livraison est retardée. Sinon, le message vient d'être envoyé.

Messages hors ligne

Lorsque les deux utilisateurs sont connectés à l'application, ils peuvent s'envoyer des messages en temps réel. Mais que se passe-t-il si l'un d'entre eux est hors ligne ? La réponse rapide est : les messages doivent être mis en mémoire tampon sur le backend . La fonction de messages hors ligne gère ce travail et envoie toutes les strophes mises en mémoire tampon à l'utilisateur une fois qu'il se reconnecte.

Mais alors plusieurs questions se posent :

  • Combien de temps ces messages doivent-ils être mis en mémoire tampon ?
  • Combien d'entre eux?
  • Devraient-ils être renvoyés juste après la reconnexion ? Mais cela va inonder le client de messages, n'est-ce pas ?
  • Que se passe-t-il si un utilisateur se connecte uniquement, mais n'entre pas dans le chat avec les nouveaux messages. Seront-ils tous partis ?
  • Que se passe-t-il si un utilisateur est connecté sur plusieurs appareils ?

Il est devenu évident que la fonction de message hors ligne ne pouvait envoyer des messages qu'au premier appareil à revenir en ligne, et ces messages seraient alors perdus pour tous les autres appareils. Nous avons décidé de supprimer cette fonctionnalité et de stocker les messages sur le backend XMPP d'une manière différente et persistante.

Gestion des archives de messages (MAM)

MAM est un stockage sur serveur pour les messages. Lorsqu'un client est connecté, il peut interroger le serveur pour obtenir des messages. Vous pouvez interroger par pages, vous pouvez interroger par dates. C'est flexible - vous pouvez même interroger une page avant ou après un message avec un identifiant spécifique, en ajoutant des filtres pour les messages de la conversation exacte.

mais voici la prise. Les messages de chat normaux sont stockés dans des messages MAM, qui ont leurs propres identifiants uniques. Lorsqu'un utilisateur reçoit un message de chat dans un flux, il ne contient pas l'ID MAM. Ils doivent interroger le MAM pour l'obtenir.

La récupération à partir de MAM est une requête réseau, ce qui signifie qu'elle peut prendre un temps relativement long. Lorsqu'un utilisateur entre dans un chat, il veut voir les messages immédiatement. Nous avons donc également besoin d'une base de données locale .

Lorsqu'un utilisateur reçoit un message dans un flux (un message en ligne), nous l'enregistrons dans la base de données locale et le montrons à l'utilisateur. De cette façon, nous affichons des messages qui arrivent rapidement en temps réel à l'utilisateur.

De plus, chaque fois qu'ils entrent dans l'écran de discussion, nous téléchargeons tous les messages à partir de maintenant dans le message MAM le plus récent stocké dans la base de données locale pour cette conversation et les plaçons dans une base de données, en ignorant les doublons.

C'est ainsi que nous gérons le stockage des anciens messages. De plus, nous sommes sûrs que la base de données contient un ensemble complet de messages pour une conversation spécifique entre le premier et le dernier message de MAM.

Pour suivre les messages téléchargés depuis MAM, nous avons ajouté deux propriétés aux entités de conversation :

  1. ID MAM du message MAM le plus récent dans la base de données
  2. ID MAM du message MAM le plus ancien dans la base de données

La gestion d'ensembles brisés de messages MAM dans une base de données locale serait très problématique.

De plus, le fait d'avoir ces deux propriétés pour chaque conversation nous permet de stocker des messages de chat normaux dans la base de données tout en ignorant le wrapper - message MAM. Et lorsque l'utilisateur entre dans le chat, nous pouvons afficher les derniers messages de la base de données et récupérer en arrière-plan les messages manquants de MAM.

Boîte de réception

Chaque application basée sur le chat a besoin d'un écran avec une liste de chats, un endroit où vous pouvez voir les noms, les derniers messages et le nombre de messages non lus. Il doit y avoir une solution à ça !

En fait, il n'y a pas... Il y a quelque chose qui s'appelle Roster - il peut contenir une liste d'utilisateurs marqués comme "amis". Malheureusement, il n'y a pas de dernier message, ni de nombre de messages non lus qui leur sont attachés. Bien sûr, vous pouvez obtenir les informations nécessaires du backend en morceaux. Au début, nous voulions faire comme ça, mais cela fonctionnerait lentement et serait compliqué à faire. C'est alors que nous avons commencé à travailler avec Erlang Solutions sur la fonctionnalité Inbox, qui fait également son chemin vers l'open source.

Chat basé sur XMPP - écran de la boîte de réception
Écran de la boîte de réception - toutes les données de chat sont fournies par la fonction de boîte de réception

Lorsqu'un utilisateur se connecte au backend XMPP, l'application récupère sa boîte de réception, qui contient toutes les conversations de cet utilisateur - à la fois en tête-à-tête et en équipe. Chacun d'eux a le dernier message qui lui est attaché et un nombre de messages non lus. L'application enregistre toute la boîte de réception dans la base de données locale. Lorsqu'un utilisateur est dans l'application et qu'un nouveau message arrive, nous mettons à jour l'état de la boîte de réception localement. De cette façon, l'application n'a pas besoin de récupérer la boîte de réception pour chaque nouveau message.

Sommaire

Certaines solutions de chat tierces offrent un haut niveau d'abstraction. C'est ok si vous voulez créer une application de chat simple. En implémentant notre propre solution basée sur XMPP dans l'application Forward, nous avons pu obtenir un bien meilleur accès de bas niveau, ce qui a beaucoup facilité la résolution des problèmes. Bien sûr, cela a pris du temps, mais nous savons maintenant que nous pouvons fournir n'importe quelle fonctionnalité personnalisée pour aider les médecins du Royaume-Uni à communiquer de manière sécurisée et simple, approuvée par le NHS.

La messagerie est synonyme de communication haute performance en temps réel. En passant au MIM, nous avons pu optimiser chaque partie de la solution pour améliorer la vitesse, la fiabilité et, finalement, la confiance. Actuellement, nous avons le code entier, il est donc facile de les retrouver. De plus, nous sommes après la phase de stabilisation et un certain nombre de rapports liés à la messagerie ont drastiquement diminué. Les utilisateurs sont satisfaits de pouvoir faire confiance à la plateforme.

Concevoir et écrire notre propre SDK était une tâche difficile et nous l'avons aimée. C'était quelque chose de différent des applications simples où vous devez récupérer des données à partir d'un serveur et les afficher à l'écran. Lors de la mise en œuvre, nous avons compris de nombreux choix de conception de l'API de bibliothèque tierce que nous utilisions précédemment. Pourquoi? Car nous avons rencontré les mêmes problèmes.