Comecei a mexer no servidor HTTP Node.js. e gosto muito de escrever Javascript no servidor, mas algo está me impedindo de começar a usar o Node.js. para meu aplicativo da web.
Entendo todo o conceito de E / S assíncrona, mas estou um pouco preocupado com os casos extremos em que o código processual consome muita CPU, como manipulação de imagem ou classificação de grandes conjuntos de dados.
Pelo que entendi, o servidor será muito rápido para solicitações simples de páginas da web, como exibir uma lista de usuários ou exibir uma postagem no blog. No entanto, se eu quiser escrever um código muito intensivo da CPU (no back-end do administrador, por exemplo) que gere gráficos ou redimensione milhares de imagens, a solicitação será muito lenta (alguns segundos). Como esse código não é assíncrono, todas as solicitações que chegam ao servidor durante esses poucos segundos serão bloqueadas até que minha solicitação lenta seja concluída.
Uma sugestão foi usar Web Workers para tarefas intensivas da CPU. No entanto, receio que os funcionários da Web tornem difícil escrever código limpo, pois ele funciona incluindo um arquivo JS separado. E se o código intensivo da CPU estiver localizado no método de um objeto? É meio ruim escrever um arquivo JS para cada método que consome muita CPU.
Outra sugestão foi gerar um processo filho, mas isso torna o código ainda menos sustentável.
Alguma sugestão para superar esse obstáculo (percebido)? Como você escreve um código orientado a objeto limpo com o Node.js enquanto garante que tarefas pesadas da CPU sejam executadas de forma assíncrona?
fonte
Respostas:
O que você precisa é de uma fila de tarefas! Mover suas tarefas de longa execução para fora do servidor da web é uma coisa BOM. Manter cada tarefa em um arquivo js "separado" promove a modularidade e a reutilização de código. Obriga você a pensar em como estruturar seu programa de maneira a facilitar a depuração e a manutenção a longo prazo. Outro benefício de uma fila de tarefas é que os trabalhadores podem ser escritos em um idioma diferente. Basta executar uma tarefa, fazer o trabalho e escrever a resposta de volta.
algo assim https://github.com/resque/resque
Aqui está um artigo do github sobre por que eles o criaram http://github.com/blog/542-introducing-resque
fonte
Isso é um mal-entendido da definição de servidor da Web - ele deve ser usado apenas para "conversar" com os clientes. Tarefas de carga pesada devem ser delegadas a programas independentes (é claro que também podem ser escritos em JS).
Você provavelmente diria que está sujo, mas garanto que um processo de servidor da Web preso ao redimensionamento de imagens é apenas pior (até digamos o Apache, quando não bloqueia outras consultas). Ainda assim, você pode usar uma biblioteca comum para evitar redundância de código.
EDIT: Eu propus uma analogia; aplicação web deve ser como um restaurante. Você tem garçons (servidor web) e cozinheiros (trabalhadores). Os garçons estão em contato com os clientes e realizam tarefas simples, como fornecer menu ou explicar se algum prato é vegetariano. Por outro lado, eles delegam tarefas mais difíceis à cozinha. Como os garçons estão fazendo apenas coisas simples, eles respondem rapidamente e os cozinheiros podem se concentrar em seu trabalho.
O Node.js aqui seria um garçom único, mas muito talentoso, capaz de processar muitas solicitações de cada vez, e o Apache seria um bando de garçons burros que apenas processam uma solicitação cada. Se esse garçom do Node.js começasse a cozinhar, seria uma catástrofe imediata. Ainda assim, o cozimento também pode esgotar até uma grande quantidade de garçons Apache, sem mencionar o caos na cozinha e a diminuição progressiva da responsividade.
fonte
Você não deseja que o código intensivo da CPU execute assíncrono, mas que ele seja executado em paralelo . Você precisa obter o trabalho de processamento do encadeamento que atende solicitações HTTP. É a única maneira de resolver esse problema. Com o NodeJS, a resposta é o módulo de cluster, para gerar processos filhos para fazer o trabalho pesado. (O Nó AFAIK não tem nenhum conceito de threads / memória compartilhada; é processos ou nada). Você tem duas opções para estruturar seu aplicativo. Você pode obter a solução 80/20 gerando 8 servidores HTTP e lidando com tarefas intensivas em computação de forma síncrona nos processos filhos. Fazer isso é bastante simples. Você pode levar uma hora para ler sobre isso nesse link. De fato, se você apenas copiar o código de exemplo na parte superior desse link, terá 95% do caminho até lá.
A outra maneira de estruturar isso é configurar uma fila de tarefas e enviar grandes tarefas de computação pela fila. Observe que há muita sobrecarga associada ao IPC para uma fila de trabalhos, portanto isso é útil apenas quando as tarefas são sensivelmente maiores que a sobrecarga.
Estou surpreso que nenhuma dessas outras respostas sequer mencione cluster.
Antecedentes: código assíncrono é um código que é suspenso até que algo aconteça em outro lugar ; nesse momento, o código é ativado e continua a execução. Um caso muito comum em que algo lento deve acontecer em outro lugar é a E / S.
O código assíncrono não é útil se o seu processador for responsável por fazer o trabalho. Esse é precisamente o caso das tarefas "intensivas em computação".
Agora, pode parecer que o código assíncrono é um nicho, mas na verdade é muito comum. Acontece que não é útil para tarefas intensivas de computação.
Esperar na E / S é um padrão que sempre acontece em servidores da web, por exemplo. Todo cliente que se conecta ao seu servidor recebe um soquete. Na maioria das vezes, os soquetes estão vazios. Você não deseja fazer nada até que um soquete receba alguns dados; nesse momento, você deseja manipular a solicitação. Um servidor HTTP como o Node está usando uma biblioteca de eventos (libev) para acompanhar os milhares de soquetes abertos. O sistema operacional notifica a libev e, em seguida, libev notifica o NodeJS quando um dos soquetes obtém dados e, em seguida, o NodeJS coloca um evento na fila de eventos, e seu código http entra em ação nesse momento e manipula os eventos um após o outro. Os eventos não são colocados na fila até que o soquete tenha alguns dados; portanto, os eventos nunca esperam pelos dados - eles já estão lá.
Servidores da Web baseados em eventos com thread único fazem sentido como um paradigma quando o gargalo aguarda várias conexões de soquete vazias e você não deseja um thread ou processo inteiro para cada conexão inativa e não deseja pesquisar seus 250k soquetes para encontrar o próximo que contém dados.
fonte
Algumas abordagens que você pode usar.
Como o @Tim observa, você pode criar uma tarefa assíncrona que fica do lado de fora ou paralela à sua lógica de exibição principal. Depende de seus requisitos exatos, mas até o cron pode atuar como um mecanismo de enfileiramento.
Os WebWorkers podem trabalhar para seus processos assíncronos, mas atualmente eles não são suportados pelo node.js. Existem algumas extensões que fornecem suporte, por exemplo: http://github.com/cramforce/node-worker
Você ainda pode reutilizar módulos e códigos através do mecanismo padrão "requer". Você só precisa garantir que o despacho inicial para o trabalhador passe todas as informações necessárias para processar os resultados.
fonte
O uso
child_process
é uma solução. Mas cada processo filho gerado pode consumir muita memória em comparação com o Gogoroutines
Você também pode usar soluções baseadas em fila, como kue
fonte