PS Beim Schreiben dieses Artikels wurde keine einzige IP beschĂ€digt , das ursprĂŒngliche Projekt, obwohl es mit dem Geist der Piraterie gesĂ€ttigt war (kostenloser Server eines kostenpflichtigen Spiels!), Keine Rechte verletzt, der Code des Copyright-Inhabers wurde dort nicht verwendet, und der Server basierte ausschlieĂlich auf der Recherche eines ehrlich gekauften Spieleclients und Sounds Sinn des Entwicklers. Dieses Werk erzĂ€hlt nur von den Herausforderungen, mit denen der Autor konfrontiert war, und den ursprĂŒnglichen Lösungsmethoden, sowohl im alten als auch im modernen Projekt. Ich entschuldige mich im Voraus fĂŒr den ErzĂ€hlstil der Geschichte, anstatt nur die Fakten aufzulisten.
EinfĂŒhrung
Sie können so viel argumentieren, wie Sie möchten. Net ist nicht fĂŒr Server gedacht, aber es schien mir damals (und heute) eine sehr vernĂŒnftige Idee zu sein, dass Sie Logik in Form von Skripten schreiben, kompilieren und unterwegs laden können, ohne zu viel ĂŒber Speicherzuweisung und Assemblierung nachzudenken TrĂŒmmer, Zeiger und mehr. Auf diese Weise können Sie das Scripting der GeschĂ€ftslogik an weniger qualifizierte Entwickler delegieren und sich nur auf die CodeĂŒberprĂŒfung beschrĂ€nken. DafĂŒr mĂŒssen Sie jedoch sicherstellen, dass der Kernel selbst fehlerfrei funktioniert und sowohl 2004 als auch 2020 zwischen 10 und 15 Uhr online ausfĂ€llt.
Im Jahr 2004 drehte sich alles unter Windows Server 2003, .Net 1.1, MSSQL 2000. Der Server und das Hosting wurden vom Wnet-Anbieter bereitgestellt, und dann wurde ein neuer Server mit Spenden von Spielern erstellt. Das Projekt war nicht rein kommerziell und einige minimale Einnahmen aus Bannern und Premium-Konten wurden fĂŒr Upgrades verwendet.Der moderne Server lĂ€uft auf Mono unter Debian im .Net 4.7-KompatibilitĂ€tsmodus mit MariaDB fĂŒr Daten, die in der Hetzner-Cloud gehostet werden. Schon lange gibt es keinen solchen Idealisten mit brennenden Augen, der glaubte, dass Spiele kostenlos sein sollten, und die Spende und der Verkauf von SpielgegenstĂ€nden töten alle Interessen. Jetzt ist dieser Charakter ziemlich grau geworden, hat seine Begeisterung fĂŒr Erfahrung geĂ€ndert und ist ĂŒberzeugt, dass ein Startup sowohl Freude als auch Einkommen bringen sollte.
âšAber in der Geschichte geht es nicht darum, sondern um selbstgeschriebene Server und ihre Probleme.
Kapitel 1. Pestilenz
. , , , . , , . , , . Visual Studio, - , . EventLog .Im Jahr 2020 war die Serveranwendung im Prinzip nur eine Konsolenanwendung, die unter Linux auf einem separaten Bildschirm ausgefĂŒhrt wurde. Es gab keine weiteren Optionen zum Starten unter Visual Studio, aber der Logger wurde im Laufe der Jahre sehr fortschrittlich, UnhandledExceptions wirkten wie Hasen im Netzwerk und es gab im Prinzip keinen nativen Code. Dies hat Sie jedoch nicht vor AbstĂŒrzen mit OOM und StackOverflowException bewahrt. Die Stapeltiefe bei StackOverflowException hat sich verzehnfacht, Hunderte von Kilobyte Protokoll mit Nachrichten desselben Typs gefĂŒllt und es abgelehnt, eine normale Stapelverfolgung zu schreiben. In jedem Fall war es durch die Umleitung zu >> log.txt schnell möglich zu verstehen, wer wo schuld ist. Der Telegramm-Bot half separat und signalisierte, dass der Serverprozess beendet war.
â , 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 .
All dies verbesserte das Bild erheblich und der Server stĂŒrzte viel seltener ab, meistens erst, wenn der Speicher knapp wurde.
Dann war es nur noch eine Frage der Technologie. Die Untersuchung der Protokolle zeigte, dass sich der StapelĂŒberlauf nicht im Kern, sondern in der GeschĂ€ftslogik manifestierte: Die Rakete kollidierte mit einer anderen Rakete oder Mine, sie detonierten, dies löste die Detonation der ersten Rakete aus und so weiter in einem Kreis. Alles in allem ist dies ein normaler Arbeitsmoment, aber dann fĂŒhlte ich ein seltsames DĂ©jĂ Vu, das gegen lĂ€ngst vergessene DĂ€monen der Vergangenheit kĂ€mpfte. Und dann trat eine neue (oder lĂ€ngst vergessene alte) Ursache der Pest auf - ein Mangel an Ressourcen.
Kapitel 2. Freut mich
â 256 , ! - , , , , â , OOM - . , â Visual Studio ( , ), WinDbg (), - dotTrace (). , . â , 1.7, . . 100%. , , , â ~100 . Maoni Stephens Rico Mariani GC, LOH (Large Object Heap) .Net. , (pin) , Gen 2, â LOH,Ein moderner Server mit weniger als 4 GB Arbeitsspeicher verursacht ein Grinsen, und Sie können mit nur wenigen Klicks und einem Neustart zusĂ€tzliche 8-16 Gigabyte fĂŒr eine Cloud-Lösung hinzufĂŒgen. Als der Speicher zu lecken begann und die Prozessorlast auf 100-150% stieg (basierend auf 800% fĂŒr 8 Kerne), fĂŒhlte ich mich wieder wie ein 20-jĂ€hriger Student, der Gigabyte und Gigaflops in der Feuerbox einer gefrĂ€Ăigen Maschine verbrannte. Es war seltsam, nicht normal und dumm. Es war besonders unangenehm, dass das Spiel nach wie vor normal lief (wenn auch mit Verzögerungen), aber nichts wurde unterbrochen. Nun, bis die Erinnerung erschöpft war, natĂŒrlich.. â , , , (, .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% .
Im Laufe der Jahre gelang es Lightweight Threads (auch bekannt als Fibers), zu erscheinen und zu verschwinden, wodurch wir keinen Zugriff mehr auf System-Threads in .Net haben, sondern nur auf die sogenannten. Verwaltete Threads und unter Mono gibt es immer noch keinen Zugriff auf ProcessThread - es gibt nur Stubs im Inneren. Die Diagnose von Threads wurde viel komplizierter, aber jetzt habe ich meinen eigenen Thread-Pool verwendet. Alle Threads wurden berechnet und benannt. FĂŒr jeden von ihnen wurden genaue Statistiken gefĂŒhrt, welche von ihnen derzeit ausgefĂŒhrt werden und wie lange eine bestimmte Aufgabe dauert. Aus diesem Grund stellte sich schnell heraus, dass die Probleme jetzt in meinem Code und nicht im System liegen, und die Thread-Statistiken zeigten, dass der zhor mit der AusfĂŒhrung der GeschĂ€ftslogik verbunden ist. Nur einige Aktionen werden 100-mal hĂ€ufiger ausgefĂŒhrt, als sie sollten. Jetzt war ich nicht auf Ressourcen beschrĂ€nkt,Daher versorgte ich den Aufruf jedes Skripts und Timers ganz ruhig mit zusĂ€tzlicher Protokollierung, maĂ die AusfĂŒhrungszeit jedes Ereignisses und konnte in einer Woche voller Experimente sicher sagen, wo das Problem lag. Es stellte sich heraus, dass ein bestimmter NPC versuchte, einen anderen NPC anzugreifen, und beide in Steinen steckten, so dass sie sich nicht bewegen konnten und ihre Versuche, aufeinander zu schieĂen, aufgrund des Mangels an Sichtlinie sofort unterbrochen wurden. Gleichzeitig versuchten sie bei jedem Zyklus der Berechnung des Verhaltens (15 ms), den Pfad zu berechnen, zu schieĂen, aber aufgrund der Unmöglichkeit des SchieĂens wurden die Waffen nicht nachgeladen und der nĂ€chste Zyklus wurde wiederholt. FĂŒr mehrere Tage des Spiels wurden Hunderte solcher NPCs rekrutiert und sie verbrauchten schlieĂlich alle Ressourcen des Servers. Die Lösung bestand darin, das Verhalten zu korrigieren und festsitzende Situationen zu reduzieren und gleichzeitig eine kurze Nachladezeit auch fĂŒr erfolglose Aufnahmen.
Und dann fing der Server an zu frieren.
Kapitel 3. Kalt
Der Herbst 2005 war nicht einfach - ich hatte eine ungewisse Situation mit meiner Arbeit, die Wohnungsmiete verdoppelte sich plötzlich. Ich war nur mit dem Spieleserver zufrieden - es waren bereits Hunderte online, aber dort begann auch das Problem - die ganze Welt begann zu frieren. Im besten Fall liefen die Pings weiter oder einige Timer arbeiteten. Und manchmal fror alles ein, der Datenverkehr wurde gestoppt und Sie mussten die Serveranwendung beenden und erneut starten. Nach wie vor war es aufgrund des erheblichen Verbrauchs und der Bremsen unmöglich, eine Verbindung mit einem Debugger zu einem laufenden Server herzustellen. Aus irgendeinem Grund stĂŒrzte Visual Studio einfach ab oder frierte ein.An einem der kalten Oktobertage des Jahres 2020 wurde die geplante Ankunft von Live-Streamern unterbrochen, da der Server plötzlich einfror. Die Autorisierung funktionierte, aber es war unmöglich, die Welt zu betreten, der Telegramm-Bot schwieg. Eine schnelle Suche nach Problemen ergab nichts in den Protokollen, es gab keine Speicherprobleme und keiner der Threads hungerte. Es hörte einfach auf. Nachdem ich mehrmals etwas ĂŒber eine Katze aus der Matrix und eine Frau mit unanstĂ€ndigem Verhalten laut gesagt hatte, machte ich mich auf die Suche nach einem Deadlock. Nachdem Microsoft Miguel de Icaz und Xamarin gekauft hat, ist die Mono-Dokumentation ein erbĂ€rmlicher Anblick - sie ist da, aber nicht aktuell oder fĂŒhrt nirgendwo hin. Zum Beispiel 3/4 der Daten von der SeiteĂŒber das Debuggen in Mono mit GDB ist nicht anwendbar und funktioniert nicht. Ich konnte ĂŒber gdb eine Verbindung zum eingefrorenen Server herstellen, aber die Befehle mono_pmip und andere gaben unverstĂ€ndliche Antworten, hauptsĂ€chlich ĂŒber Syntaxfehler. Durch ein Wunder wurde mir klar, dass gdb möchte, dass ich die Parameter und das Ergebnis von mono_ * -Befehlen in bestimmte Typen umwandle, und so konnte ich eine Liste von Threads erhalten, die beim Cross-Blocking eingefroren wurden. Die Zahlen in der Liste stimmten jedoch weder mit dem Befehl ps noch mit der ManagedThreadId vom Server ĂŒberein. Die erweiterte Protokollierung, mit der ich festgestellt habe, dass der Prozessor brennt, hat mir sehr geholfen. Daraus konnte ich erkennen, welche Pakete und Timer zuletzt ausgefĂŒhrt wurden, und der Kreis der VerdĂ€chtigen wurde allmĂ€hlich enger. Als Ăbel war Cross-Blocking nicht mit zwei Threads, sondern mit drei, so dass es nicht möglich war, ein detaillierteres Bild zu erhalten.Dann erinnerte ich mich an den alten Rechen und fing an, den Code fĂŒr die Verwendung von Schlössern zu ĂŒberprĂŒfen. Wie sich herausstellte, wurden im Laufe der Jahre mehrere Refactorings durchgefĂŒhrt, und SpinLock wurde schrittweise durch Monitor.Enter / Monitor.Exit und hĂ€ufig durch eine einfache Sperre ersetzt. Und dann fiel mir plötzlich etwas aufEric Gunnersons Artikel , der besagt, dass Sie es viel einfacher machen können: Verwenden Sie Monitor.TryEnter ĂŒberall mit einer ZeitĂŒberschreitung. Wenn die Blockierung fehlschlĂ€gt, lösen Sie eine Ausnahme aus. Dies ist eine unglaublich einfache und sehr effektive Methode. Wenn der TryEnter-Aufruf irgendwo lĂ€nger als 30 Sekunden gewartet hat und ausgefallen ist (und solche Verzögerungen nicht fĂŒr die Logik charakteristisch sind), muss dieser Ort untersucht und ĂŒberprĂŒft werden, wer so lange hĂ€tte dauern können und das Sperrobjekt nicht erhalten hat. Als ich Asche auf meinen Kopf streute, wurde mir klar, dass ich vor 15 Jahren alles auf diese Weise hĂ€tte aufrĂ€umen können. Es war nicht notwendig, das Rad neu zu erfinden, um die âTiefe des Lochsâ zu berechnen. Aber vielleicht war es dann das Beste.
â , . , - . , - . SOS.dll. Son Of Strike WinDbg .Net , , . , .Net GC. - sos.dll 50. , , , . , â deadlock!
, . â . â , , , , ! , . SpinLock try/finally . , , â , SpinLock , , , , , . 8 , . , : , , â â. , . , , â .
, , Xeon 5130x2 8 . 2000, 2500, . , , , , -, . .
Nun, dann kam der 4. Fahrer zu einem neuen Projekt, wie einmal zu einem Emulator. Nur hatte er keine Zeit, populĂ€r zu werden. Das Vorhandensein von bis zu drei kritischen Problemen zu Beginn des Projekts warf ihn jedoch schnell um. Und das Spiel war ĂŒberhaupt nicht Mainstream. Dies ist aber auch kein Thema fĂŒr diesen Artikel.
PPS Der Artikel verwendet Illustrationen eines unbekannten KĂŒnstlers Parsakoira mit der Signatur âChoW # 227 :: VOTING :: 4 Horsemen of the Apocalypseâ, vermutlich von der bereits verstorbenen Website conceptart.com:
https://www.pinterest.com/pin/460141286926583086/
https : //www.pinterest.com/pin/490681321879914768/