node.js, mongodb, redis, sur la dégradation des performances ubuntu en production, la RAM est libre, CPU 100%

11

Comme le titre de la question le suggère, j'ai du mal à comprendre ce qui peut être amélioré sur mon application (ou réglé dans le système d'exploitation, ubuntu) pour obtenir une performance acceptable. Mais je vais d'abord expliquer l'architecture:

Le serveur frontal est une machine à 8 cœurs avec 8 Go de RAM exécutant Ubuntu 12.04. L'application est entièrement écrite en javascript et s'exécute dans node.js v 0.8.22 (car certains modules semblent se plaindre sur les nouvelles versions de node) J'utilise nginx 1.4 pour proxy le trafic http du port 80 et 443 à 8 nœuds qui sont gérés et a commencé à utiliser l'api du cluster de nœuds. J'utilise la dernière version de socket.io 0.9.14 pour gérer les connexions websocket, sur lesquelles j'ai activé uniquement les websockets et xhr-polling comme transports disponibles. Sur cette machine, je lance également une instance de Redis (2.2)

Je stocke des données persistantes (comme les utilisateurs et les scores) sur un deuxième serveur sur mongodb (3.6) avec 4 Go de RAM et 2 cœurs.

L'application est en production depuis quelques mois (elle fonctionnait sur une seule boîte jusqu'à il y a quelques semaines) et elle est utilisée par environ 18 000 utilisateurs par jour. Il a toujours très bien fonctionné en dehors d'un problème principal: la dégradation des performances. Avec l'utilisation, la quantité de cpu utilisée par chaque processus augmente jusqu'à ce qu'elle stabilise le travailleur (qui ne servira plus les demandes). Je l'ai résolu temporairement en vérifiant le processeur utilisé par chaque travailleur toutes les minutes et en le redémarrant s'il atteint 98%. Donc, le problème ici est principalement le processeur, et non la RAM. La RAM n'est plus un problème depuis que j'ai mis à jour vers socket.io 0.9.14 (la version précédente fuyait de la mémoire), donc je doute que ce soit un problème de fuite de mémoire, surtout parce que maintenant c'est le processeur qui se développe assez rapidement ( Je dois redémarrer chaque travailleur environ 10 à 12 fois par jour!). Pour être honnête, la RAM utilisée augmente également, mais très lentement, 1 concert tous les 2 ou 3 jours d'utilisation, et ce qui est étrange, c'est qu'il n'est pas libéré même lorsque je redémarre complètement l'application entière. Il n'est libéré que si je redémarre le serveur! cela, je ne peux pas vraiment comprendre ...

J'ai maintenant découvert nodefly qui est incroyable, donc je peux enfin voir ce qui se passe sur mon serveur de production, et je collecte des données depuis quelques jours. Si quelqu'un veut voir les graphiques, je peux vous donner accès, mais en gros, je peux voir que j'ai entre 80 et 200 connexions simultanées! Je m'attendais à ce que node.js gère des milliers, pas des centaines de demandes. De plus, le temps de réponse moyen pour le trafic http oscille entre 500 et 1500 millisecondes, ce qui, je pense, est beaucoup. De plus, en ce moment même avec 1300 utilisateurs en ligne, voici la sortie 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

ce qui montre que j'ai beaucoup de connexions fermées en attendant. J'ai augmenté le nombre maximal de fichiers ouverts à 999999, voici la sortie d'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

J'ai donc pensé que le problème pourrait être sur le trafic http qui, pour certaines raisons, sature les ports / sockets disponibles (?), Mais une chose n'a pas de sens pour moi: pourquoi quand je redémarre les travailleurs et que tous les clients se reconnectent en quelques secondes, la charge sur le processeur du travailleur descend à 1% et est capable de répondre correctement aux demandes jusqu'à ce qu'il sature après environ 1 heure (en période de pointe)?

Je suis principalement un programmeur javascript, pas un administrateur sys, donc je ne sais pas combien de charge je dois m'attendre à gérer avec mes serveurs, mais il ne fonctionne sûrement pas comme il se doit. L'application est stable sinon et ce dernier problème m'empêche d'expédier les versions mobiles de l'application qui sont prêtes, car évidemment elles apporteront plus de charge et finiront par planter le tout!

J'espère qu'il y a quelque chose d'évident que je fais mal, et quelqu'un aidera à le repérer ... n'hésitez pas à me demander plus d'informations, et je suis désolé pour la longueur de la question mais c'était nécessaire je crois ... Merci d'avance!

Franjanko
la source
Existe-t-il un moyen d'obtenir quelque chose comme un vidage de thread à partir de node.js? Il y a probablement quelques threads dans une boucle infinie. Aussi, qu'est-ce qui utilise réellement le processeur? Que voyez-vous toplorsque l'utilisation du processeur est proche de 100%?
rvs
cpu est entièrement utilisé par nodejs, lorsque je lance top, je vois les processus de noeud prendre tout le cpu. Je ne sais pas comment je peux sortir un vidage de thread depuis un nœud pour être honnête ...
Franjanko
une autre chose à souligner est que la majorité du temps CPU semble aller au système, pas le temps utilisateur
Franjanko
Quelqu'un sait-il au moins combien de connexions simultanées dois-je pouvoir gérer avec les serveurs que j'ai en place? en ce moment je supporte 200 connexions simultanées max. Cela m'aidera à estimer à quelle distance je suis d'une configuration optimale ... merci.
Franjanko

Réponses:

10

Après quelques jours d'essais et d'erreurs intenses, je suis heureux de pouvoir dire que j'ai compris où était le goulot d'étranglement, et je le posterai ici afin que d'autres personnes puissent bénéficier de mes découvertes.

Le problème réside dans les connexions pub / sub que j'utilisais avec socket.io, et en particulier dans le RedisStore utilisé par socket.io pour gérer la communication interprocessus des instances de socket.

Après avoir réalisé que je pouvais facilement implémenter ma propre version de pub / sub en utilisant redis, j'ai décidé de l'essayer et j'ai supprimé le redisStore de socket.io, le laissant avec le magasin de mémoire par défaut (je n'ai pas besoin de diffuser vers tous les clients connectés mais seulement entre 2 utilisateurs différents connectés éventuellement sur des processus différents)

Initialement, je n'ai déclaré que 2 connexions redis globales x processus pour gérer le pub / sub sur chaque client connecté, et l'application utilisait moins de ressources mais j'étais toujours affecté par une croissance constante de l'utilisation du CPU, donc peu de choses avaient changé. Mais alors j'ai décidé d'essayer de créer 2 nouvelles connexions à redis pour chaque client pour gérer leur pub / sub uniquement sur leurs sessions, puis fermer les connexions une fois l'utilisateur déconnecté. Puis après une journée d'utilisation en production, les processeurs étaient toujours à 0-5% ... bingo! aucun processus ne redémarre, aucun bogue, avec les performances que j'attendais. Maintenant, je peux dire que node.js est génial et je suis heureux de l'avoir choisi pour créer cette application.

Heureusement, redis a été conçu pour gérer de nombreuses connexions simultanées (différemment par mongo) et par défaut, il est défini sur 10k, ce qui laisse de la place pour environ 5k utilisateurs simultanés, sur une seule instance de redis, ce qui me suffit pour le moment, mais je '' J'ai lu qu'il peut être poussé jusqu'à 64k connexions simultanées, donc cette architecture devrait être assez solide je crois.

À ce stade, je pensais à implémenter une sorte de pools de connexions à redis, pour l'optimiser un peu plus, mais je ne sais pas si cela ne fera pas recommencer les événements pub / sub sur les connexions, à moins que chacun d'eux est détruit et recréé à chaque fois, pour les nettoyer.

Quoi qu'il en soit, merci pour vos réponses et je serai curieux de savoir ce que vous en pensez et si vous avez d'autres suggestions.

À votre santé.

Franjanko
la source
2
J'ai ce qui semble être le même problème dans mon application de production, également nouveau dans le rôle d'administrateur de serveur. Je suis ce que vous avez fait dans le concept, mais j'ai quelques questions sur la façon de le faire - peut-être pourriez-vous fournir un lien vers une ressource dans votre réponse acceptée? Ou simplement fournir plus d'informations? En particulier à propos de "Mais j'ai décidé d'essayer de créer 2 nouvelles connexions pour redis pour chaque client pour gérer leur pub / sub uniquement sur leurs sessions, puis fermer les connexions une fois que l'utilisateur s'est déconnecté."
toblerpwn
2

Avez-vous du code source à vider? Il se peut que les connexions à la base de données ne soient pas fermées? Processus en attente de connexions HTTP qui ne se ferment jamais.

Pouvez-vous publier des journaux?

Faites un ps -ef et assurez-vous que rien ne fonctionne toujours. J'ai vu des processus Web laisser des zombies qui ne mourront pas tant que vous n'aurez pas tué -9. Parfois, l'arrêt ne fonctionne pas ou ne fonctionne pas complètement et ces threads ou processus contiennent de la RAM et parfois du CPU.

Il peut s'agir d'une boucle infinie quelque part dans le code ou d'un processus bloqué se tenant sur une connexion db.

Quels modules NPM utilisent? Sont-ils tous les derniers?

Attrapez-vous des exceptions? Voir: http://geoff.greer.fm/2012/06/10/nodejs-dealing-with-errors/ Voir: /programming/10122245/capture-node-js-crash-reason

Conseils généraux:

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/

Tim Spann
la source
1

Pas une réponse en soi, car votre question est plus une histoire qu'une question à réponse unique.

Juste pour dire que j'ai réussi à construire un serveur node.js avec socket.io gérant plus d'un million de connexions persistantes avec une charge utile moyenne de 700 octets.

La carte d'interface réseau à 1 Gbit / s saturait au début et je voyais BEAUCOUP d'E / S attendre la publication des événements pour tous les clients.

La suppression de nginx du rôle proxy a également rendu une mémoire précieuse, car pour atteindre un million de connexions persistantes avec un seul serveur, il est difficile de modifier les configurations, les applications et le réglage des paramètres du système d'exploitation. Gardez à l'esprit que cela n'est faisable qu'avec beaucoup de RAM (environ 1M de connexions websockets mange environ 16 Go de RAM, avec node.js, je pense que l'utilisation de sock.js serait idéal pour une faible consommation de mémoire, mais pour l'instant, socket.io consomme beaucoup).

Ce lien était mon point de départ pour atteindre ce volume de connexions avec le nœud. En plus d'être une application Erlang, tout le réglage du système d'exploitation est à peu près indépendant de l'application et devrait être utile à quiconque vise de nombreuses connexions persistantes (websockets ou longues interrogations).

HTH,

Marcel
la source