Présentation
Développé par Ryan Dahl - Société Joyent Inc. US en 2009, écrit en C++ et JavaScript.
Projet "logiciel libre" (license MIT) construit pour permettre le développement, en JavaScript, de
services réseaux hautement performants et supportant facilement les montées en charge.
Utilise le moteur JavaScript V8 de Google, une architecture événementielleet des
entrées/sorties asynchronespour améliorer les performances.
Node.js est devenu rapidement très populaire comme plateforme serveur et est utilisé notamment par:
MicroSoft, SAP, Linkedin, PayPal, ...
La version courante en 2015 est la 0.12.
Principales caractéristiques
Fonctionne sur le principe d'une boucle d'événements mono-thread (comme "Event Machine" de Ruby ou
"Twisted" de Python).
Utilise des Entrées/Sorties Asynchrones (utilise pour cela en interne la librairie C libuv).
Se programme en JavaScript, mais un JavaScript
"amélioré".
Propose une librairie JavaScript intégrée assez
sommaire mais implémentée de manière très efficace.
Si la librairie interne n'est pas suffisante, il propose un très grand nombre de modules et un superbe outil (npm) pour les gérer.
Ce que cache ses entrailles …
JS
JS / C++
C/C++
node standard library http(s), net, stream, fs, events, buffer
node bindings
V8 JavaScript VM
libuv thread pool
event loop async I/O
c-ares async DNS
http_parser OpenSSL
zlib, etc.
JavaScript pourquoi?
JavaScript est très populaire (utilisé par tous les développeurs web).
JavaScript est, comme Node.js, entièrement basé sur la programmation événementielle
JavaScript n'a pas intrinsèquement d'I/O bloquante (en fait il n'a pas d'I/O du tout sauf via une API Node.js spéciale).
Certains problèmes du langage sont corrigés:
modularisation, encapsulation, API d'entrées/sorties, ...
(implémentation des specs. CommonJS).
Node.js utilise le moteur V8 de Google qui est très optimisé et compile le JavaScript en langage machine.
Oui, mais un JavaScript modulaire!
Exemple de module (fichier base64.js):
var encoding = 'base64'; // variable privée exports.toBase64 = function(s) {
return new Buffer(s).toString(encoding);
}
et d'utilisation du module (fichier apps.js):
var b64 = require('./base64');
var a = b64.toBase64('JSSaturday');
Les événements
Node.js est basé sur les événements.
Node.js utilise un seul thread (une boucle
d'événements) mais aucune opération n'est bloquante.
Les opérations un peu longues (chargement d'un fichier, requête réseau, …) sont lancées en tâche de fond (par un "worker thread") et au terme de l'opération un événement est envoyé, celui-ci déclenche alors l'exécution d'une fonction dite de "callback".
Ces fonctions de callback apparaissent dans toutes les applications Node.js.
Le problème du multi-threads
1.
La synchronisation des threads: difficile à gérer, coûteuse question performances.
2.
Le passage d'un thread à un autre nécessite un
"context switch": coûteux en temps d'exécution et mémoire.
La boucle d'événements
Les événements
Un très grand nombre d'objets Node.js émettent des événements: ils héritent tous d'un objet
EventEmitterfourni par Node.
Par exemple le module "http", utilisable pour créer un serveur web, comprend un objet Serverqui émet différents événements: request, connection, close, ...
Pour écouter un événement il faut faire appel à la méthode on()et indiquer :
1. Le nom de l'événement écouté
2. La fonction de callback à appeler quand l'événement survient
Les événements
Exemple:
var server = http.createServer();
server.on('request', function(req, res) { …});
Ou, plus directement, grâce à un raccourci syntaxique proposé par le module http:
var server = http.createServer(function(req, res) {
… });
Les événements
Pour émettre un événement il faut inclure le module events et créer un EventEmitter.
var game= require('events').EventEmitter();
Pour émettre un événement il faut faire appel à emit() en précisant le nom de l'événement à émettre et les informations à inclure dans celui-ci:
game.emit('gameover', 'Vous avez perdu !');
On peut écouter cet événement avec la technique habituelle:
game.on('gameover', function(message) { });
I/O Asynchrones
Les entrées/sorties sont asynchrones: Node.js initie la requête d'entrée/sortie et retourne (pas d'attente bloquante).
Le serveur peut effectuer d'autres tâches en attendant la complétion de la requête.
A la complétion de la requête il y a notification, par événement, du résultat ou de l'erreur.
Exemples de serveurs Web "asynchrones":
nginx, lighttpd
I/O Asynchrones: pourquoi?
Temps d'attente approximatif d'une requête de type I/O en fonction du support utilisé:
Cycles CPU
L1 cache 3
L2 cache 14
RAM 250
disk 41 000 000
network 240 000 000
Métaphore
Pièce d’à côté Maison d’en face A l’autre bout du quartier
Tour complet de la terre A mi-chemin vers la lune
I/O Asynchrones: un exemple
Lecture de fichier asynchrone:
function getAFile(req, res) { fs.readFile ("c:/data.xml",
function(err, data) {
if (!err) res.send(200,
JSON.stringify(data));
} );
}
Bibliothèques JS intégrées
Voici la liste des bibliothèques contenues dans Node.js:
REPL: c'est l'interpréteur que vous avez quand vous tapez node dans votre console.
assert: pour faire des assertions.
console : pour les logs.
debugger : point d'arrêt, pas à pas, ...
dns: accès aux serveurs de noms de domaines.
event: gestion des événements.
fs: gestion du système de fichiers.
global: facilités disponibles partout http: gestion du protocole http
net: gestion du réseau en mode asynchrone.
path : gestion des paths sur un système de fichiers.
Bibliothèques JS intégrées
os: gestion du système: dossiers temporaires, noms d'hôtes, ...
querystring: échapper, analyser les arguments d'une requête.
string_decoder: permet de passer d'un buffer à une chaîne.
timers: permet d'appeler régulièrement des actions, tls: gestion du protocole SSL
dgram: support des datagram Socket UDP util: différents outils, héritage, tests de type, ....
zlib: compression et lecture des formats gzip.
Le streaming des données
Traditionnellement les frameworks HTTP traitent les requêtes et réponses comme des objets "monolitiques", alors qu'en fait ce sont des flux (streams) de données.
Node.js étant très performant dans ses I/O, on peut s'en servir pour lire et écrire des flux de données via des WebSockets ou via HTTP.
On peut ainsi imaginer, par exemple, transcoder un fichier video tout en le téléchargeant (réduisant ainsi sensiblement le temps de traitement).
On peut aussi imaginer "piper" la sortie d'un processus tournant sur le serveur avec le stream d'un websocket à destination du browser et bénéficier ainsi d'une page Web qui s'affiche en temps réel.
Voici pourquoi Node excelle dans les applications Web multi-utilisateurs, temps réels comme les "chat" ou les jeux.
Un exemple de serveur socket
var net = require('net');
var server = net.createServer(function(socket) {
socket.write('hello\n');
socket.end();
})
server.listen(9898);
Un exemple de serveur HTTP
var http = require('http');
var fs = require('fs');
// Chargement du fichier index.html affiché au client var server = http.createServer(function(req, res) {
fs.readFile('./index.html', 'utf-8', function(error, content) {
res.writeHead(200, {"Content-Type":
"text/html"});
res.end(content);
});
});
server.listen(8080);
Meilleur exemple de serveur HTTP
var http = require('http');
var fs = require('fs');
function callback(req, res) {
fs.readFile('./index.html', 'utf-8', function(error, content) {
if(!error){ res.writeHead(200, {"Content-Type": "text/html"});
res.end(content);
}else{ res.writeHead(500) res.end()
});
}
// Chargement du fichier index.html affiché au client var server = http.createServer();
server.on('request', callback);
server.listen(8080);
NPM: Node Package Management
npm est un module Node.js très pratique: il permet de rechercher et d'installer des
packages, mais aussi de mettre à jour, et gérer leurs versions et plus encore (+/- équivalent de pip en python, gem en Ruby ou encore apt-get dans le monde Linux).
npm utilise un registre centralisé de modules.
Note: nombreux sont les développeurs qui trouvent, qu'à lui seul, cet outil justifie l'usage de Node.js !
NPM: Node Package Management
npm est très utile pour installer, mettre à jour et gérer les dépendances nécessaires à une application Node.js, mais il sert aussi à publier un package et permet ainsi de partager une application ou une librairie.
Enfin, npm peut aussi être utilisé pour lancer
une application Node.js
NPM: Node Package Management
Pour utiliser npm avec une application Node, il faut placer un fichier package.json (un fichier de configuration au format JSON) à la racine du projet.
Une commande comme npm initpermettra de créer un squelette de fichier package.jsonmais on peut aussi créer celui-ci à la main.
Le fichier package.jsonpermet d'identifier le projet (nom, version, lancement, licences, données de configuration, ...) et ses dépendances.
NPM: Node Package Management
{"name": "testNode",
"version": "0.1.0",
"description": "Petite application de test",
"author": "JP Forestier <jpf@osyx.com>",
"dependencies": {"colors": "0.x.x",
"express": "3.3.x",
"commander": "2.3.1"},
"devDependencies": {"mocha": "0.5.x"},
"engine": "node >= 0.8"
} Modules nécessaire au développement
seulement: unit tests, minification, ...
Modules nécessaire à l'exécution
NPM: Node Package Management
Quelques exemples de commandes :
npm help
npm install packagename [-g]: effectue une installation du package en question dans le projet courant. –g effectue une installation du package globalement (pour toutes les applications).
npm update: met à jour, si besoin, les dépendances du projet courant
npm init: crée un squelette de fichier package.json
npm start: démarre l'application courante (section "script" dans package.jsonnécessaire)
npm ls, npm search, npm config, …
NPM: Node Package Management
Le registre central des modules npm est:
http://npmjs.com . Quelques chiffres:
>145 000 modules (privés ou publics)
>50 millions de téléchargements par jour
Voici quelques modules très utilisés pour les
applications Web.
Modules Node populaires
•
Express
"LE" framework Web (minimaliste) pour Node.js (inspiré du framework Ruby Sinatra).
D'autres frameworks Web existent: Sails,
Locomotive, ... mais expressest le plus utilisé.
•
Request
Request est un utilitaire simple, permettant de faire de Node.js un client HTTP (pratique pour utiliser des services REST depuis Nodes.js).
Modules Node populaires
•
mongoose
Ce module est une librairie de type ODM (Object to Document Mapping) permettant d'interagir facilement avec MongoDB depuis Node.js.
mongooseoffre la possibilité de construire un schéma précis des données et se charge de vérifier celui-ci lors des insertions et mises à jour.
Modules Node populaires
• Underscore (ou Lodash)
Underscore est une librairie JavaScript apportant à Node.js différents outils liés à la programmation fonctionnelle chère aux Pythonistes, comme les fonctions each(), map(), reduce(), filter() etc.
•
CoffeeScript
CoffeeScript est un langage de programmation qui se compile en JavaScript. Le langage ajoute du "sucre syntaxique", inspiré par Python, Ruby et Haskell, afin d'améliorer la brièveté et la lisibilité du code, tout en ajoutant des fonctionnalités comme le filtrage par motif ou les listes en compréhension.
Modules Node populaires
• EJS (ou Jade)
Jadeet EJSsont des outils de type "template engine". Un template est un moyen de séparer le contenu rédactionnel (contenu textuel) de la forme (la manière dont il est présenté).
Un template fait office de gabarit (modèle) où seuls certains éléments sont modifiables (le contenu texte, les images, le fond, …).
L’utilisation d’un template facilite la conception d’un site Internet et sa mise à jour, aussi bien en termes de contenu que de présentation.
Modules Node populaires
• Cheerio
Une implémentation des fonctionnalités "core" de jQuery pour le côté serveur (manipulation du DOM, extraction de données). Cheerio est "léger" et très efficace.
• Stylus (ou sass ou less)
Stylus est un préprocesseur de CSS (tout comme Less ou SASS). Les préprocesseurs viennent combler certains manques du CSS (variables, structures de contrôles, ...). Stylus semble le plus avancé des 3, sa syntaxe est proche de Jade.
Modules Node populaires
• Socket.io
Ce module permet de faire, très simplement, de la communication synchrone dans les applications Node.js en utilisant le principe des WebSocket HTML5. Comme tous les navigateurs ne connaissent pas encore l’API WebSocket, Socket.io est capable de s’adapter aux capacités du client et d'utiliser d'autres techniques de communication synchrones: Adobe Flash Socket, AJAX long polling, …
Socket.io permet une communication par
événements entre serveur et client(s) (le broadcast est supporté) (sans toutefois utiliser les SSE HTML 5:
vous pouvez utiliser le middleware connect-sse pour cela).
Modules Node populaires
• Uglify-js
Ce module s'utilise généralement en ligne de commande et sert à minimiser et optimiser le code JavaScript. Ce processus peut être automatisé grâce à une tâche Grunt.
async
Ce module propose de nombreux outils liés à la programmation fonctionnelle (map, reduce, filter, each…) et implémente quelques design pattern très utile dans le contrôle des tâches asynchrones (parallel, series, waterfall…).
Autres modules
• Sails/Americano(autres framework web)
• Commander/Optimist/Nopt(parsers d'arguments)
• Moment (date)
• Through (gestion de flux)
• Glob(recherche de fichiers)
• Rimraf/fs-extra (rm -rf, add-on file-system)
• Shelljs (Bash dans node)
• Chalk, Colors(coloration sortie console)
Grunt
• Gruntest un task runner (exécuteur de tâches) en ligne de commande écrit en JavaScript.
• Similaire dans les grandes lignes à ANT, Maven ou Gradle, il est conçu principalement pour automatiser le traitement des tâches récurrentes effectuées sur les applications web : minification, optimisation des images, génération de sprites, compilation Sass/Less, etc.
Grunt
• Son principe de fonctionnement repose sur un fichier de configuration: Gruntfile.jsqui sert à la
configuration des tâches, à gérer leur ordre
d'exécution et à charger les modules nécessaires à leur bon déroulement:
module.exports = function(grunt) { grunt.initConfig({
pkg: grunt.file.readJSON('package.json'), // … configuration des tâches grunt
});
// Les modules pour gérer les tâches sont chargés ici:
grunt.loadNpmTasks('grunt-contrib-jshint');
// Les tâches sont enregistrées ici:
grunt.registerTask('test', ['jshint', 'qunit']);
};
Grunt
• On lance les tâches Grunt en tapant simplement grunten ligne de commandes, mais on peut même éviter de taper cette commande et automatiser complètement la procédure grâce au module watch.
• Parmi les concurrents de Grunt on peut citer Gulp, Cakeou Broccoli(Grunt est à ce jour le plus utilisé).
Module d'Authentification
• Si une authentification des clients s'avère nécessaire, le module Passport peut être utilisé pour cela.
• Passport sert à authentifier les requêtes, il délègue toutes les autres fonctionnalités à l'application.
Note: Passport ne se charge que de l'aspect authentification, les autorisationspeuvent êtres contrôlées par d'autres modules: everyauth, authom, … (il n'y a pas de réel consensus sur un module d'autorisation à ce jour).
Module d'Authentification
•
Avec passport, l'authentification peut prendre différentes formes:
• Traditionnelle: l'utilisateur entre un login et un mot de passe.
• Utiliser un token d'authentification (JWT ou autre)
• Passer par le mécanisme SSO (Single sign-on) d'un fournisseur OAuth tel que Facebook, Twitter ou Google.
Module d'Authentification
• Passport considère que chaque application a un besoin d'authentification qui lui est propre et chaque technique d'authentification a un module qui lui est propre
(passport-local, passport-token, passport- facebook, passport-twitter, ...).
• Au final le code principal est très simple et ressemble à cela:
app.post('/login', passport.authenticate('local', { successRedirect: '/',
failureRedirect: '/login' }));
Node.js pour quel usage?
Les serveurs Websocket (ex: serveur de chat) Le serveurs de téléchargement de fichiers (upload) Les serveurs d’annonces publicitaires (AD-Server) Tous types de serveurs de données temps réel Le E-Commerce, la gestion des paiements, les médias sociaux, les services Web d'entreprises, …
En résumé, Node est un environnement JavaScript coté serveur, permettant le développement d'applications Web, de serveurs d'applications, et de toutes sortes d'applications réseau client ou serveur.
Node.js adapté aussi pour …
Servir des documents "statiques", mais on obtient encore de meilleures performances en utilisant le serveur Nginx en "front end" d’un serveur Node.js.
Mais pas ideal pour …
Les calculs intensifs (sollicitation du CPU): ils peuvent dégrader les temps de réponse du serveur et empêcher les montées en charge.
Solutions:
On peut améliorer les choses en créant un cluster de serveurs Nodes.js (il y a un module pour cela).
On peut aussi installer plusieurs serveurs Node.js derrière un serveur Nginx agissant comme "reverse proxy".
Node.js n’est en tous cas pas …
Un framework pour applications Web: il y a des modules pour cela (express, Sails.js, …)
Pour les débutants: car de très bas niveau
Multi-thread (au sens où on l'entend en
général)
En résumé, les avantages
• Logiciel libre (licence MIT)
• Très bonnes performances
baisse des coûts d'infrastructure meilleure satisfaction des clients Exemples:
Linkedin: Rails->Node.js: 10x moins de serveurs, 20x plus rapide Groupon: baisse de 50% du chargement des pages
Paypal: temps de réponse / 2
• Large et active communauté de développeurs
En résumé, les avantages
• JavaScript+npm:
Productivité élevée (ex: Paypal -> productivité x 2) Satisfaction des développeurs
Uniformisation des développements côté client et serveur
• De grands acteurs l'utilisent (avec satisfaction):
Wallmart, PayPal, Yahoo, Linkedin, …
• Différents services d'hébergement d'applications Node.js (type Platform-as-a-Service (PaaS)) comme Modulus ouHerokupermettent un déploiement simple, sécurisé et économique.
Et pour les dev. Web …
Echange de données entre client et server facilité grâce au format commun: JSON
Le même langage est utilisé côté client et serveur:
Une même équipe de développeurs peut intervenir Même si 2 équipes distinctes: meilleure compréhension mutuelle.
Mêmes outils de dev., test, profiling, etc.. des 2 côtés.
Usage possible des templates côté client et/ou serveur.
Un concurrent ?
• io.js un fork ("amical") récent de Node.js.
• But du fork: échapper à la tutelle de Joyent, pour avoir une réactivité et une souplesse plus grandes
(adoption du dernier moteur V8, adoption de nouveautés EcmaScript 6, utilisation de nouvelles librairies, …).
• io.js améliore encore les performances de Node.js dans de nombreuses situations.
• Fusion vraisemblable de Node.js et io.js prochainement … on en parle, on l'espère, …