Cette traduction fournie par StrongLoop / IBM.

Il se peut que ce document soit obsolÚte par rapport à la documentation en anglais. Pour connaßtre les mises à jour les plus récentes, reportez-vous à la documentation en anglais.

✖

Meilleures pratiques en production : performances et fiabilité

Cet article traite des meilleures pratiques en termes de performances et de fiabilité pour les applications Express déployées en production.

La prĂ©sente rubrique s’inscrit clairement dans le concept “devops”, qui couvre Ă  la fois le dĂ©veloppement traditionnel et l’exploitation. Ainsi, les informations se divisent en deux parties :

A faire dans votre code

Les actions suivantes peuvent ĂȘtre rĂ©alisĂ©es dans votre code afin d’amĂ©liorer les performances de votre application :

Utiliser la compression gzip

La compression Gzip peut considĂ©rablement rĂ©duire la taille du corps de rĂ©ponse et ainsi augmenter la vitesse d’une application Web. Utilisez le middleware compression pour la compression gzip dans votre application Express. Par exemple :

const compression = require('compression')
const express = require('express')
const app = express()

app.use(compression())

Pour un site Web en production dont le trafic est Ă©levĂ©, la meilleure mĂ©thode pour mettre en place la compression consiste Ă  l’implĂ©menter au niveau d’un proxy inverse (voir Utiliser un proxy inverse). Dans ce cas, vous n’avez pas besoin d’utiliser le middleware compression. Pour plus de dĂ©tails sur l’activation de la compression gzip dans Nginx, voir Module ngx_http_gzip_module dans la documentation Nginx.

Ne pas utiliser les fonctions synchrones

Les fonctions et les mĂ©thodes synchrones ralentissent le processus d’exĂ©cution jusqu’à leur retour. Un simple appel Ă  une fonction synchrone peut revenir en quelques microsecondes ou millisecondes ; pour les sites Web dont le trafic est Ă©levĂ©, ces appels s’additionnent et rĂ©duisent les performances de l’application. Evitez de les utiliser en production.

Bien que Node et plusieurs modules mettent Ă  disposition les versions synchrone et asynchrone de leurs fonctions, utilisez toujours la version asynchrone en production. L’utilisation d’une fonction synchrone n’est justifiĂ©e que lors du dĂ©marrage initial.

You can use the --trace-sync-io command-line flag to print a warning and a stack trace whenever your application uses a synchronous API. Bien entendu vous n’utiliserez pas rĂ©ellement cette option en production, mais plutĂŽt pour vĂ©rifier que votre code est prĂȘt pour la phase production. Pour plus d’informations, voir Weekly update for io.js 2.1.0.

Procéder à une journalisation correcte

En rĂšgle gĂ©nĂ©rale, vous utilisez la journalisation Ă  partir de votre application Ă  deux fins : le dĂ©bogage et la journalisation de l’activitĂ© de votre application (principalement tout le reste). L’utilisation de console.log() ou de console.err() pour imprimer des messages de journal sur le terminal est une pratique courante en dĂ©veloppement. But these functions are synchronous when the destination is a terminal or a file, so they are not suitable for production, unless you pipe the output to another program.

Pour le débogage

Si vous utilisez la journalisation Ă  des fins de dĂ©bogage, utilisez un module de dĂ©bogage spĂ©cial tel que debug plutĂŽt que d’utiliser console.log(). Ce module vous permet d’utiliser la variable d’environnement DEBUG pour contrĂŽler les messages de dĂ©bogage envoyĂ©s Ă  console.err(), le cas Ă©chĂ©ant. Pour que votre application reste exclusivement asynchrone, vous devrez toujours diriger console.err() vers un autre programme. Mais bon, vous n’allez pas vraiment procĂ©der Ă  un dĂ©bogage en production, n’est-ce pas ?

Pour journaliser l’activitĂ© de votre application

If you’re logging app activity (for example, tracking traffic or API calls), instead of using console.log(), use a logging library like Pino, which is the fastest and most efficient option available.

Traiter correctement les exceptions

Les applications Node plantent lorsqu’elles tombent sur une exception non interceptĂ©e. Si vous ne traitez pas les exceptions et ne prenez pas les dĂ©cisions appropriĂ©es, votre application Express plantera et sera dĂ©connectĂ©e. Si vous suivez les conseils de la rubrique ci-dessous intitulĂ©e VĂ©rifier que votre application redĂ©marre automatiquement, votre application pourra ĂȘtre restaurĂ©e suite Ă  un plantage. Le dĂ©lai de dĂ©marrage des applications Express est heureusement court en rĂšgle gĂ©nĂ©rale. Vous souhaitez toutefois Ă©viter tout plantage en prioritĂ© et pour ce faire, vous devez traiter les exceptions correctement.

Pour vérifier que vous traitez toutes les exceptions, procédez comme suit :

Avant de s’immerger dans les rubriques qui suivent, il est conseillĂ© de possĂ©der des connaissances de base concernant le traitement des erreurs Node/Express, Ă  savoir l’utilisation des rappels “error-first” et la propagation des erreurs dans le middleware. Node utilise la convention de “rappel error-first” pour renvoyer les erreurs issues des fonctions asynchrones, dans laquelle le premier paramĂštre de la fonction callback est l’objet error, suivi par les donnĂ©es de rĂ©sultat dans les paramĂštres suivants. Pour n’indiquer aucune erreur, indiquez null comme premier paramĂštre. La fonction de rappel doit suivre la convention de rappel “error-first” de sorte Ă  traiter l’erreur de maniĂšre significative. Dans Express, la meilleure pratique consiste Ă  utiliser la fonction next() pour propager les erreurs via la chaĂźne du middleware.

Pour plus d’informations sur les bases du traitement des erreurs, voir :

Utiliser try-catch

Try-catch est un Ă©lĂ©ment de langage JavaScript que vous pouvez utiliser pour intercepter les exceptions dans le code synchrone. Utilisez try-catch pour traiter les erreurs d’analyse JSON, comme indiquĂ© ci-dessous, par exemple.

Voici un exemple d’utilisation de try-catch pour traiter une exception potentielle de plantage de processus. Cette fonction middleware accepte un paramĂštre de zone de requĂȘte nommĂ© “params” qui est un objet JSON.

app.get('/search', (req, res) => {
  // Simulating async operation
  setImmediate(() => {
    const jsonStr = req.query.params
    try {
      const jsonObj = JSON.parse(jsonStr)
      res.send('Success')
    } catch (e) {
      res.status(400).send('Invalid JSON string')
    }
  })
})

Toutefois, try-catch ne fonctionne que dans le code synchrone. Etant donnĂ© que la plateforme Node est principalement asynchrone (en particulier dans un environnement de production), try-catch n’interceptera pas beaucoup d’exceptions.

Utiliser des promesses

When an error is thrown in an async function or a rejected promise is awaited inside an async function, those errors will be passed to the error handler as if calling next(err)

app.get('/', async (req, res, next) => {
  const data = await userData() // If this promise fails, it will automatically call `next(err)` to handle the error.

  res.send(data)
})

app.use((err, req, res, next) => {
  res.status(err.status ?? 500).send({ error: err.message })
})

Also, you can use asynchronous functions for your middleware, and the router will handle errors if the promise fails, for example:

app.use(async (req, res, next) => {
  req.locals.user = await getUser(req)

  next() // This will be called if the promise does not throw an error.
})

Best practice is to handle errors as close to the site as possible. So while this is now handled in the router, it’s best to catch the error in the middleware and handle it without relying on separate error-handling middleware.

A ne pas faire

Vous ne devriez pas Ă©couter l’évĂ©nement uncaughtException, Ă©mis lorsqu’une exception remonte vers la boucle d’évĂ©nements. L’ajout d’un programme d’écoute d’évĂ©nement pour uncaughtException va modifier le comportement par dĂ©faut du processus qui rencontre une exception ; le processus va continuer Ă  s’exĂ©cuter malgrĂ© l’exception. Cela pourrait ĂȘtre un bon moyen d’empĂȘcher votre application de planter, mais continuer Ă  exĂ©cuter l’application aprĂšs une exception non interceptĂ©e est une pratique dangereuse qui n’est pas recommandĂ©e, Ă©tant donnĂ© que l’état du processus devient peu fiable et imprĂ©visible.

De plus, l’utilisation d’uncaughtException est officiellement reconnue comme Ă©tant rudimentaire et il a Ă©tĂ© proposĂ© de le supprimer. Ecouter uncaughtException n’est qu’une mauvaise idĂ©e. VoilĂ  pourquoi nous recommandons d’utiliser plusieurs processus et superviseurs Ă  la place : faire planter son application et la redĂ©marrer est souvent plus sĂ»r que de la restaurer aprĂšs une erreur.

L’utilisation de domain n’est Ă©galement pas recommandĂ©e. Ce module obsolĂšte ne rĂ©sout globalement pas le problĂšme.

Things to do in your environment / setup

{#in-environment}

Les actions suivantes peuvent ĂȘtre rĂ©alisĂ©es dans votre environnement systĂšme afin d’amĂ©liorer les performances de votre application :

DĂ©finir NODE_ENV sur “production”

La variable d’environnement NODE_ENV spĂ©cifie l’environnement dans lequel une application s’exĂ©cute (en rĂšgle gĂ©nĂ©rale, dĂ©veloppement ou production). One of the simplest things you can do to improve performance is to set NODE_ENV to production.

En dĂ©finissant NODE_ENV sur “production”, Express :

Tests indicate that just doing this can improve app performance by a factor of three!

Si vous avez besoin d’écrire du code spĂ©cifique Ă  un environnement, vous pouvez vĂ©rifier la valeur de NODE_ENV avec process.env.NODE_ENV. Sachez que la vĂ©rification de la valeur de n’importe quelle variable d’environnement pĂ©nalise les performances et devrait donc ĂȘtre effectuĂ©e avec modĂ©ration.

En dĂ©veloppement, vous dĂ©finissez gĂ©nĂ©ralement les variables d’environnement dans votre shell interactif, Ă  l’aide de export ou de votre fichier .bash_profile par exemple. But in general, you shouldn’t do that on a production server; instead, use your OS’s init system (systemd). La section qui suit fournit des dĂ©tails sur l’utilisation de votre systĂšme init en gĂ©nĂ©ral, mais la dĂ©finition de NODE_ENV est tellement importante pour les performances (et facile Ă  rĂ©aliser), qu’elle est mise en Ă©vidence ici.

Avec systemd, utilisez la directive Environment dans votre fichier d’unitĂ©. Par exemple :

# /etc/systemd/system/myservice.service
Environment=NODE_ENV=production

For more information, see Using Environment Variables In systemd Units.

Vérifier que votre application redémarre automatiquement

En production, vous ne souhaitez jamais que votre application soit dĂ©connectĂ©e. Vous devez donc veiller Ă  ce qu’elle redĂ©marre si elle plante et si le serveur plante. MĂȘme si vous espĂ©rez que cela n’arrive pas, vous devez en rĂ©alitĂ© considĂ©rer ces deux Ă©ventualitĂ©s en :

Les applications Node plantent si elles tombent sur une exception non interceptĂ©e. Avant toute chose, vĂ©rifiez que votre application est correctement testĂ©e et qu’elle traite toutes les exceptions (voir Traiter correctement les exceptions pour plus de dĂ©tails). En cas d’échec, mettez en place un mĂ©canisme qui garantit que si et lorsque votre application plante, elle redĂ©marre automatiquement.

Utiliser un gestionnaire de processus

En dĂ©veloppement, vous avez simplement dĂ©marrĂ© votre application Ă  partir de la ligne de commande avec node server.js ou une instruction similaire. En production, cela vous mĂšnera droit au dĂ©sastre. Si l’application plante, elle sera dĂ©connectĂ©e tant que vous ne la redĂ©marrerez pas. Pour garantir que votre application redĂ©marre si elle plante, utilisez un gestionnaire de processus. Un gestionnaire de processus est un “conteneur” d’applications qui facilite le dĂ©ploiement, offre une haute disponibilitĂ© et vous permet de gĂ©rer l’application lors de son exĂ©cution.

En plus de redĂ©marrer votre application lorsqu’elle plante, un gestionnaire de processus peut vous permettre :

Historically, it was popular to use a Node.js process manager like PM2. See their documentation if you wish to do this. However, we recommend using your init system for process management.

Utiliser un systĂšme init

Le niveau de fiabilitĂ© suivant consiste Ă  garantir que votre application redĂ©marre lorsque le serveur redĂ©marre. Les systĂšmes peuvent toujours tomber en panne pour divers motifs. Pour garantir que votre application redĂ©marre si le serveur plante, utilisez le systĂšme init intĂ©grĂ© Ă  votre systĂšme d’exploitation. The main init system in use today is systemd.

Vous pouvez utiliser les systĂšmes init de deux maniĂšres dans votre application Express :

Systemd

Systemd est un systÚme Linux et un gestionnaire de services. La plupart des distributions Linux principales ont adopté systemd comme leur systÚme init par défaut.

Un fichier de configuration de service systemd est appelĂ© fichier d’unitĂ© et porte l’extension .service. Voici un exemple de fichier d’unitĂ© permettant de gĂ©rer une application Node directement (remplacez le texte en gras par les valeurs appropriĂ©es Ă  votre systĂšme et votre application) : Replace the values enclosed in <angle brackets> for your system and app:

[Unit]
Description=<Awesome Express App>

[Service]
Type=simple
ExecStart=/usr/local/bin/node </projects/myapp/index.js>
WorkingDirectory=</projects/myapp>

User=nobody
Group=nogroup

# Environment variables:
Environment=NODE_ENV=production

# Allow many incoming connections
LimitNOFILE=infinity

# Allow core dumps for debugging
LimitCORE=infinity

StandardInput=null
StandardOutput=syslog
StandardError=syslog
Restart=always

[Install]
WantedBy=multi-user.target

Pour plus d’informations sur systemd, voir la page d’aide de systemd.

Exécuter votre application dans un cluster

Dans un systĂšme multicoeur, vous pouvez augmenter les performances d’une application Node en lançant un cluster de processus. Un cluster exĂ©cute plusieurs instances de l’application, idĂ©alement une instance sur chaque coeur d’UC, rĂ©partissant ainsi la charge et les tĂąches entre les instances.

Balancing between application instances using the cluster API

IMPORTANT : Ă©tant donnĂ© que les instances d’application s’exĂ©cutent en tant que processus distincts, elles ne partagent pas le mĂȘme espace mĂ©moire. Autrement dit, les objets sont en local sur chaque instance de l’application. Par consĂ©quent, vous ne pouvez pas conserver l’état dans le code de l’application. Vous pouvez toutefois utiliser un magasin de donnĂ©es en mĂ©moire tel que Redis pour stocker les donnĂ©es de session et l’état. Cette fonctionnalitĂ© s’applique essentiellement Ă  toutes les formes de mise Ă  l’échelle horizontale, que la mise en cluster soit effectuĂ©e avec plusieurs processus ou avec plusieurs serveurs physiques.

Dans les applications mises en cluster, les processus de traitement peuvent planter individuellement sans impacter le reste des processus. Outre les avantages en termes de performance, l’isolement des pannes constitue une autre raison d’exĂ©cuter un cluster de processus d’application. Chaque fois qu’un processus de traitement plante, veillez toujours Ă  consigner l’évĂ©nement et Ă  gĂ©nĂ©ration un nouveau processus Ă  l’aide de cluster.fork().

Utilisation du module cluster de Node

Clustering is made possible with Node’s cluster module. Ce module permet Ă  un processus maĂźtre de gĂ©nĂ©rer des processus de traitement et de rĂ©partir les connexions entrantes parmi ces processus.

Using PM2

If you deploy your application with PM2, then you can take advantage of clustering without modifying your application code. You should ensure your application is stateless first, meaning no local data is stored in the process (such as sessions, websocket connections and the like).

When running an application with PM2, you can enable cluster mode to run it in a cluster with a number of instances of your choosing, such as the matching the number of available CPUs on the machine. You can manually change the number of processes in the cluster using the pm2 command line tool without stopping the app.

To enable cluster mode, start your application like so:

# Start 4 worker processes
$ pm2 start npm --name my-app -i 4 -- start
# Auto-detect number of available CPUs and start that many worker processes
$ pm2 start npm --name my-app -i max -- start

This can also be configured within a PM2 process file (ecosystem.config.js or similar) by setting exec_mode to cluster and instances to the number of workers to start.

Once running, the application can be scaled like so:

# Add 3 more workers
$ pm2 scale my-app +3
# Scale to a specific number of workers
$ pm2 scale my-app 2

For more information on clustering with PM2, see Cluster Mode in the PM2 documentation.

Mettre en cache les rĂ©sultats d’une demande

Pour amĂ©liorer les performances en production, vous pouvez Ă©galement mettre en cache le rĂ©sultat des demandes, de telle sorte que votre application ne rĂ©pĂšte pas l’opĂ©ration de traitement de la mĂȘme demande plusieurs fois.

Use a caching server like Varnish or Nginx (see also Nginx Caching) to greatly improve the speed and performance of your app.

Utiliser un équilibreur de charge

Quel que soit le niveau d’optimisation d’une application, une instance unique ne peut traiter qu’un volume limitĂ© de charge et de trafic. Pour faire Ă©voluer une application, vous pouvez exĂ©cuter plusieurs instances de cette application et rĂ©partir le trafic en utilisant un Ă©quilibreur de charge. La configuration d’un Ă©quilibreur de charge peut amĂ©liorer les performances et la vitesse de votre application et lui permettre d’évoluer plus largement qu’avec une seule instance.

Un Ă©quilibreur de charge est gĂ©nĂ©ralement un proxy inverse qui orchestre le trafic entrant et sortant de plusieurs instances d’application et serveurs. You can easily set up a load balancer for your app by using Nginx or HAProxy.

Avec l’équilibrage de charge, vous devrez peut-ĂȘtre vĂ©rifier que les demandes associĂ©es Ă  un ID de session spĂ©cifique sont connectĂ©es au processus dont elles sont issues. Ce procĂ©dĂ© est appelĂ© affinitĂ© de session (ou sessions persistantes) et peut ĂȘtre effectuĂ© en utilisant un magasin de donnĂ©es tel que Redis pour les donnĂ©es de session (en fonction de votre application), comme dĂ©crit ci-dessus. Pour en savoir plus, voir Using multiple nodes.

Utiliser un proxy inverse

Un proxy inverse accompagne une application Web et exĂ©cute des opĂ©rations de prise en charge sur les demandes, en plus de diriger les demandes vers l’application. Il peut gĂ©rer les pages d’erreur, la compression, la mise en cache, le dĂ©pĂŽt de fichiers et l’équilibrage de charge entre autres.

La transmission de tĂąches qui ne requiĂšrent aucune connaissance de l’état d’application Ă  un proxy inverse permet Ă  Express de rĂ©aliser des tĂąches d’application spĂ©cialisĂ©es. For this reason, it is recommended to run Express behind a reverse proxy like Nginx or HAProxy in production.

Edit this page