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énementielle et des entrées/sorties asynchrones pour 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. 1
Ce que cache ses entrailles node standard library http(s), net, stream, fs, events, buffer JS node bindings JS / C++ V8 JavaScript VM libuv thread pool event loop async I/O c-ares async DNS http_parser OpenSSL zlib, etc. C/C++ 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. 2
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. 3
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 4
Les événements Un très grand nombre d'objets Node.js émettent des événements: ils héritent tous d'un objet EventEmitter fourni par Node. Par exemple le module "http", utilisable pour créer un serveur web, comprend un objet Server qui é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) { }); 5
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 6
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)); } ); } 7
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. 8
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 varnet = require('net'); varserver = net.createserver(function(socket) { socket.write('hello\n'); socket.end(); }) server.listen(9898); 9
Un exemple de serveur HTTP varhttp = require('http'); varfs = 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 varhttp = require('http'); varfs = 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); 10
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 11
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 npminit permettra de créer un squelette de fichier package.json mais on peut aussi créer celui-ci à la main. Le fichier package.json permet 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 à l'exécution Modules nécessaire au développement seulement: unit tests, minification,... 12
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.json né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. 13
Modules Node populaires Express "LE" frameworkweb (minimaliste) pour Node.js (inspiré du framework Ruby Sinatra). D'autres frameworksweb existent: Sails, Locomotive,... mais express est 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. mongoose offre 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. 14
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) Jade et EJS sont 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. 15
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). 16
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) Et comme outil de build: Grunt, Cake, Gulp ou Broccoli 17
Grunt Grunt est 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.js qui 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']); }; 18
Grunt On lance les tâches Grunt en tapant simplement grunt en 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, Cake ou 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 autorisations peuvent ê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). 19
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, passportfacebook, passport-twitter,...). Au final le code principal est très simple et ressemble à cela: app.post('/login', passport.authenticate('local', { successredirect: '/', failureredirect: '/login' })); 20
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. 21
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) 22
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 ou Heroku permettent un déploiement simple, sécurisé et économique. 23
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, 24