Développement d’applications web et mobiles qui consomment des données en temps réel

Voici le cinquième d’une série d’articles consacrée au développement d’applications Web et Mobiles qui utilisent des données en temps réel. Cette série a débuté sur le blog de Julien Corioland, avec qui j’ai eu le plaisir d’animer une session sur ce sujet lors du dernier AzureCamp (mardi 24 Juin 2014).

Vous trouverez ces articles publiés plus ou moins alternativement sur nos blogs respectifs :

· Développement d’applications web et mobiles qui consomment des données en temps réel

· Utilisation de SignalR pour implémenter un service Backend Cloud dans Azure

· Consommer un hub SignalR depuis une application web cliente

· Consommer un hub SignalR depuis une application native (Windows / Windows Phone)

· Utilisation de Socket.IO et Node.JS pour implémenter un service Backend Cloud

· Consommation d’un service socket.io depuis une application web cliente

· Implémenter une infrastructure IOT (AMQP, Service Bus,…)

Avant de rentrer dans les details d’implémentation d’un serveur socket.io, parlons un peu de l’environnement d’exécution, node.js.

Node.js

Node.js est framework Open Source événementiel permettant de développer des applications réseau en JavaScript. Ce framework permet de construire des services en ligne scalables en utilisant le moteur JavaScript V8 de Google et un wrapper C++ optimisé pour gérer les I/O. Tous les requêtes y sont asynchrones et s’exécutent sur un seul thread, la gestion de la concurrence étant directement assurée par le système d’exploitation.

Nodejs et Microsoft

L’installation sur une machine Windows peut se faire de façon interactive (en téléchargeant le MSI depuis le site https://nodejs.org ou via Microsoft WebPI. Elle peut également être réalisée en mode silencieux depuis un réfétentiel OneGet (composante de WMF 5 preview).

image

Pour aller au-delà de la simple exéction d’un process node dans Windows, Microsoft a implémenté un module Open Source permettant d’exécuter les applications node.js dans IIS, afin de simplifier la gestion des processus, d’augmenter la scalabilité sur des serveurs multi-cœurs (un process node par cœur), de bénéficier du système d’accès aux logs,…

image

Sur la base de ce module, Microsoft propose de multiples modes d’hébergement de node.js sur la plateforme Azure (installation sur une VM, déploiement sur un Cloud Service Paas, sur un Azure Web Site, ou utilisation pour implémenter le backend d’une application mobile avec les Azure Mobiles Services).

La gestion des versions d’une application node.js ans un Cloud Service PaaS a déjà fait l’objet d’un article sur mon blog :

https://blogs.msdn.com/b/stephgou/archive/2014/01/10/gestion-des-montees-de-version-nodejs-dans-windows-azure.aspx

Il est également très rapide de déployer une application node.js dans un Windows Azure Web Site, qui en plus de l’exécution au sein d’un iisnode, propose nativement un mécanisme de sélection de version de node.js.

image

Modules Node.js

Node.js propose une approche de développement très modulaire avec un référentiel (https://npm.org) très régulièrement mis à jour par la communauté Open Source et offrant de très nombreux modules tels que :image

• "fs" : système de fichier

• "net" : réseau, TCP

• "crypto" : cryptographie

• "http" : serveur web

• …

Il suffit alors de combiner l’usage de ces modules.

 

Par exemple, l’écriture d’un serveur Web qui affiche le message « HelloWorld » se résume à :

var http = require('http');

http.createServer(function (req, res) {

    res.writeHead(200, { 'Content-Type': 'text/plain' });

    res.end('Hello World\n');

}).listen(1337, "127.0.0.1");

                            

Modèle de développement symétrique

L’un des intérêts du framework node.js est de proposer un modèle de développement symétrique (même langage) entre le client et le serveur.

image

 

Socket.io

Le module Socket.io

Socket.io est un module qui a été initialement implémenté pour node.js, peu après que ce framework ait fait sa première apparition.

De même que SignalR, Socket.io est une librairie Open Source offrant des fonctions “temps reel” pour les applications web. La première version de Socket.io coïncidait avec les débuts de la spécification de l’API et du protocole WebSocket, qui à l’époque était loin d’être supportés par l’ensemble des browsers. Le développement d’'applications web intégrant de la communication “temps reel” pouvait donc s’avérer plutôt contraignant en fonction des langages et environnements de programmation.

L’approche retenue pour Socket.io a donc été de proposer un modèle de développement offrant une interface équivalente à l'API WebSocket qui était encore en cours de normalisation.

Comme pour SignalR, l’un des atouts de cette librairie était et de permettre de faire abstraction du browser en offrant le service de connectivité même dans le cas où les WebSockets ne seraient pas supportées. Le principe était d’exploiter FlashSocket, AJAX long polling et quelques autres techniques permettant la communication bidirectionnelle du client au serveur.

D’autres implémentations de Socket.io ont par la suite été proposées pour d’autres langages de programmation. Dans cet article, nous nous limiterons volontairement au cas de node.js.

Socket.io version 1.0

Une version 1.0 de Socket.io a récemment fait son apparition. Elle propose de nouvelles fonctions comme l'envoi de données binaires ou une approche simplifiée pour offrir une solution scalable distribuée sur plusieurs nœuds.

Avec cette version, la gestion des protocoles de transport et des incompatibilités de navigateur est déléguée au nouveau module Engine.io. Cela apporte beaucoup de flexibilité. Si WebSocket est le seul transport ciblé, Engine.io peut être retiré en toute transparence (inutile alors de s’encombrer de toutes les solutions de contournement liées à la multiplicité des browsers) . De même, il est possible d’utiliser une autre couche de transport, comme le module Node.js TCP Sockets.

En outre, Socket.io 1.0 part du principe que la communication n’utilisera pas nécessairement les WebSockets et établit donc au préalable une connexion avec XHR (XMLHttpRequest) ou JSONP (JSON with Padding) avant d’essayer de mettre à jour la connexion de WebSocket (ce qui diffère de la méthode « fallback » précédemment utilisée, qui, elle, se fonde sur les délais d'attente, et donc potentiellement sur une expérience dégradée de l’utilisateur).

Implémentation d’un serveur de traitement de requêtes

Le développement d’un serveur node js est fondé sur l’utilisation des modules http et socketio. La première étape consiste à importer ces modules, avant de créer le serveur http et de démarrer l’écoute des requêtes socket.io.

var port = process.env.port || 888;

var http = require('http').createServer(handler),

io = require('socket.io').listen(http);

http.listen(port);

Ces modules doivent être présents dans l’application. Leur téléchargement depuis le référentiel npm.org est assurée par la déclaration de dépendances dans le fichier package.json.

{

"name": "NodejsHub",

"version": "0.0.0",

"description": "NodejsHub",

"main": "server.js",

"author": {

"name": "stephgou",

"email": ""

},

"dependencies": {

"socket.io": "~1.0.6"

}

}

Socket.io permet de déclarer un (ou plusieurs) espace(s) de nommage, ce qui a pour effet d’assigner un point de terminaison spécifique pour les appels sur le socket. Il s’agit d’une spécificité de l’implémentation de Socket.io. Cela n’a pas d’incidence sur l’url exposée par le transport sous-jacent. Cette fonctionnalité est fort utile pour minimiser le nombre de ressources (connexions TCP) et isoler les canaux de communication dédiés à une application.

var namespace = '/iot-node-hub';

var iotNodeHub = io

.of(namespace)

Chaque espace de noms émet un événement de connexion qui reçoit chaque instance de socket en tant que paramètre.

.on('connection', function (socket) {

Socket.io permet une communication fondée sur n'importe quel type de structure de données que l’on peut nommer et déclarer dynamiquement dans le code (serveur ou client).

var initMessage = { type: 'init', message: id };

L’envoi du message est réalisé sur un socket identifié par son nom (par exemple : « msgtocli »)

socket.emit('msgtocli', initMessage);

Le traitement des requêtes se fait sur la réception d’un message, en écoutant sur le socket (par exemple : « msgtosrv»)

socket.on('msgtosrv', function (data) {

processReceivedData(socket,data);

});

Certains noms sont réservés pour la gestion d’évènements liés au protocole d’échange

socket.on('disconnect', function () {

console.log("disconnect");

});

L’émission ou le relais de message peut se faire directement sur la connexion de l’appelant (par l’utilisation de la méthode « emit ») ou sur l’ensemble des connexions en cours (via un « broadcast »).

if (data.type === 'meteo') {

var meteoMessage = { type: 'meteo', message: data.message };

socket.emit('msgtocli', meteoMessage);

socket.broadcast.emit('msgtocli', meteoMessage);

}

En plus d’offrir les fonctions pour bâtir un serveur de traitement de requêtes, Socket.io publie le code pour l’implémentation côté client. Il suffit d'inclure l’extrait de code suivant :

<script src="/socket.io/socket.io.js"></script>

dans le code javascript appelant. Nous étudierons plus en détail la consommation d’un service socket.io depuis le browser dans le prochain article.

Scalabilité

Les clients qui utilisent WebSockets peuvent faire usage d’un canal de communication bidirectionnel dans lequel l’écriture d’un message est immédiate. Mais dans le cas des connexions de type long polling, il est nécessaire de continuer à relayer les requêtes vers le même nœud, plus exactement vers le même process, car il probable qu’il faudra faire usage d’une mémoire tampon pour conserver les messages dans leur état intermédiaire pendant toute la durée du processus d’échange.

La distribution d’une application node.js sur de multiples nœuds requiert donc la mise en œuvre d’une affinité de session sur le load balancing (par exemple en fonction de l'adresse IP d'origine). Dans Azure, ce type d’affinité est nativement offert lorsque la solution est déployée sur une ferme d’Azure WebSites. Lorsque la cible est différente (par exemple, Cloud Service PaaS), il existe plusieurs méthodes pour mettre en place un système la garantissant, en s’appuyant sur une configuration système avec un mécanisme comme IIS Application Request Routing (ARR), par une configuration déclarative de la liste des nœuds d’un cluster NginX.

Avec la version 1.0, la scalabilité d’une application node.js sur de multiples nœuds est assurée par un relai des événements entre les process (dans les précédentes versions, elle était liée à la réplication des donnés entre les nœuds). Le routage des messages est assuré au niveau d’une interface baptisée « Adapter », dont une implémentation est nativement proposée dans le module « socket.io-redis ».

Sécurité

Le protocole SSL peut être utilisé pour sécuriser le transport des données entre un client et un serveur socket.io.

Socket.io propose un mécanisme natif de gestion des autorisations d’accès : le principe est de ne pas appeler la callback si l’autorisation échoue.

var key = 'MyKey';

io.set('authorization', function (req, callback) {

if (req.query.token === undefined || req.query.token.length === 0) {

return false;

}

var validated = false;

if (key == req.query.token) {

validated = true;

break;

}

if (validated) {

return callback(null, true);

}

else {

return false;

}

});

Dans notre contexte, ne s’agissant pas d’informations sensibles, le Hub reste accessible à des utilisateurs anonymes.

Côté serveur, des stratégies de sécurité doivent permettre des requêtes avec une longue durée de vie, d’autres ports doivent être ouverts, et le trafic doit être géré afin de permettre l’utilisation de tous les protocoles de communication supportés.

Côté client, il faut s’assurer que les firewalls laissent passer les connexions sur le port du serveur et se méfier du filtrage inopportun de tel ou tel antivirus…

Environnement de développement

Node.js Tools for Visual Studio (NTVS) est un plug-in Open Source pour Visual Studio qui prend en charge le développement d'applications pour Node.js. NTVS support un large éventail de fonctionnalités : édition, intelliSense, débogage, profiling, intégration NPM, TypeScript, tests unitaires (support de Mocha)…

NTVS permet également de rapidement créer des sites web Node.js et de les déployer dans Microsoft Azure Web Sites ou dans un Cloud Service PaaS Azure (dans un WorkerRole).

image

Prochain article ?

Le prochain article de la série traitera de la consommation d’un service socket.io depuis une application web cliente.