Sommaire
Salut la compagnie ! Aujourd’hui, on va parler d’un sujet qui fait transpirer plus d’un développeur Node.js : la mise à l’échelle. Eh oui, quand votre application cartonne et que les requêtes affluent de partout, il faut être paré. Pas de panique, on va vous expliquer comment passer de « petit leader technique » à « génie de l’optimisation » en quelques étapes. (Bon, on exagère un peu, mais vous allez quand même en apprendre un rayon !)
Le cœur du problème : la gestion des requêtes et des threads
Alors, pour bien comprendre d’où vient la nécessité de mettre à l’échelle, il faut d’abord saisir comment Node.js gère les requêtes et les threads. En gros, Node.js a un thread principal qui s’occupe de traiter toutes les requêtes, une par une. (Un peu comme quand vous deviez attendre patiemment que maman ait fini de servir votre grand frère avant de vous donner votre part de gâteau.)
Concrètement, ça donne quelque chose comme ça :
- Une requête arrive et est traitée en 1 seconde.
- Une autre requête débarque, mais elle doit attendre 1 seconde que la première soit terminée pour être traitée (soit 2 secondes au total).
- Une troisième requête se pointe, mais doit attendre 2 secondes que les deux premières soient finies pour être exécutée (soit 3 secondes au total).
Vous voyez le topo ? Plus il y a de requêtes, plus ça prend du temps. (Un peu comme quand vous étiez gosses et que vous vous battiez pour avoir le dernier morceau de pizza.)
Une petite subtilité
Ceci dit, Node.js est assez malin pour déléguer certaines tâches au noyau du système d’exploitation, qui lui est multi-thread. Des opérations comme la lecture de fichiers, les requêtes DNS ou la compression de données peuvent donc être exécutées en parallèle. (Un peu comme quand maman vous donnait une tâche pendant qu’elle s’occupait de votre frère.)
Mais pour le reste, c’est le thread principal de Node.js qui doit tout gérer. Et quand il est surchargé, les requêtes s’accumulent et doivent attendre leur tour. (Comme quand vous deviez faire la queue pour avoir votre part de gâteau.)
Plus le serveur Node.js est occupé, plus le temps d’attente est long. Plus il y a de demandes, plus le temps d’attente s’allonge.
La solution ? Les Worker threads et la mise à l’échelle verticale
Bon, maintenant que vous avez compris le problème, voici la première solution : les Worker threads. L’idée est de déléguer le code JavaScript gourmand en ressources CPU à ces threads séparés, pendant que le thread principal continue à gérer les requêtes entrantes. (Un peu comme si maman vous avait fait faire vos devoirs pendant qu’elle cuisinait.)
Cette approche, qu’on appelle la mise à l’échelle verticale , consiste à ajouter plus de ressources (comme de la RAM ou des cœurs de processeur) à votre serveur existant. Avec les Worker threads, vous pouvez exploiter ces ressources supplémentaires pour exécuter du code JavaScript intensif en parallèle du thread principal.
Le module worker_threads
de Node.js vous permet de mettre en place cette solution. Reste à identifier les portions de code gourmandes en ressources et à les isoler pour les faire tourner dans ces threads dédiés. (Un peu comme quand vous deviez ranger votre chambre pendant que maman faisait le ménage ailleurs.)
Les avantages de la mise à l’échelle verticale
- Vous pouvez exploiter tous les cœurs de processeur de votre machine.
- Le thread principal reste dégagé pour répondre à davantage de requêtes.
- Vous n’avez pas besoin de déployer d’autres serveurs (pour le moment).
Quand la verticalité ne suffit plus : la mise à l’échelle horizontale
Mais que se passe-t-il si, malgré la mise à l’échelle verticale, votre application continue à être submergée par les requêtes ? C’est là qu’intervient la mise à l’échelle horizontale . L’idée est simple : au lieu d’ajouter des ressources à un seul serveur, vous déployez plusieurs instances de votre application Node.js sur différentes machines. (Un peu comme si vous aviez plusieurs mamans pour vous servir.)
Un équilibreur de charge se charge alors de répartir les requêtes entrantes entre ces différentes instances. Plus vous avez d’instances, plus vous pouvez traiter de requêtes en parallèle. (Comme si vous aviez une armée de mamans à votre service.)
Avec la mise à l’échelle horizontale, vous utilisez tous les cœurs de processeurs disponibles sur plusieurs machines.
Cette approche présente plusieurs avantages :
- Vous n’avez pas besoin d’identifier le code JavaScript intensif ni de le séparer dans des Worker threads.
- Vous pouvez facilement ajouter ou supprimer des instances en fonction de la charge.
- Vous bénéficiez d’une tolérance aux pannes accrue, car si une instance tombe en panne, les autres peuvent prendre le relais.
Le cloud, ami de la mise à l’échelle horizontale
La mise à l’échelle horizontale se marie à merveille avec le cloud. Sur des plateformes comme AWS, Google Cloud ou Azure, vous pouvez provisionner (ou déprovisionner) des machines virtuelles d’un simple clic. Ainsi, vous ajustez votre capacité en fonction de la demande, sans avoir à investir dans du matériel coûteux. (Un peu comme si vous pouviez engager ou renvoyer des mamans à la volée.)
Bien sûr, pour tirer parti de cette flexibilité, il faut mettre en place une architecture adaptée, avec un équilibreur de charge et un mécanisme de déploiement automatisé. Mais ne vous inquiétez pas, on va y venir !
Gérer les données de session dans une configuration mise à l’échelle
Avant d’aller plus loin, il faut aborder un point crucial : la gestion des données spécifiques à une session. Quand vous avez plusieurs instances de votre application qui tournent, vous devez vous assurer que les requêtes consécutives d’une même session soient traitées par n’importe quelle instance, sans préférence.
Donc, interdiction de stocker les données de session dans la mémoire de vos instances Node.js . Sinon, vous seriez obligé de router toutes les requêtes d’une session vers la même instance, annulant ainsi les bénéfices de la mise à l’échelle. (Un peu comme si vous deviez toujours revenir vers la même maman pour avoir votre part de gâteau.)
La solution ? Utiliser un magasin de données partagé, accessible par toutes vos instances. Une base de données comme PostgreSQL ou MySQL, ou un magasin en mémoire comme Memcached ou Redis, fera parfaitement l’affaire. Ainsi, n’importe quelle instance peut récupérer (ou stocker) les données de session depuis (ou vers) ce magasin commun.
Gérer votre configuration à grande échelle avec les bons outils
Enfin, dernière étape : choisir les bons outils pour gérer votre configuration mise à l’échelle. En fonction de vos besoins et de votre niveau d’expertise, vous avez plusieurs options :
Nginx + systemd
Cette combinaison est idéale si vous n’avez pas besoin d’une configuration évolutive (horizontalement) ni d’une automatisation poussée. Nginx jouera le rôle d’équilibreur de charge, tandis que systemd se chargera de gérer les processus Node.js (redémarrage en cas de crash, gestion des logs, etc.).
PM2
PM2 est une solution tout-en-un basée sur Node.js. Elle vous permet de faire de l’équilibrage de charge, de démarrer/arrêter vos instances, et de gérer les logs. Très pratique si vous n’avez pas besoin d’une configuration évolutive ni d’automatisation DevOps.
Docker + services cloud
Si vous visez une configuration hautement évolutive et automatisée, Docker est votre allié. Vous packagez votre application Node.js dans des conteneurs, que vous déployez sur des machines virtuelles (provisionées automatiquement par votre service cloud). Un service d’équilibrage de charge, comme AWS Application Load Balancer, se charge de répartir le trafic entre vos instances.
L’automatisation DevOps vous permet de créer (ou détruire) des machines virtuelles à la volée, en fonction de la charge. Une belle manière de n’avoir que le nombre d’instances strictement nécessaire à tout moment. (Un peu comme si vous pouviez engager ou renvoyer des mamans en un clin d’œil.)
Le fin mot de l’histoire
En fin de compte, il n’y a pas de solution miracle pour mettre à l’échelle vos applications Node.js. Tout dépend de vos besoins, de votre budget et de votre niveau d’expertise. Si vous débutez, commencez par quelque chose de simple comme Nginx + systemd ou PM2. Si vous visez les sommets, foncez sur Docker et l’automatisation cloud.
Mais n’oubliez pas l’essentiel : identifier les goulots d’étranglement, séparer le code intensif des tâches de routines, et mettre en place une architecture résiliente qui vous permettra de faire face à la croissance. Avec les bonnes pratiques (et un peu d’humour), vous allez monter en charge comme une fusée ! (Ou comme une pile de gâteaux, au choix.)
Le plus important à retenir
- Node.js traite les requêtes de manière séquentielle, ce qui peut causer des goulots d’étranglement.
- La mise à l’échelle verticale (avec les Worker threads) permet d’exécuter du code JavaScript intensif en parallèle.
- La mise à l’échelle horizontale (avec plusieurs instances) répartit la charge sur plusieurs machines.
- Il faut utiliser un magasin de données partagé pour gérer les données de session dans une configuration mise à l’échelle.
- Des outils comme Nginx, PM2 ou Docker permettent de gérer une configuration à grande échelle.