node.js, mongodb, redis, na degradação do desempenho do ubuntu na produção, a RAM é livre, CPU 100%


11

Como o título da pergunta sugere, estou tendo dificuldades para descobrir o que pode ser melhorado no meu aplicativo (ou sintonizado no sistema operacional ubuntu) para obter um desempenho aceitável. Mas primeiro vou explicar a arquitetura:

O servidor front-end é uma máquina de 8 núcleos com 8 GB de RAM executando o Ubuntu 12.04. O aplicativo é gravado inteiramente em javascript e executado no node.js v 0.8.22 (como alguns módulos parecem reclamar em versões mais recentes do nó) eu uso o nginx 1.4 para proxy do tráfego http das portas 80 e 443 a 8 trabalhadores de nó gerenciados e começou a usar a API do cluster de nós. Eu uso a versão mais recente do socket.io 0.9.14 para lidar com as conexões do websocket, nas quais habilitei apenas websockets e xhr-polling como transportes disponíveis. Nesta máquina, também executo uma instância do Redis (2.2)

Eu armazeno dados persistentes (como usuários e pontuações) em um segundo servidor no mongodb (3.6) com 4gigs de RAM e 2 núcleos.

O aplicativo está em produção há alguns meses (está sendo executado em uma única caixa até algumas semanas atrás) e está sendo usado por cerca de 18 mil usuários por dia. Sempre funcionou muito bem além de um problema principal: degradação do desempenho. Com o uso, a quantidade de CPU usada por cada processo aumenta até que ele estatura o trabalhador (que não atende mais a solicitações). Resolvi-o temporariamente, verificando a cpu em uso por cada trabalhador a cada minuto e reiniciando-a se atingir 98%. Portanto, o problema aqui é principalmente CPU, e não RAM. A RAM não é mais um problema, pois eu atualizei para o socket.io 0.9.14 (a versão anterior estava com vazamento de memória), por isso duvido que seja um problema de vazamento de memória, principalmente porque agora é a CPU que cresce rapidamente ( Eu tenho que reiniciar cada trabalhador cerca de 10 a 12 vezes por dia!). A RAM em uso também cresce para ser honesto, mas bem devagar, 1 show a cada 2-3 dias de uso, e o mais estranho é que ele não é lançado, mesmo quando eu reinicio completamente o aplicativo. Só é lançado se eu reiniciar o servidor! isso eu realmente não consigo entender ...

Agora eu descobri o que é incrível, então finalmente posso ver o que está acontecendo no meu servidor de produção e coleto dados há alguns dias. Se alguém quiser ver os gráficos, posso dar acesso, mas basicamente vejo que tenho entre 80 e 200 conexões simultâneas! Eu esperava que o node.js manipulasse milhares, não centenas de solicitações. Além disso, o tempo médio de resposta para o tráfego http varia entre 500 e 1500 milissegundos, o que eu acho realmente muito. Além disso, neste exato momento, com 1300 usuários on-line, esta é a saída de "ss -s":

Total: 5013 (kernel 5533)
TCP:   8047 (estab 4788, closed 3097, orphaned 139, synrecv 0, timewait 3097/0), ports 0

Transport Total     IP        IPv6
*         5533      -         -
RAW       0         0         0
UDP       0         0         0
TCP       4950      4948      2
INET      4950      4948      2
FRAG      0         0         0

o que mostra que tenho muitas conexões fechadas em tempo de espera. Aumentei o máximo de arquivos abertos para 999999, eis a saída do ulimit -a:

core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 63724
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 999999
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 63724
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

Por isso, pensei que o problema poderia estar no tráfego http que, por alguns motivos, satura as portas / soquetes disponíveis (?), Mas uma coisa não faz sentido para mim: por que, quando reinicio os trabalhadores e todos os clientes se reconectam em poucos segundos, a carga no processador do trabalhador diminui para 1% e é capaz de atender a solicitações corretamente até saturar após cerca de 1 hora (no horário de pico)?

Sou principalmente um programador de javascript, não um administrador de sistemas, por isso não sei quanta carga devo esperar para lidar com meus servidores, mas certamente não está funcionando como deveria. Caso contrário, a aplicação é estável e esse último problema está me impedindo de enviar as versões móveis do aplicativo que estão prontas, pois obviamente elas trarão mais carga e eventualmente travarão a coisa toda!

Espero que haja algo óbvio que eu esteja fazendo errado, e alguém ajude a identificá-lo ... sinta-se à vontade para me pedir mais informações e desculpe-me pela duração da pergunta, mas acredito que seja necessário ... desde já, obrigado!


Existe alguma maneira de obter algo como despejo de thread no node.js? Provavelmente existem alguns threads em um loop infinito. Além disso, o que realmente está usando a CPU? O que você vê topquando o uso da CPU é próximo de 100%?
precisa

A CPU é usada inteiramente pelo nodejs, quando eu corro o topo, vejo os processos do nó pegando toda a CPU. Não tenho certeza como eu posso emitir um despejo de thread do nó para ser honesto ...
Franjanko

outra coisa a questão é que a maioria do tempo da CPU parece ir para o sistema, e não o tempo do usuário
Franjanko

Alguém pelo menos sabe quantas conexões simultâneas devo ser capaz de lidar com os servidores que tenho instalados? No momento, eu apoio 200 conexões simultâneas no máximo. Isso me ajudará a estimar o quão longe estou de uma configuração ideal ... obrigado.
Franjanko

Respostas:


10

Após alguns dias de intensas tentativas e erros, fico feliz em poder dizer que entendi onde estava o gargalo e o publicarei aqui para que outras pessoas possam se beneficiar das minhas descobertas.

O problema está nas conexões pub / sub que eu estava usando com socket.io e, em particular, no RedisStore usado pelo socket.io para lidar com a comunicação entre processos de instâncias de soquete.

Depois de perceber que eu poderia implementar facilmente minha própria versão do pub / sub usando redis, decidi experimentá-lo e removi o redisStore do socket.io, deixando-o no armazenamento de memória padrão (não preciso transmitir para todos os clientes conectados, mas apenas entre 2 usuários diferentes, possivelmente conectados em processos diferentes)

Inicialmente, declarei apenas 2 conexões redis globais x processo para manipular o pub / sub em todos os clientes conectados, e o aplicativo estava usando menos recursos, mas eu ainda estava sendo afetado por um crescimento constante no uso da CPU, então não havia muita coisa mudada. Mas então eu decidi tentar criar 2 novas conexões para redis para cada cliente para lidar com seu pub / sub apenas em suas sessões e, em seguida, feche as conexões quando o usuário for desconectado. Depois de um dia de uso na produção, os processadores ainda estavam em 0-5% ... bingo! nenhum processo é reiniciado, não há bugs, com o desempenho que eu esperava ter. Agora posso dizer que o node.js é ótimo e estou feliz por tê-lo escolhido para criar este aplicativo.

Felizmente, o redis foi projetado para lidar com muitas conexões simultâneas (diferentemente do mongo) e, por padrão, é definido em 10k, o que deixa espaço para cerca de 5k usuários simultâneos, em uma única instância de redis, o que é suficiente para o momento para mim, mas eu '' Eu li que ele pode ser enviado até 64k conexões simultâneas, então essa arquitetura deve ser sólida o suficiente, acredito.

Nesse ponto, eu estava pensando em implementar algum tipo de pool de conexão para redis, para otimizá-lo um pouco mais, mas não tenho certeza se isso não fará com que os eventos pub / sub se acumulem nas conexões, a menos que cada um deles é destruído e recriado a cada vez, para limpá-los.

De qualquer forma, obrigado por suas respostas, e ficarei curioso para saber o que você pensa e se você tem outra sugestão.

Felicidades.


2
Estou tendo o que parece ser o mesmo problema no meu aplicativo de produção, também novo na função de administrador do servidor. Sigo o que você fez no conceito, mas tenho algumas perguntas sobre como fazê-lo - talvez você possa fornecer um link para algum recurso na sua resposta aceita? Ou simplesmente fornecer mais informações? Em particular, sobre "Mas então eu decidi tentar criar 2 novas conexões para redis para cada cliente manipular seu pub / sub apenas em suas sessões e, em seguida, fechar as conexões quando o usuário desconectar".
toblerpwn

2

Você tem algum código-fonte para despejar? Pode ser que as conexões com o banco de dados não estejam fechadas? Processos aguardando conexões HTTP que nunca fecham.

Você pode postar alguns logs?

Faça um ps -ef e verifique se nada ainda está sendo executado. Vi processos da web deixar zumbis que não morrerão até você matar -9. Às vezes, o desligamento não funciona ou não funciona completamente e esses threads ou processos retêm RAM e, às vezes, CPU.

Pode ser um loop infinito em algum lugar do código ou um processo travado mantendo uma conexão db no topo.

Quais módulos do NPM estão usando? Eles são os mais recentes?

Você está capturando exceções? Consulte: http://geoff.greer.fm/2012/06/10/nodejs-dealing-with-errors/ Consulte: /programming/10122245/capture-node-js-crash-reason

Dicas Gerais:

http://clock.co.uk/tech-blogs/preventing-http-raise-hangup-error-on-destroyed-socket-write-from-crashing-your-nodejs-server

http://blog.nodejitsu.com/keep-a-nodejs-server-up-with-forever

http://hectorcorrea.com/blog/running-a-node-js-web-site-in-production-a-beginners-guide

/programming/1911015/how-to-debug-node-js-applications

https://github.com/dannycoates/node-inspector

http://elegantcode.com/2011/01/14/taking-baby-steps-with-node-js-debugging-with-node-inspector/


1

Não é uma resposta em si, pois sua pergunta é mais uma história do que uma pergunta de resposta única.

Só para dizer que eu criei com êxito um servidor node.js. com o socket.io manipulando mais de 1 milhão de conexões persistentes com uma carga útil média de 700 bytes.

A placa de interface de rede a 1 Gbps estava saturando no início e eu estava vendo MUITA espera de E / S nos eventos de publicação para todos os clientes.

A remoção do nginx da função de proxy também retornou uma memória preciosa, pois alcançar um milhão de conexões persistentes com apenas um servidor é uma tarefa difícil de ajustar parâmetros de configurações, aplicativos e sistemas operacionais. Lembre-se de que isso só é possível com muita RAM (cerca de 1M de conexões WebSockets consome cerca de 16 GB de RAM, com node.js, acho que usar sock.js seria ideal para baixo consumo de memória, mas, por enquanto, socket.io consome muito).

Esse link foi meu ponto de partida para atingir esse volume de conexões com o nó. Além de ser um aplicativo Erlang, todo o ajuste do SO é praticamente independente do aplicativo e deve ser usado por qualquer pessoa que vise muitas conexões persistentes (soquetes da web ou pesquisas longas).

HTH,

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.