Node.js est performant en matière de concurrence d'I/O. Il n'est pas performant à faire semblant que le travail CPU est asynchrone simplement parce que la fonction gestionnaire utilise `async`.
Cette distinction est importante car de nombreux incidents de performance ne proviennent pas de bases de données lentes ou de réseaux lents. Ils proviennent d'une requête effectuant trop de travail synchrone pendant que chaque autre requête attend derrière.
## L'Exemple Trompeur
Cette route semble inoffensive à première vue :
```ts
app.post("/webhook", async (req, res) => {
const payload = JSON.parse(req.body.raw);
await database.save(payload);
res.send("ok");
});
L'appel à la base de données est asynchrone. JSON.parse ne l'est pas.
Si le payload est énorme, l'analyse se fait sur le fil principal, et la boucle d'événements ne peut pas continuer à gérer d'autres requêtes entrantes jusqu'à ce que ce travail soit terminé.
Ce Qui Bloque Réellement
Dans les services Node réels, les coupables habituels sont :
- des appels énormes à
JSON.parse et JSON.stringify
- la génération d'images ou de PDF
- le travail cryptographique effectué au mauvais endroit
- de grands travaux de système de fichiers synchrones
- des boucles regex ou de transformation coûteuses
La solution n'est pas "rendre tout asynchrone." La solution consiste à déplacer ou remodeler le travail.
Meilleures Options
Lorsque cela est possible :
- utilisez des flux au lieu de mettre en mémoire tampon de gros payloads
- utilisez des travailleurs pour les tâches lourdes en CPU
- déplacez les transformations coûteuses hors des chemins de requêtes critiques
Par exemple, les threads de travail constituent souvent une frontière plus propre pour les opérations lourdes en calcul :
import { Worker } from "node:worker_threads";
export function runHeavyTask(input: unknown) {
return new Promise((resolve, reject) => {
const worker = new Worker(new URL("./worker.js", import.meta.url), {
workerData: input,
});
worker.once("message", resolve);
worker.once("error", reject);
});
}
Cela ne rend pas le travail moins cher. Cela permet de garder la boucle d'événements réactive pendant que le travail s'effectue ailleurs.
Le Compromis
Node est toujours un bon choix pour de nombreux systèmes backend. Vous devez simplement respecter le modèle d'exécution. Si votre service passe la plupart de son temps à attendre sur l'I/O, Node s'intègre naturellement. S'il passe la plupart de son temps à traiter des données sur le CPU, vous aurez besoin d'une isolation plus délibérée.
Lectures Complémentaires