J'ai commencé à bricoler avec le serveur HTTP Node.js et j'aime vraiment écrire du Javascript côté serveur mais quelque chose m'empêche de commencer à utiliser Node.js pour mon application Web.
Je comprends tout le concept d'E / S asynchrone, mais je suis quelque peu préoccupé par les cas extrêmes où le code procédural est très consommateur d'UC, comme la manipulation d'images ou le tri de grands ensembles de données.
Si je comprends bien, le serveur sera très rapide pour les demandes de pages Web simples telles que l'affichage d'une liste d'utilisateurs ou l'affichage d'un article de blog. Cependant, si je veux écrire du code très gourmand en CPU (dans le back-end administrateur par exemple) qui génère des graphiques ou redimensionne des milliers d'images, la requête sera très lente (quelques secondes). Étant donné que ce code n'est pas asynchrone, toutes les demandes envoyées au serveur pendant ces quelques secondes seront bloquées jusqu'à ce que ma demande lente soit terminée.
Une suggestion était d'utiliser Web Workers pour des tâches gourmandes en CPU. Cependant, je crains que les travailleurs Web ne rendent difficile l'écriture de code propre car cela fonctionne en incluant un fichier JS distinct. Que faire si le code gourmand en CPU se trouve dans la méthode d'un objet? Cela craint d'écrire un fichier JS pour chaque méthode qui consomme beaucoup de CPU.
Une autre suggestion était de générer un processus enfant, mais cela rend le code encore moins maintenable.
Avez-vous des suggestions pour surmonter cet obstacle (perçu)? Comment écrivez-vous du code orienté objet propre avec Node.js tout en vous assurant que les tâches lourdes du processeur sont exécutées en mode asynchrone?
la source
Réponses:
Ce dont vous avez besoin, c'est d'une file d'attente de tâches! Déplacer vos tâches de longue durée hors du serveur Web est une BONNE chose. Conserver chaque tâche dans un fichier js "séparé" favorise la modularité et la réutilisation du code. Cela vous oblige à réfléchir à la façon de structurer votre programme de manière à faciliter le débogage et la maintenance à long terme. Un autre avantage d'une file d'attente de tâches est que les travailleurs peuvent être écrits dans une langue différente. Passez simplement une tâche, faites le travail et écrivez la réponse.
quelque chose comme ça https://github.com/resque/resque
Voici un article de github expliquant pourquoi ils l'ont construit http://github.com/blog/542-introducing-resque
la source
Il s'agit d'une incompréhension de la définition de serveur Web - il ne doit être utilisé que pour "parler" avec les clients. Les tâches à forte charge doivent être déléguées à des programmes autonomes (qui bien sûr peuvent également être écrits en JS).
Vous diriez probablement qu'il est sale, mais je vous assure qu'un processus de serveur Web bloqué dans le redimensionnement des images est tout simplement pire (même pour, disons Apache, quand il ne bloque pas les autres requêtes). Néanmoins, vous pouvez utiliser une bibliothèque commune pour éviter la redondance de code.
EDIT: J'ai trouvé une analogie; l'application Web doit être un restaurant. Vous avez des serveurs (serveur web) et des cuisiniers (travailleurs). Les serveurs sont en contact avec les clients et effectuent des tâches simples comme fournir un menu ou expliquer si un plat est végétarien. En revanche, ils délèguent des tâches plus difficiles à la cuisine. Parce que les serveurs ne font que des choses simples, ils répondent rapidement et les cuisiniers peuvent se concentrer sur leur travail.
Node.js serait ici un serveur unique mais très talentueux qui peut traiter de nombreuses demandes à la fois, et Apache serait un gang de serveurs stupides qui ne traitent qu'une seule demande chacun. Si ce serveur Node.js commençait à cuisiner, ce serait une catastrophe immédiate. Pourtant, la cuisson pourrait également épuiser même une grande quantité de serveurs Apache, sans parler du chaos dans la cuisine et de la diminution progressive de la réactivité.
la source
Vous ne voulez pas que votre code gourmand en CPU exécute async, vous voulez qu'il s'exécute en parallèle . Vous devez extraire le travail de traitement du thread qui sert les requêtes HTTP. C'est le seul moyen de résoudre ce problème. Avec NodeJS, la réponse est le module de cluster, pour engendrer des processus enfants pour faire le gros du travail. (AFAIK Node n'a pas de concept de threads / mémoire partagée; c'est des processus ou rien). Vous disposez de deux options pour structurer votre application. Vous pouvez obtenir la solution 80/20 en générant 8 serveurs HTTP et en gérant de manière synchrone les tâches à forte intensité de calcul sur les processus enfants. Faire cela est assez simple. Vous pourriez prendre une heure pour en savoir plus sur ce lien. En fait, si vous extrayez simplement l'exemple de code en haut de ce lien, vous obtiendrez 95% du chemin.
L'autre façon de structurer cela est de configurer une file d'attente de travaux et d'envoyer de grandes tâches de calcul sur la file d'attente. Notez qu'il y a beaucoup de surcharge associée à l'IPC pour une file d'attente de travaux, donc cela n'est utile que lorsque les tâches sont sensiblement plus grandes que la surcharge.
Je suis surpris qu'aucune de ces autres réponses ne mentionne même le cluster.
Contexte: Le code asynchrone est un code qui se suspend jusqu'à ce que quelque chose se produise ailleurs , auquel cas le code se réveille et continue son exécution. Un E / S est un cas très courant où quelque chose de lent doit se produire ailleurs.
Le code asynchrone n'est pas utile si c'est votre processeur qui est responsable de faire le travail. C'est précisément le cas des tâches "intensives en calcul".
Maintenant, il peut sembler que le code asynchrone est une niche, mais en fait, il est très courant. Il se trouve que cela n'est pas utile pour les tâches de calcul intensives.
L'attente sur les E / S est un modèle qui se produit toujours dans les serveurs Web, par exemple. Chaque client qui se connecte à votre serveur obtient un socket. La plupart du temps, les prises sont vides. Vous ne voulez rien faire jusqu'à ce qu'un socket reçoive des données, moment auquel vous souhaitez gérer la demande. Sous le capot, un serveur HTTP comme Node utilise une bibliothèque d'événements (libev) pour garder une trace des milliers de sockets ouverts. Le système d'exploitation notifie libev, puis libev avertit NodeJS lorsque l'un des sockets obtient des données, puis NodeJS place un événement dans la file d'attente d'événements, et votre code http entre en action à ce stade et gère les événements les uns après les autres. Les événements ne sont pas mis dans la file d'attente jusqu'à ce que le socket ait des données, donc les événements n'attendent jamais de données - il est déjà là pour eux.
Les serveurs Web basés sur des événements à thread unique ont du sens comme paradigme lorsque le goulot d'étranglement attend sur un tas de connexions de socket principalement vides et que vous ne voulez pas un thread ou un processus entier pour chaque connexion inactive et que vous ne voulez pas interroger votre 250k sockets pour trouver le prochain qui contient des données.
la source
Quelques approches que vous pouvez utiliser.
Comme le note @Tim, vous pouvez créer une tâche asynchrone qui se trouve à l'extérieur ou parallèlement à votre logique de desserte principale. Cela dépend de vos besoins exacts, mais même cron peut agir comme un mécanisme de mise en file d'attente.
Les WebWorkers peuvent fonctionner pour vos processus asynchrones mais ils ne sont actuellement pas pris en charge par node.js. Il existe quelques extensions qui fournissent un support, par exemple: http://github.com/cramforce/node-worker
Vous obtenez toujours que vous pouvez toujours réutiliser les modules et le code via le mécanisme standard "requiert". Il vous suffit de vous assurer que l'envoi initial au travailleur transmet toutes les informations nécessaires au traitement des résultats.
la source
L'utilisation
child_process
est une solution. Mais chaque processus enfant généré peut consommer beaucoup de mémoire par rapport à Gogoroutines
Vous pouvez également utiliser une solution basée sur la file d'attente telle que kue
la source