Node.js , la découverte.

« Le javascript s’exécute coté client » , première phrase du premier cours/tuto qui commence à remonter à loin, sur du JavaScript. Et bien non pas cette fois. Cette fois on va se la jouer JavaScript coté serveur. « Ho mon dieu quel horreur » peut être la première réaction qui vient à l’esprit, un « pourquoi faire? » est aussi envisageable, pourquoi? C’est simple, depuis des années les navigateurs web font la course aux performances sur l’interprétation du JavaScript, alors quand on met le V8 de chrome sur un serveur qu’on couple le tout avec un serveur web pensé de manière moderne, ça fait mal aux performances, après deux ou trois tests (certes pas vraiment viables puisque je tape dans un soap qui répond plus ou moins, pour récupérer des données), php pleure.

First thing first, le site du projet : http://nodejs.org/

Comment on installe, ce node?, comment ça marche? , c’est parti allons y faire un tour.

L’install

Les sources sont là : http://nodejs.org/dist/node-v0.1.101.tar.gz (si vous préférez git : http://github.com/ry/node )
Sous linux mac ou solaris l’installe se fait en compilant le paquet (allez dans le dossier en ligne de commande et) :

> ./configure

> make

> make install

Une fois que c’est fait on peut utiliser la ligne de commande :

> node nomDeFichier.js

Et c’est là que ça commence à devenir marrant.

Mise en place

Voilà actuellement un fichier que j’ai appelé node.js dans mon dossier home sur mon mac, et sur lequel j’exécute la commande « node » (> node node.js) :

//On charge le module http, et on l'utilise pour créer notre serveur web.
var http = require('http');
http.createServer(function (req, res) {

//On utilise l'objet res (response) pour modifier le header, je veux du xml.

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

//On charge un autre module pour gérer des webservices en SOAP(maison celui là, j'y reviendrai plus tard)
var soapClient = require("./soap");

/*
* parameters like http://localhost:8124/functionName/param
*/
//On utilise l'objet url de la request (req) , un petit coup d'expression régulière pour récupérer un nom de fonction et un paramètre comme dans l'url au dessus et les mettre dans des variables.
var urlreq = req.url.match(/\/(.*)\/(.*)/);
if(urlreq != null){
var soapRequest = urlreq[1];
var soapParam = urlreq[2];
//On utilise les méthodes du module maison.
soapClient.setSoapAction(soapRequest).setParam(soapParam).launch(res);
}else{
//sinon on affiche quand même un xml d'erreur
res.write("<error><message>No parameters</message></error>");
//IMPORTANT si on ne spécifie pas le end(); le serveur va tourner en rond pendant un long moment, le res.end() est effectué par la méthode launch dans le if.
res.end();
}
//le port et l'ip
}).listen(8124, "127.0.0.1");
//Un petit affichage sur la console, pour dire que tout est démarré.
console.log('Server running at http://127.0.0.1:8124/');

Attention aussi après avoir fait une modification dans le fichier il faut couper le serveur (ctrl+c) et refaire la commande « node fichier.js ».

Les modules

Un module c’est un fichier que l’on va appeler avec la fonction require et qui génère un objet (bien plus de détail dans la partie module içi : http://nodejs.org/api.html )

Actuellement mon soap.js n’est pas terminé, mais je vais vous en montrer une grande partie quand même :

//Les paramètres spécifiques au soap:
var soapAction = '';
var param = '';

//L'enveloppe et le body du soap (plus d'infos ici : <a taret="_blanck"  href="http://www.w3schools.com/soap/soap_body.asp">http://www.w3schools.com/soap/soap_body.asp</a> ). Je mets des paramètres comme %@soapAction ou %@param que je remplacerai plus tard par leur vraie valeur.

var body = "<soap:Envelope
xmlns:soap="http://www.w3.org/2001/12/soap-envelope"
soap:encodingStyle="http://www.w3.org/2001/12/soap-encoding"><soap:Body><%@soapAction xmlns='URL'>%@param</%@soapAction></soap:Body></soap:Envelope>";

var format = 'text/xml; charset=utf-8';
var strHost = 'toto.com';
var rq = '/toto/?wsdl';

//Ensuite le mot clé exports permet de spécifier ce que mon objet aura comme méthodes publiques (ce que mon module exporte comme fonction, le reste est par défaut privé), je retourne "this" pour pouvoir chainer mes requêtes.

exports.setSoapAction = function(strAction){
soapAction = strAction;
return this;
}
exports.setParam = function(strParam){

param = strParam;
return this;
}

//La méthode qui lance l'appel au serveur soap et qui récupère l'xml

exports.launch = function(res){

//On insère nos paramètres
body = body.replace(/%@soapAction/gi,soapAction).replace(/%@param/gi,param);
//On charge un module http
var http = require('http');

var s = http.createClient(80, strHost);

//On spécifie les paramètres de notre requête et on effectue cette requête
var request = s.request('POST', rq,
{'host': strHost,'SOAPAction':soapAction,'Content-Type':format,'Content-Length':body.length});
// On écrit le contenu de body (alors que notre flux de notre requête est ouvert, du coup on envoie le contenu de body sur le serveur).
request.write(body);
//La requête est terminé
request.end();

//Tout est asynchrone on va donc utiliser des fonctions de callback quand certains événements apparaissent.
//On effectue une fonction si l'on a une réponse du serveur
request.on('response', function (response) {
response.setEncoding('utf8');
//si on a des données(ou des morceaux de données) on les écrit sur la page
response.on('data', function (chunk) {
res.write(chunk);
});
//Si la requête est terminée
response.on('end' ,function(){
//On signifie qu'on termine la réponse.
res.end();
});
});
}

Mon module ne respecte sans doute pas certaines bonnes pratiques, je me suis lancé dedans sans vraiment aller voir la liste des modules existants ( http://wiki.github.com/ry/node/modules ), le but étant de tester rapidement une fonctionnalité.

Pourquoi pas en php ?
Sur 100 appels consécutifs avec node je tournais autour de 80ms, avec php(5.3.2 standard) sur apache 260ms (ces chiffres sont approximatifs je reviens vite avec des tests plus précis et rigoureux).

Pourquoi récupérer un xml en soap et l’afficher? Pour l’instant le seul avantage est que mon app cliente n’a plus à gérer la connexion au soap, ce qui est quand même un grand soulagement… Parser un xml et renvoyer seulement certaines informations qui intéressent mon app est ma prochaine tâche sur ma todo.

En réalité non, j’ai d’abord quelques améliorations à faire sur mon app iPhone… Et la préparation de plusieurs podcasts qui prend un temps beaucoup plus long que ce que j’aurais cru au premier abord… Ceci dit l’article sur l’outline view façon iCal arrivera assez vite, j’ai refait tout le code de manière propre optimisée, il ne reste plus qu’à écrire les explications en essayant de rester le plus clair possible…

Un commentaire pour Node.js , la découverte.

  1. TheSorrow dit :

    Hello,
    Le seul truc chiant avec nodejs c’est que l’imbrication de callback devient assez vite n’importe quoi. Je te conseil cette lib pour y remédier :

    http://github.com/caolan/async

    Elle rend vraiment simple l’application des patterns les plus utilisés en matière de prog asynchrone

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

*

Vous pouvez utiliser ces balises et attributs HTML : <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>