Como o título da pergunta sugere, estou tendo dificuldades para descobrir o que pode ser melhorado em meu aplicativo (ou sintonizado no sistema operacional, o 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 rodando o Ubuntu 12.04. O aplicativo é escrito inteiramente em javascript e executado em node.js v 0.8.22 (como alguns módulos parecem reclamar em versões mais novas do nó)
Eu uso o nginx 1.4 para o tráfego HTTP do proxy da porta 80 e 443 para 8 trabalhadores do nó que são gerenciados e iniciados usando 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 websocket, nas quais habilitei apenas websockets e xhr-polling como transportes disponíveis. Nesta máquina eu 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 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 uma questão principal: a degradação do desempenho. Com o uso, a quantidade de cpu usada por cada processo aumenta até a estatura do trabalhador (que não atende mais às solicitações). Eu resolvi temporariamente verificar a cpu em uso por cada trabalhador a cada minuto, e reiniciá-lo se atingir 98%. Então o problema aqui é principalmente cpu e não RAM. A RAM não é mais um problema desde que eu atualizei para socket.io 0.9.14 (a versão anterior estava vazando memória) então eu duvido que seja um problema de vazamento de memória, especialmente porque agora é a cpu que cresce rapidamente ( Eu tenho que reiniciar cada trabalhador em torno de 10-12 vezes por dia!). O RAM em uso cresce também para ser honesto, mas muito lentamente, 1 gig a cada 2-3 dias de uso, e o estranho é que ele não é liberado mesmo quando eu reinicio completamente o aplicativo inteiro. Só é liberado se eu reiniciar o servidor! isso eu realmente não consigo entender ...
Eu agora descobri nodefly que é incrível, para que eu possa finalmente ver o que está acontecendo no meu servidor de produção e estou coletando dados desde um par de dias. Se alguém quiser ver os gráficos eu posso te dar acesso, mas basicamente eu posso ver que eu tenho entre 80 e 200 conexões simultâneas! Eu esperava que o node.js lidasse com milhares, não com centenas de solicitações. Também o tempo médio de resposta para o tráfego http flutua entre 500 e 1500 milissegundos, o que eu acho que é 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
que mostra que eu tenho muitas conexões fechadas no tempo. Eu aumentei o máximo de arquivos abertos para 999999, aqui está 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
Então, achei 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 eu reinicio os trabalhadores e todos os clientes se reconectam alguns segundos, a carga na cpu do funcionário diminui para 1% e é capaz de atender às solicitações corretamente até que sature após cerca de 1 hora (no horário de pico)?
Sou principalmente um programador de javascript, não um administrador de sistema, por isso não sei quanta carga devo esperar para lidar com meus servidores, mas com certeza ele não está funcionando como deveria. O aplicativo é estável de outra forma e este último problema está me impedindo de enviar as versões móveis do aplicativo que estão prontas, como, obviamente, eles vão trazer mais carga e, eventualmente, quebrar a coisa toda!
Espero que haja algo óbvio que estou fazendo errado, e alguém irá ajudar a identificá-lo ... fique à vontade para me pedir mais informações, e eu sinto muito pela duração da pergunta, mas foi necessário eu acredito ... obrigado antecipadamente!