Node.js em máquinas com vários núcleos

606

O Node.js parece interessante, mas devo perder alguma coisa - o Node.js não está sintonizado apenas para executar em um único processo e thread?

Então, como é dimensionado para CPUs com vários núcleos e servidores com várias CPUs? Afinal, é ótimo tornar o servidor de thread único o mais rápido possível, mas para cargas altas eu gostaria de usar várias CPUs. E o mesmo vale para tornar os aplicativos mais rápidos - parece hoje o caminho para usar várias CPUs e paralelizar as tarefas.

Como o Node.js se encaixa nessa imagem? A ideia é distribuir de alguma forma várias instâncias ou o quê?

zaharpopov
fonte
4
Parece que Ryah está começando a ficar sério sobre incluindo suporte integrado multi-core no nó: github.com/joyent/node/commit/...
broofa
2
Módulo aglomerado uso gerenciador de processos PM2 internamente para espalhar suas NodeJS aplicativos para todos os núcleos disponíveis: github.com/Unitech/pm2
Unitech
@broofa, esses não são threads reais e os processos filhos não têm memória compartilhada. Veja também Qual é o equivalente do Nodejs às variáveis ​​reais de encadeamento e estática volátil do Java? .
Pacerier 28/02

Respostas:

697

[ Esta postagem está atualizada em 02/09/2012 (mais recente que acima). ]

O Node.js é totalmente escalável em máquinas com vários núcleos.

Sim, o Node.js é um thread por processo. Essa é uma decisão de projeto muito deliberada e elimina a necessidade de lidar com a semântica de bloqueio. Se você não concorda com isso, provavelmente ainda não percebe o quão insanamente difícil é depurar código multiencadeado. Para uma explicação mais aprofundada do modelo de processo do Node.js. e por que ele funciona dessa maneira (e por que NUNCA suporta vários threads), leia meu outro post .

Então, como aproveito minha caixa de 16 núcleos?

Dois caminhos:

  • Para grandes tarefas de computação pesada, como codificação de imagens, o Node.js pode iniciar processos filho ou enviar mensagens para processos de trabalho adicionais. Nesse design, você teria um thread gerenciando o fluxo de eventos e N processos executando tarefas pesadas de computação e mastigando as outras 15 CPUs.
  • Para dimensionar a taxa de transferência em um serviço da Web, você deve executar vários servidores Node.js. em uma caixa, uma por núcleo e dividir o tráfego de solicitação entre eles. Isso fornece excelente afinidade da CPU e aumentará a produtividade quase linearmente com a contagem de núcleos.

Escalando a taxa de transferência em um serviço da web

Desde a v6.0.X, o Node.js incluiu o módulo de cluster imediatamente, o que facilita a configuração de vários trabalhadores do nó que podem escutar em uma única porta. Observe que este NÃO é o mesmo que o módulo "cluster" do learnboost mais antigo disponível no npm .

if (cluster.isMaster) {
  // Fork workers.
  for (var i = 0; i < numCPUs; i++) {
    cluster.fork();
  }
} else {
  http.Server(function(req, res) { ... }).listen(8000);
}

Os trabalhadores competirão para aceitar novas conexões, e o processo menos carregado provavelmente vencerá. Funciona muito bem e pode aumentar bastante a produtividade em uma caixa com vários núcleos.

Se você tiver carga suficiente para se preocupar com vários núcleos, também precisará fazer mais algumas coisas:

  1. Execute o serviço Node.js atrás de um proxy da Web como Nginx ou Apache - algo que pode acelerar a conexão (a menos que você queira que as condições de sobrecarga reduzam completamente a caixa), reescreva URLs, sirva conteúdo estático e faça proxy de outros sub-serviços.

  2. Recicle periodicamente seus processos de trabalho. Para um processo de longa execução, até um pequeno vazamento de memória acabará aumentando.

  3. Coleta / monitoramento de logs de instalação


PS: Há uma discussão entre Aaron e Christopher nos comentários de outro post (até o momento em que este artigo foi escrito, é o post principal). Alguns comentários sobre isso:

  • Um modelo de soquete compartilhado é muito conveniente para permitir que vários processos escutem em uma única porta e competam para aceitar novas conexões. Conceitualmente, você pode pensar no Apache pré-bifurcado fazendo isso com a ressalva significativa de que cada processo só aceita uma única conexão e depois morre. A perda de eficiência do Apache está na sobrecarga de criar novos processos e não tem nada a ver com as operações do soquete.
  • Para o Node.js, fazer com que N trabalhadores concorram em um único soquete é uma solução extremamente razoável. A alternativa é configurar um front-end na caixa, como o Nginx, e ter esse tráfego proxy para os trabalhadores individuais, alternando entre os trabalhadores para atribuir novas conexões. As duas soluções têm características de desempenho muito semelhantes. E como, como mencionei acima, você provavelmente desejará que o Nginx (ou uma alternativa) esteja de frente para o serviço do nó, a escolha aqui é realmente entre:

Portas compartilhadas: nginx (port 80) --> Node_workers x N (sharing port 3000 w/ Cluster)

vs

Portas individuais: nginx (port 80) --> {Node_worker (port 3000), Node_worker (port 3001), Node_worker (port 3002), Node_worker (port 3003) ...}

É possível que haja alguns benefícios na configuração de portas individuais (potencial para ter menos acoplamento entre processos, tomar decisões de balanceamento de carga mais sofisticadas etc.), mas é definitivamente mais trabalho para configurar e o módulo de cluster embutido é baixo. alternativa de complexidade que funciona para a maioria das pessoas.

Dave Dopson
fonte
1
você pode oferecer algum conselho para executar diferentes serviços baseados em nodejs em uma única caixa? Por exemplo, digamos que eu tenho 1 servidor e quero executar myservice1.js no CpuCore1 e myservice2.js no CpuCore2. Posso usar o cluster para isso? ou é útil apenas para criar serviços clonados?
UpTheCreek 20/09/2012
6
Você deve postar uma pergunta para isso! (e copiarei esse comentário como sua primeira resposta). O que você quer fazer é realmente muito simples. Você realmente não precisaria de "cluster", basta executar dois serviços de nó diferentes. Dois scripts, dois processos, duas portas. Por exemplo, você pode ter serviceA listen em 3000 e serviceB listen em 3001. Cada um desses serviços pode usar "cluster" para ter mais de 1 trabalhador e reciclá-los periodicamente, etc. Em seguida, você pode configurar o Nginx para escutar na porta 80 e encaminhar para o serviço correto com base no cabeçalho "Host" recebido e / ou no caminho da URL.
21412 Dave Dopson
1
Obrigado. Já postei uma pergunta relacionada - você descreveu praticamente o que eu tinha em mente, mas não tenho certeza sobre como direcionar núcleos de CPU (ao usar algo como sempre).
UpTheCreek 20/09/2012
Ótima resposta ddopson. Qual é a melhor maneira de fazer com que dois processos de nó se comuniquem na mesma máquina? Existe um protocolo mais rápido que o TCP quando eles estão na mesma máquina?
winduptoy
1
@Serob_b - bem, sim. A execução de um aplicativo Node.js em várias máquinas é muito comum. Não há biblioteca necessária para fazer isso. Você acabou de executar seu código em várias máquinas e distribuir a carga entre elas. Arquitetar seu software para que ele seja dimensionado (isto é, armazena o estado em algum tipo de serviço de dados externo, em vez de manter o estado na memória) - esse é o seu trabalho.
Dave Dopson
45

Um método seria executar várias instâncias do node.js no servidor e, em seguida, colocar um balanceador de carga (de preferência um não bloqueador como o nginx) na frente deles.

Chandra Sekar
fonte
36
node.js é quase tão rápido como nginx, você poderia colocar um balanceador de carga node.js na frente de seus servidores Node.js se você queria também :)
Mikeal
26
ryan disse especificamente não fazer isso até o nó ficar mais estável. A melhor maneira é executar o nginx na frente do nó.
resopollution 29/07
2
quanto ao nginx na frente do nó, ele não resolverá certos problemas, como se você tiver uma fila na memória. 2 instâncias do nó não poderão acessar a fila um do outro.
resopollution 29/07
5
Além disso, o nginx não suporta totalmente o HTTP 1.1, portanto, coisas como o WebSockets não podem ser enviadas por proxy.
ashchristopher
2
@mikeal, resopolution - eu estou fortemente do lado do Nginx. Fiz uma batida forte no Node.js várias vezes (sem rastreamento de pilha, apenas morre). Eu nunca bati no Nginx. O Nginx pronto para uso é configurado com todos os tipos de aceleradores sãos. Por padrão, o Node.js continuará aceitando novas conexões, em vez de servir as existentes, até que a caixa fique inativa ... sim, a caixa inteira; Eu bati o kernel em uma caixa do CentOS5 testando o nó do estresse (agora isso realmente não deveria acontecer). Cheguei um pouco e vejo um futuro brilhante para o Node, potencialmente incluindo funções dedicadas do tipo LB. Ainda não.
Dave Dopson
30

Ryan Dahl responde a essa pergunta na palestra sobre tecnologia que ele deu no Google no verão passado. Parafraseando, "basta executar processos de vários nós e usar algo sensato para permitir que eles se comuniquem. Por exemplo, IPC no estilo sendmsg () ou RPC tradicional".

Se você quiser sujar as mãos imediatamente, consulte o módulo spark2 Forever . Torna fácil a geração de múltiplos processos de nó. Ele lida com a configuração do compartilhamento de portas, para que cada um possa aceitar conexões com a mesma porta e também com reaparecimento automático se desejar garantir que um processo seja reiniciado se / quando morrer.

ATUALIZAÇÃO - 10/11/11 : O consenso na comunidade de nós parece ser que o Cluster agora é o módulo preferido para gerenciar várias instâncias de nós por máquina. Para sempre também vale a pena dar uma olhada.

broofa
fonte
8
Forever e Cluster fazem coisas muito diferentes. Você pode até usar os dois. Para sempre reinicia um processo quando morre. O cluster gerencia vários trabalhadores. Você usaria sempre para gerir o seu processo mestre ...
Dave Dopson
4
Também, o módulo LearnBoost é largamente suplantado pela versão de Cluster cozido em v0.6.x nó (aviso: superfície do API difere)
Dave Dopson
@broofa Como o IPC padrão é comparado com, digamos, usando Redis ou Memcache, apenas enviando string / dados / matrizes entre os processos? Qual caminho seria mais rápido?
NiCk Newman
1
@broofa, o IPC possui enormes custos indiretos em comparação à memória compartilhada real que Java e C são capazes de executar.
Pacerier 28/02
@Pacerier É verdade, mas a memória compartilhada apenas resolve o problema de como escalar no contexto de um único host, sem abordar os problemas de macro necessários para escalar em muitos hosts. Ou seja, como executar na nuvem.
broofa
20

Você pode usar o módulo de cluster . Verifique isso .

var cluster = require('cluster');
var http = require('http');
var numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
    // Fork workers.
    for (var i = 0; i < numCPUs; i++) {
        cluster.fork();
    }

    cluster.on('exit', function(worker, code, signal) {
        console.log('worker ' + worker.process.pid + ' died');
    });
} else {
    // Workers can share any TCP connection
    // In this case its a HTTP server
    http.createServer(function(req, res) {
        res.writeHead(200);
        res.end("hello world\n");
    }).listen(8000);
}
Sergey Zhukov
fonte
13

O nó múltiplo aproveita todos os núcleos que você pode ter.
Dê uma olhada em http://github.com/kriszyp/multi-node .

Para necessidades mais simples, você pode iniciar várias cópias do nó em diferentes números de porta e colocar um balanceador de carga na frente deles.

CyberFonic
fonte
12

O nó Js oferece suporte ao clustering para aproveitar todas as vantagens do seu processador. Se você não o está executando com cluster, provavelmente está desperdiçando seus recursos de hardware.

O armazenamento em cluster no Node.js permite criar processos separados que podem compartilhar a mesma porta do servidor. Por exemplo, se executarmos um servidor HTTP na porta 3000, ele será um servidor em execução no encadeamento único no núcleo único do processador.

O código mostrado abaixo permite agrupar seu aplicativo. Este código é um código oficial representado por Node.js.

var cluster = require('cluster');
var numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
    // Fork workers.
    for (var i = 0; i < numCPUs; i++) {
        cluster.fork();
    }

    Object.keys(cluster.workers).forEach(function(id) {
        console.log("I am running with ID : " + cluster.workers[id].process.pid);
    });

    cluster.on('exit', function(worker, code, signal) {
        console.log('worker ' + worker.process.pid + ' died');
    });
} else {

    //Do further processing.
}

verifique este artigo para o tutorial completo

Toumi
fonte
11

Como mencionado acima, o Cluster escalará e balanceará o aplicativo em todos os núcleos.

adicionando algo como

cluster.on('exit', function () {
  cluster.fork();
});

Reiniciará todos os trabalhadores com falha.

Hoje em dia, muitas pessoas também preferem PM2 , que lida com o armazenamento em cluster para você e também fornece alguns recursos interessantes de monitoramento .

Em seguida, adicione Nginx ou HAProxy na frente de várias máquinas em execução com clustering e você terá vários níveis de failover e uma capacidade de carga muito maior.

Will Stern
fonte
3
O PM2 é ótimo para uso em produção. As ferramentas de monitoramento me ajudaram a resolver problemas de memória com aplicativos.
mbokil
7

A versão futura do nó permitirá que você bifurque um processo e passe mensagens para ele, e Ryan declarou que deseja encontrar uma maneira de também compartilhar manipuladores de arquivos, para que não seja uma implementação direta do Web Worker.

No momento, não há uma solução fácil para isso, mas ainda é muito cedo, e o node é um dos projetos de código aberto em movimento mais rápido que eu já vi, então, espere algo incrível no futuro próximo.

mikeal
fonte
7

O Spark2 é baseado no Spark, que agora não é mais mantido. O cluster é seu sucessor e possui alguns recursos interessantes, como gerar um processo de trabalho por núcleo de CPU e reaparecer trabalhadores mortos.

O desenvolvedor
fonte
A pergunta original e muitas dessas respostas têm alguns meses e o nó está se movendo tão rápido que eu agradeço por adicionar o resumo sobre o Cluster. Depois de examinar o Cluster e seus exemplos, ele se parece exatamente com o que eu (ou o OP?) Deseja para o Node, obrigado!
Riyad Kalla
5

Estou usando o operador Node para executar processos de maneira simples a partir do meu processo principal. Parece estar funcionando muito bem enquanto esperamos a maneira oficial de chegar.

christkv
fonte
1
por que o node worker example.js não pode ser executado, meu nó é a versão 0.3.3 anterior
guilin #:
5

O novo garoto da quadra aqui é o "Up" do LearnBoost .

Ele fornece "recargas de tempo de inatividade zero" e, adicionalmente, cria vários trabalhadores (por padrão, o número de CPUs, mas é configurável) para fornecer o melhor de todos os mundos.

É novo, mas parece bastante estável, e estou usando-o alegremente em um dos meus projetos atuais.

Roy
fonte
5

O módulo de cluster permite que você utilize todos os núcleos da sua máquina. De fato, você pode tirar proveito disso em apenas 2 comandos e sem tocar no seu código usando um gerenciador de processos muito popular pm2 .

npm i -g pm2
pm2 start app.js -i max
Alister
fonte
4

Você pode executar o aplicativo node.js em vários núcleos usando o módulo cluster em combinação com os módulo , que pode ser usado para detectar quantas CPUs você possui.

Por exemplo, vamos imaginar que você tem um servermódulo que executa um servidor http simples no back-end e deseja executá-lo em várias CPUs:

// Dependencies.
const server = require('./lib/server'); // This is our custom server module.
const cluster = require('cluster');
const os = require('os');

 // If we're on the master thread start the forks.
if (cluster.isMaster) {
  // Fork the process.
  for (let i = 0; i < os.cpus().length; i++) {
    cluster.fork();
  }
} else {
  // If we're not on the master thread start the server.
  server.init();
}

Oleksii Trekhleb
fonte
0

Também é possível projetar o serviço da Web como vários servidores independentes que escutam soquetes unix, para que você possa colocar funções como processamento de dados em processos separados.

Isso é semelhante à maioria das arquiteturas de servidor da Web de scrpting / banco de dados, em que um processo cgi manipula a lógica de negócios e, em seguida, envia e extrai os dados por meio de um soquete unix para um banco de dados.

a diferença é que o processamento de dados é gravado como um servidor da web de nó escutando em uma porta.

é mais complexo, mas, em última análise, é para onde o desenvolvimento multinúcleo deve ir. uma arquitetura multiprocessos usando vários componentes para cada solicitação da web.

Corvo de fogo
fonte
0

É possível dimensionar o NodeJS para várias caixas usando um balanceador de carga TCP puro (HAProxy) na frente de várias caixas executando um processo NodeJS cada.

Se você tiver algum conhecimento comum para compartilhar entre todas as instâncias, poderá usar um repositório central Redis ou similar, que poderá ser acessado de todas as instâncias do processo (por exemplo, de todas as caixas)

Martin Tajur
fonte
A menos que você tenha CPUs de núcleo único nesses servidores, isso não utilizará toda a sua capacidade de CPU (a menos que você esteja fazendo outra coisa também).
UpTheCreek 20/09/2012