PS Lors de la rédaction de cet article, pas une seule adresse IP n'a été lésée , le projet original, bien qu'il soit saturé de l'esprit de piratage (serveur gratuit d'un jeu payant!), N'a violé aucun droit, le code du détenteur du droit d'auteur n'y a pas été utilisé, et le serveur était entièrement basé sur la recherche d'un client de jeu honnêtement acheté et d'un son sens du développeur. Cet opus ne raconte que les défis rencontrés par l'auteur et les méthodes originales pour les résoudre, à la fois dans l'ancien et dans le projet moderne. Je m'excuse à l'avance pour le style narratif de l'histoire, par opposition à simplement énumérer les faits.
introduction
Vous pouvez argumenter autant que vous le souhaitez .Net n'est pas pour les serveurs, mais alors (et maintenant) il m'a semblé une idée très sensée que vous puissiez écrire la logique sous forme de scripts, la compiler et la charger en déplacement, sans trop penser à l'allocation de mémoire, à l'assemblage débris, pointeurs et plus encore. En fait, cela vous permet de déléguer le script de la logique métier à des développeurs moins qualifiés, en se limitant uniquement à la révision de code. Mais pour ce faire, vous devez vous assurer que le noyau lui-même fonctionne sans échec et qu'il a commencé à échouer même à 10-15 en ligne, à la fois en 2004 et en 2020.
En 2004, tout tournait sur Windows Server 2003, .Net 1.1, MSSQL 2000. Le serveur et l'hébergement étaient fournis par le fournisseur Wnet, puis un nouveau serveur a été construit grâce aux dons des joueurs. Le projet n'était pas purement commercial et des revenus minimes provenant de bannières et de comptes premium ont été utilisés pour les mises à niveau.Le serveur moderne fonctionne sur Mono sous Debian en mode de compatibilité .Net 4.7, avec MariaDB pour les données, hébergé dans le cloud Hetzner. Depuis longtemps déjà, il n'y a pas d'idéaliste aux yeux brûlants qui croyait que les jeux devraient être gratuits, et le don et la vente d'objets de jeu tuent tout intérêt. Maintenant, ce personnage est devenu assez gris, a changé son enthousiasme pour l'expérience et est convaincu qu'une startup doit apporter à la fois plaisir et revenu.
Mais l'histoire ne concerne pas cela, mais les serveurs auto-écrits et leurs problèmes.
Chapitre 1. Pestilence
. , , , . , , . , , . Visual Studio, - , . EventLog .En 2020, l'application serveur n'était, en principe, qu'une application console, fonctionnant dans un écran séparé sous Linux. Il n'y avait plus d'options pour le lancement sous Visual Studio, mais le logger est devenu très avancé au fil des ans, UnhandledExceptions est apparu comme des lapins dans le réseau, et il n'y avait pas de code natif en principe. Ce qui, cependant, ne m'a pas sauvé des plantages avec MOO et StackOverflowException. La profondeur de la pile dans le cas de StackOverflowException a décuplé, remplissant des centaines de kilo-octets de journal avec des messages du même type et refusant d'écrire une trace de pile normale. Mais dans tous les cas, la redirection vers >> log.txt a rapidement permis de comprendre qui est à blâmer et où. Le bot Telegram a aidé séparément, signalant que le processus serveur était mort.
— , Console.Out Console.Error. UnhandledExceptionHandler, . AutoFlush = true, , .
cmd — , . , , , - — , . - — .Net >> log.txt.
UnhandledExceptionHandler : OutOfMemoryException ( ), StackOverflowException Unmanaged . , — Access Violation - OOM.
Access Violation — ZLib ( ICSharpCode.SharpZipLib), OpenSSL ( SRP-6), MySQL ( System.Data MSSQL ).
, Socket.BeginReceive . .Net Thread Pool ( , IO Threads) , UnhandledExceptionHandler. , BeginReceive->EndReceive->BeginReceive , BeginReceive .
Tout cela a considérablement amélioré l'image et le serveur a commencé à planter beaucoup moins souvent, principalement uniquement lorsque la mémoire était épuisée.
Ensuite, ce n'était qu'une question de technologie. L'étude des logs a montré que le débordement de pile ne se manifestait pas dans le noyau, mais dans la logique métier: la fusée est entrée en collision avec une autre fusée ou une autre mine, elles ont explosé, cela a déclenché la détonation de la première fusée, et ainsi de suite en cercle. Dans l'ensemble, c'est un moment de travail normal, mais c'est là que j'ai ressenti un étrange déjà vu en combattant des démons oubliés depuis longtemps. Et puis une nouvelle cause (ou ancienne oubliée depuis longtemps) de la peste est apparue - un manque de ressources.
Chapitre 2. Heureux
— 256 , ! - , , , , — , OOM - . , — Visual Studio ( , ), WinDbg (), - dotTrace (). , . — , 1.7, . . 100%. , , , — ~100 . Maoni Stephens Rico Mariani GC, LOH (Large Object Heap) .Net. , (pin) , Gen 2, — LOH,Un serveur moderne avec moins de 4 Go de mémoire provoque un sourire, et vous pouvez ajouter 8 à 16 gigaoctets supplémentaires pour une solution cloud en quelques clics et un redémarrage. Néanmoins, lorsque la mémoire a commencé à fuir et que la charge du processeur est passée à 100-150% (sur la base de 800% pour 8 cœurs), je me suis de nouveau sentie comme un étudiant de 20 ans, brûlant des gigaoctets et des gigaflops dans le foyer d'une voiture vorace. C'était étrange, pas normal et stupide. Il était particulièrement désagréable que, comme auparavant, le jeu continue à se dérouler normalement (bien qu'avec des retards), mais rien n'a été interrompu. Eh bien, jusqu'à ce que la mémoire soit épuisée, bien sûr.. — , , , (, .Net 1.1 Generics!). — , - , . Marshal.AllocHGlobal ( - , ). , , . , , , 100% CPU - . Interop WSASend/WSAReceive ( Windows , .Net) . - , .Net : BeginSend/BeginReceive , , 100% CPU.
, , , , , . , - 100% , !
, 2005 Workstation GC Server GC .Net 2.0 Preview. — , GC , 5-10% CPU.
, , Thread Pool Net 1.1 Workstation GC , ( !) ( 100% ).
BeginSend/BeginReceive Windows IOCP . , , , OOM 100% .
Au fil des ans, Lightweight Threads (alias Fibers) a réussi à apparaître et à disparaître, ce qui fait que nous n'avons plus accès aux threads système dans .Net, uniquement aux soi-disant. Threads gérés, et sur Mono, il n'y a toujours pas d'accès à ProcessThread - il n'y a que des stubs à l'intérieur. Les diagnostics des threads sont devenus beaucoup plus compliqués, mais maintenant j'ai utilisé mon propre Thread Pool, tous les threads ont été calculés et nommés, pour chacun d'eux des statistiques précises ont été conservées, lequel d'entre eux est actuellement en cours d'exécution, combien de temps une tâche spécifique prend. Pour cette raison, il s'est rapidement avéré que les problèmes se trouvaient désormais dans mon code, et non dans celui du système, et les statistiques de thread ont montré que le zhor est associé à l'exécution de la logique métier, seules certaines actions sont effectuées 100 fois plus souvent qu'elles ne le devraient. Maintenant je n'étais pas limité en ressources,par conséquent, j'ai calmement fourni l'appel de chaque script et minuterie avec une journalisation supplémentaire, mesuré le temps d'exécution de chaque événement, et en une semaine d'expériences, j'ai pu dire avec confiance quel était le problème. Il s'est avéré qu'un certain PNJ essayait d'attaquer un autre PNJ et que les deux étaient coincés dans des pierres, ils ne pouvaient donc pas bouger et leurs tentatives de se tirer dessus ont été instantanément interrompues en raison du manque de ligne de vue. Mais en même temps, à chaque cycle de calcul du comportement (15ms), ils ont essayé de calculer le chemin, ont commencé à tirer, mais en raison de l'impossibilité de tirer, les armes ne se rechargeaient pas et au cycle suivant, tout était répété. Pendant plusieurs jours du jeu, des centaines de ces PNJ ont été recrutés et ils ont finalement mangé toutes les ressources du serveur. La solution était de corriger le comportement et de réduire les situations de collage, et en même temps un temps de rechargement court même pour les tirs infructueux.
Et puis le serveur a commencé à se figer.
Chapitre 3. Froid
L'automne 2005 n'a pas été facile - j'avais une situation incertaine avec mon travail et le loyer de mon appartement a soudain doublé. J'étais seulement satisfait du serveur de jeu - il y avait déjà des centaines de jeux en ligne, mais le problème a commencé là aussi - le monde entier a commencé à se figer. Dans le meilleur des cas, les pings ont continué à marcher ou certains minuteries ont fonctionné. Et parfois, tout se figeait, le trafic s'arrêtait et vous deviez tuer l'application serveur et la redémarrer. Comme auparavant, il était impossible de se connecter avec un débogueur à un serveur en cours d'exécution en raison d'une consommation et de freins importants. Pour une raison quelconque, Visual Studio se bloque ou se fige simplement.Lors de l'une des froides journées d'octobre 2020, l'arrivée prévue de diffuseurs en direct a été interrompue car le serveur s'est soudainement gelé. L'autorisation a fonctionné, mais il était impossible d'entrer dans le monde, le robot Telegram était silencieux. Une recherche rapide des problèmes n'a montré rien dans les journaux, il n'y avait aucun problème de mémoire et aucun des threads ne mourait de faim. Ça s'est juste arrêté. Après avoir dit à haute voix plusieurs fois quelque chose à propos d'un chat de la matrice et d'une femme au comportement indécent, je suis allé chercher une impasse. Après que Microsoft a acheté Miguel de Icaz et Xamarin, la documentation Mono est un spectacle pitoyable - elle est là, mais pas à jour ou ne mène nulle part. Par exemple, 3/4 des données de la pagesur le débogage en mono avec gdb n'est pas applicable et ne fonctionne pas. J'ai pu me connecter au serveur gelé via gdb, mais les commandes appellent mono_pmip et d'autres ont donné des réponses inintelligibles, principalement sur les erreurs de syntaxe. Par miracle, j'ai réalisé que gdb voulait que je transforme les paramètres et le résultat des commandes mono_ * dans certains types, et j'ai donc fini par être en mesure d'obtenir une liste de threads figés dans le blocage croisé. Mais les nombres de la liste ne correspondaient ni à la commande ps ni au ManagedThreadId du serveur. La journalisation étendue, que j'ai faite pour détecter la brûlure du processeur, a beaucoup aidé - à partir de là, j'ai pu comprendre quels paquets et minuteries ont été exécutés en dernier et j'ai progressivement commencé à rétrécir le cercle des suspects. En tant que mal, le blocage croisé n'était pas avec deux fils, mais avec trois, il n'était donc pas possible d'obtenir une image plus détaillée.Ensuite, je me suis souvenu de l'ancien râteau et j'ai commencé à regarder le code d'utilisation des verrous. Il s'est avéré que plusieurs refactorisations sont passées au fil des ans et SpinLock a été progressivement remplacé par Monitor.Enter / Monitor.Exit, et souvent par un simple verrou. Et puis soudain j'ai attiré mon attentionL' article d' Eric Gunnerson, qui dit que vous pouvez le faire beaucoup plus facilement: utilisez Monitor.Essayez d'entrer partout avec un timeout, et si le verrou échoue, lancez une exception. C'est une méthode incroyablement simple et très efficace - si quelque part l'appel TryEnter a attendu plus de 30 secondes et est tombé (et que de tels retards ne sont pas caractéristiques de la logique), alors cet endroit doit être étudié et vérifié qui aurait pu prendre pendant si longtemps et ne pas recevoir l'objet de verrouillage. En aspergeant des cendres sur ma tête, j'ai réalisé que j'aurais pu tout nettoyer de cette façon il y a 15 ans, il n'était pas nécessaire de réinventer la roue en calculant la «profondeur du trou». Mais c'était peut-être pour le mieux alors.
— , . , - . , - . SOS.dll. Son Of Strike WinDbg .Net , , . , .Net GC. - sos.dll 50. , , , . , — deadlock!
, . — . — , , , , ! , . SpinLock try/finally . , , — , SpinLock , , , , , . 8 , . , : , , “ ”. , . , , — .
, , Xeon 5130x2 8 . 2000, 2500, . , , , , -, . .
Eh bien, le 4ème pilote est venu à un nouveau projet, comme une fois à un émulateur. Seulement, il n'a pas eu le temps de devenir populaire. Pourtant, la présence de jusqu'à trois problèmes critiques dès le début du projet l'a rapidement renversé. Et le jeu n'est pas du tout sorti grand public. Mais ce n'est pas non plus un sujet pour cet article.
PPS L'article utilise des illustrations d'un artiste inconnu Parsakoira avec la signature «ChoW # 227 :: VOTING :: 4 Cavaliers de l'Apocalypse», probablement du site déjà décédé conceptart.com:
https://www.pinterest.com/pin/460141286926583086/
https : //www.pinterest.com/pin/490681321879914768/