Nous traitons les messages à travers une variété de services (un message touchera probablement 9 services avant qu'il ne soit fait, chacun effectuant une fonction spécifique liée aux E / S). À l'heure actuelle, nous avons une combinaison du pire des cas (sérialisation du contrat de données XML) et du meilleur des cas (MSMQ en mémoire) pour les performances.
La nature du message signifie que nos données sérialisées finissent par environ 12-15 kilo-octets, et nous traitons environ 4 millions de messages par semaine. Les messages persistants dans MSMQ étaient trop lents pour nous, et à mesure que les données augmentent, nous ressentons la pression des fichiers mappés en mémoire de MSMQ. Le serveur utilise 16 Go de mémoire et augmente, juste pour la mise en file d'attente. Les performances souffrent également lorsque l'utilisation de la mémoire est élevée, car la machine commence à échanger. Nous faisons déjà le comportement d'autonettoyage MSMQ.
J'ai l'impression qu'il y a une partie que nous faisons mal ici. J'ai essayé d'utiliser RavenDB pour conserver les messages et simplement mettre en file d'attente un identifiant, mais les performances y étaient très lentes (1000 messages par minute, au mieux). Je ne sais pas si c'est le résultat de l'utilisation de la version de développement ou quoi, mais nous avons certainement besoin d'un débit plus élevé [1]. Le concept fonctionnait très bien en théorie mais les performances n'étaient pas à la hauteur.
Le modèle d'utilisation comporte un service faisant office de routeur, qui effectue toutes les lectures. Les autres services attacheront des informations en fonction de leur raccordement tiers et les renverront au routeur. La plupart des objets sont touchés 9 à 12 fois, bien qu'environ 10% soient contraints de boucler dans ce système pendant un certain temps jusqu'à ce que les tierces parties répondent de manière appropriée. Les services en tiennent compte en ce moment et ont des comportements de sommeil appropriés, car nous utilisons le champ prioritaire du message pour cette raison.
Donc, ma question, quelle est la pile idéale pour le passage de messages entre des machines discrètes mais LAN dans un environnement C # / Windows? Je commencerais normalement par BinaryFormatter au lieu de la sérialisation XML, mais c'est un trou de lapin si une meilleure façon est de décharger la sérialisation vers un magasin de documents. D'où ma question.
[1]: La nature de notre entreprise signifie que plus tôt nous traitons les messages, plus nous gagnons d'argent. Nous avons empiriquement prouvé que le traitement d'un message plus tard dans la semaine signifie que nous sommes moins susceptibles de gagner cet argent. Alors que les performances de "1000 par minute" semblent très rapides, nous avons vraiment besoin de ce nombre à partir de 10k / minute. Ce n'est pas parce que je donne des chiffres dans les messages par semaine que nous avons une semaine entière pour traiter ces messages.
=============== modifier:
Information additionnelle
Sur la base des commentaires, je vais ajouter quelques précisions:
Je ne suis pas sûr que la sérialisation soit notre goulot d'étranglement. J'ai comparé l'application, et bien que la sérialisation apparaisse dans le graphique thermique, elle n'est responsable que de 2,5 à 3% de l'utilisation du processeur du service.
Je suis principalement préoccupé par la permanence de nos messages et l'utilisation potentielle abusive de MSMQ. Nous utilisons des messages non transactionnels et non persistants afin de pouvoir maintenir les performances de mise en file d'attente, et j'aimerais vraiment avoir au moins des messages persistants pour qu'ils survivent à un redémarrage.
Ajouter plus de RAM est une mesure provisoire. La machine est déjà passée de 4 Go à 16 Go de RAM et il devient de plus en plus difficile de la retirer pour continuer à en ajouter.
En raison du modèle de routage en étoile de l'application, la moitié du temps qu'un objet est sauté puis poussé dans une file d'attente, il ne change pas du tout. Cela se prête à nouveau (IMO) à le stocker dans une sorte de magasin de valeurs-clés ailleurs et à simplement passer des identificateurs de message.
Le modèle de routage en étoile fait partie intégrante de l'application et ne changera pas. Nous ne pouvons pas le centrer sur l'application, car chaque élément en cours de route fonctionne de manière asynchrone (de manière d'interrogation) et nous voulons centraliser le comportement de nouvelle tentative en un seul endroit.
La logique d'application est écrite en C #, les objets sont des POCO immuables, l'environnement de déploiement cible est Windows Server 2012 et nous sommes autorisés à lever des machines supplémentaires si un logiciel particulier n'est pris en charge que sous Linux.
Mes objectifs sont de maintenir le débit actuel tout en réduisant l'empreinte mémoire et en augmentant la tolérance aux pannes avec un minimum de capital.
la source
Réponses:
Voici quelques repères de file d'attente qui pourraient vous intéresser. MSMQ devrait être capable de gérer 10 000 messages par seconde. Serait-ce un problème de configuration ou peut-être que les clients ne suivent pas la lecture de la file d'attente? Notez également à quel point ZeroMQ est incroyablement rapide dans ces benchmarks (environ 100 000 messages par seconde), il n'offre pas d'option de persistance, mais il devrait vous amener là où vous voulez être sage en termes de performances.
la source
Nous avons eu une situation quelque peu similaire il y a plusieurs années, avec un système de messagerie en file d'attente (empreintes audio dans notre cas). Nous avons fortement apprécié la persistance des paquets de données mis en file d'attente, mais nous avons découvert que tout mettre en file d'attente sur le disque et consommer la file d'attente à partir du disque était très coûteux.
Si nous passions aux files d'attente basées sur la mémoire, les performances étaient exceptionnelles, mais nous avions un problème majeur. De temps en temps, les consommateurs des files d'attente sont devenus indisponibles pendant une période de temps considérable (les éléments consommateurs et producteurs dans notre cas sont connectés via WAN), de sorte que la file d'attente du producteur augmentait au point qu'elle devenait ingérable et comme votre cas, une fois que la consommation de mémoire était très élevée, une saturation excessive de la mémoire pendant l'échange a amené le système à une analyse complète.
Nous avons conçu une file d'attente que nous avons baptisée
VMQueue
(pour Virtual Memory Queue, un très mauvais nom rétrospectivement). L'idée de cette file d'attente est que si le processus consommateur s'exécute au pair, en d'autres termes, un traitement assez rapide pour être en mesure de maintenir le nombre d'éléments mis en file d'attente en dessous d'un certain niveau, il a essentiellement les mêmes performances qu'une mémoire. file d'attente basée. Cependant, lorsque le consommateur ralentit ou devient indisponible et que la file d'attente du producteur atteint une certaine taille, la file d'attente commence automatiquement la pagination des éléments vers et depuis le disque (à l'aide deBinaryFormatter
sérialisation d'ailleurs). Ce processus permet de contrôler complètement l'utilisation de la mémoire et le processus de pagination est rapide, ou au moins beaucoup plus rapide que l'échange de mémoire virtuelle se produisant lors d'une charge de mémoire importante. Une fois que le consommateur parvient à vider la file d'attente en dessous du seuil, il recommence à fonctionner comme une file d'attente basée uniquement sur la mémoireSi le système plante ou redémarre, la file d'attente peut récupérer tous les éléments paginés qui ont été stockés sur le disque, elle ne perdra que les éléments qui étaient encore conservés en mémoire avant le crash. Si vous pouvez vous permettre de perdre un nombre limité de paquets lors d'un plantage ou d'un redémarrage, cette file d'attente peut être utile.
Si vous êtes intéressé, je peux partager le
VMQueue
code source de la classe afin que vous puissiez jouer avec. La file d'attente acceptera toute classe marquée comme sérialisable. Lors de la création de la file d'attente, vous établissez la taille de la page en nombre d'éléments. L'interface de classe est pratiquement la même que celle d'une classe de file d'attente standard. Le code est cependant très ancien (.net 1.1) donc aucune interface générique n'existe malheureusement.Je sais que passer de la technologie MSMQ éprouvée est un énorme pari, mais cette file d'attente fonctionne de manière fiable depuis près de 6 ans et nous a permis de survivre et de nous remettre de scénarios où la machine du producteur était hors ligne depuis plusieurs semaines! Veuillez me faire savoir si vous êtes intéressé. :)
la source
Le système HP ProLiant ML350G5 obtient 82 000 transactions par minute, c'est-à-dire qu'il a plus de 8 fois le débit de «10 000 / minute» que vous avez mentionné.
De plus, pour être honnête, je serais juste allé avec 64 ou même 128 Go de RAM - la RAM est bon marché. Greenspun souligne la différence entre "lancer de la RAM" et "demander à un gars intelligent formé au MIT de l'optimiser", et la RAM gagne.
A noter que "la machine est déjà passée à 16 Go de RAM" est loin d'être suffisante, avec un article soulignant un serveur qui gérait 400k utilisateurs sur 64 Go de RAM.
la source