PSこの記事を書いているとき、元のプロジェクトは単一のIPに害はありませんでしたが、海賊行為の精神(有料ゲームの無料サーバー!)で飽和していましたが、権利を侵害せず、著作権所有者のコードはそこで使用されておらず、サーバーは完全に正直に購入されたゲームクライアントとサウンドの調査に基づいていました開発者の感覚。この作品は、古いプロジェクトと現代のプロジェクトの両方で、著者が直面した課題とそれらを解決するための元の方法についてのみ説明しています。単に事実を列挙するのではなく、物語の物語のスタイルについて事前に謝罪します。
前書き
.Netはサーバー用ではないということは好きなだけ議論できますが、当時(そして今)、メモリの割り当てやアセンブリについてあまり考えずに、スクリプトの形式でロジックを記述し、コンパイルしてロードできるという非常に賢明なアイデアのように思えました。破片、ポインターなど。実際、これにより、ビジネスロジックのスクリプトを資格のない開発者に委任して、コードレビューのみに制限することができます。ただし、このためには、カーネル自体が障害なく動作することを確認する必要があり、2004年と2020年の両方で、オンラインで10〜15で障害が発生し始めました。
2004年には、すべてがWindows Server 2003、.Net 1.1、MSSQL 2000で回転していました。サーバーとホスティングはWnetプロバイダーによって提供され、プレーヤーからの寄付で新しいサーバーが構築されました。このプロジェクトは純粋に商業的なものではなく、バナーとプレミアムアカウントからの最小限の収入がアップグレードに使用されました。最新のサーバーは、Hetznerクラウドでホストされているデータ用のMariaDBを使用して、.Net4.7互換モードのDebianでMono上で実行されます。長い間、ゲームは無料であるべきだと信じていた燃えるような目を持つ理想主義者はすでに存在せず、ゲームアイテムを寄付して販売することはすべての興味を殺します。今、このキャラクターはかなり灰色になり、経験への熱意を変え、スタートアップは喜びと収入の両方をもたらすべきだと確信しています。
しかし、物語はそれについてではなく、自作のサーバーとその問題についてです。
第1章害虫
. , , , . , , . , , . Visual Studio, - , . EventLog .2020年には、サーバーアプリケーションは、原則として、Linuxの別の画面で実行されるコンソールアプリケーションのみでした。Visual Studioで起動するためのオプションはこれ以上ありませんでしたが、ロガーは何年にもわたって非常に高度になり、UnhandledExceptionsはネットワーク内のバニーのように遭遇し、原則としてネイティブコードはありませんでした。ただし、OOMとStackOverflowExceptionによるクラッシュから私を救うことはできませんでした。StackOverflowExceptionの場合のスタックの深さは10倍になり、数百キロバイトのログが同じタイプのメッセージでいっぱいになり、通常のスタックトレースの書き込みが拒否されます。しかし、いずれにせよ、>> log.txtにリダイレクトすることで、誰がどこで責任を負うのかをすぐに理解することができました。Telegramボットは個別に支援し、サーバープロセスが停止したことを通知しました。
— , 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 .
これらすべてが全体像を大幅に改善し、サーバーがクラッシュする頻度ははるかに少なくなりました。ほとんどの場合、メモリが不足したときだけです。
それからそれはただの技術の問題でした。ログの調査では、スタックオーバーフローはコアではなく、ビジネスロジックで明らかになりました。ロケットが別のロケットまたは鉱山と衝突し、爆発し、これが最初のロケットの爆発を引き起こしました。全体として、これは通常の仕事の瞬間ですが、それは私が過去の長い間忘れられていた悪魔と戦う奇妙なdéjàvuを感じたときです。そして、疫病の新しい(または長い間忘れられていた)原因が現れました-リソースの不足。
第2章うれしい
— 256 , ! - , , , , — , OOM - . , — Visual Studio ( , ), WinDbg (), - dotTrace (). , . — , 1.7, . . 100%. , , , — ~100 . Maoni Stephens Rico Mariani GC, LOH (Large Object Heap) .Net. , (pin) , Gen 2, — LOH,メモリが4GB未満の最新のサーバーでは問題が発生し、数回クリックして1回再起動するだけで、クラウドソリューション用に8〜16ギガバイトを追加できます。それにもかかわらず、メモリがリークし始め、プロセッサの負荷が100〜150%(8コアの800%に基づく)に跳ね上がったとき、私は再び20歳の学生のように感じ、貪欲な車の火室でギガバイトとギガフロップを燃やしました。それは奇妙で、普通ではなく、愚かでした。以前のように、ゲームが(ラグはあるものの)正常に実行され続けたが、何も中断されなかったことは特に不快でした。もちろん、メモリがなくなるまで。. — , , , (, .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% .
何年にもわたって、軽量スレッド(別名ファイバー)は何とか現れたり消えたりしました。そのため、.Netのシステムスレッドにアクセスできなくなりました。管理対象スレッドであり、MonoではまだProcessThreadにアクセスできません。内部にはスタブしかありません。スレッドの診断ははるかに複雑になりましたが、今では独自のスレッドプールを使用して、すべてのスレッドが計算され、名前が付けられました。それぞれについて、正確な統計が保持され、現在実行されているもの、特定のタスクにかかる時間が示されます。このため、問題がシステムではなく私のコードにあることがすぐにわかりました。スレッドの統計では、zhorがビジネスロジックの実行に関連付けられていることが示されました。一部のアクションは、必要以上に100倍頻繁に実行されます。今、私はリソースに制限されていませんでした、そのため、各スクリプトとタイマーの呼び出しに追加のログを非常に静かに提供し、各イベントの実行時間を測定し、1週間の実験で、問題が何であるかを自信を持って伝えることができました。あるNPCが別のNPCを攻撃しようとしていて、両方が岩に引っかかっていたため、移動できず、視線がないためにお互いを撃つ試みが即座に中断されたことが判明しました。しかし同時に、行動を計算する各サイクル(15ms)で、パスを計算しようとし、射撃を開始しましたが、発砲が不可能であったため、銃はリロードされず、次のサイクルが繰り返されました。ゲームの数日間、そのようなNPCが何百も採用され、最終的にサーバーのすべてのリソースを使い果たしました。解決策は、動作を修正してスタック状態を減らすと同時に、ショットが失敗した場合でもリロード時間を短くすることでした。
そして、サーバーがフリーズし始めました。
第3章寒さ
2005年の秋は簡単なことではありませんでした。仕事の状況が不確かで、アパートの家賃が突然2倍になりました。私はゲームサーバーに満足しているだけでした-すでに何百ものオンラインサーバーがありましたが、問題はそこから始まりました-全世界が凍結し始めました。最良の場合、pingは歩き続けるか、いくつかのタイマーが機能しました。また、すべてがフリーズし、トラフィックが停止し、サーバーアプリケーションを強制終了して再起動する必要がある場合がありました。以前のように、かなりの消費とブレーキのために、実行中のサーバーにデバッガーで接続することは不可能でした。何らかの理由で、VisualStudioはこれから単にクラッシュまたはフリーズします。2020年の10月の寒い日の1つで、サーバーが突然ハングアップしたため、ライブストリーマーの予定されていた到着が中断されました。承認は機能しましたが、世界に入ることができず、テレグラムボットは沈黙していました。問題をすばやく検索しても、ログには何も表示されず、メモリの問題はなく、どのスレッドも飢えていませんでした。止まった。マトリックスからの猫と卑猥な振る舞いの女性について何度か声を出して言ったので、私は行き詰まりを探しに行きました。 MicrosoftがMiguelde IcazとXamarinを購入した後、Monoのドキュメントは哀れな光景です。そこにはありますが、最新ではなく、どこにも通じていません。たとえば、ページのデータの3/4gdbを使用したモノラルでのデバッグについては適用できず、機能しません。 gdbを介してフリーズしたサーバーに接続できましたが、mono_pmipを呼び出すコマンドなどで、主に構文エラーについて理解できない回答が得られました。奇跡的に、gdbがパラメータとmono_ *コマンドの結果を特定のタイプにキャストすることを望んでいることに気づき、クロスブロッキングでフリーズしたスレッドのリストを取得できるようになりました。しかし、リスト内の番号は、psコマンドまたはサーバーのManagedThreadIdのいずれとも一致しませんでした。プロセッサの書き込みを見つけるために行った拡張ロギングは、非常に役立ちました。それから、どのパッケージとタイマーが最後に実行されたかを理解でき、容疑者の輪を徐々に狭め始めました。悪として、クロスブロッキングは2つのスレッドではなく、3つのスレッドで行われたため、より詳細な画像を取得することはできませんでした。それから私は古いレーキを思い出し、ロックを使用するためのコードを見始めました。結局のところ、いくつかのリファクタリングが何年にもわたって経過し、SpinLockは徐々にMonitor.Enter / Monitor.Exitに置き換えられ、多くの場合、単純なロックに置き換えられました。そして突然私は目を引いたEric Gunnersonの記事によると、はるかに簡単に実行できます。タイムアウトを設定してどこでもMonitor.TryEnterを使用し、ブロックに失敗した場合は例外をスローします。これは非常にシンプルで非常に効果的な方法です。TryEnter呼び出しが30秒以上待機して失敗した場合(このような遅延はロジックでは一般的ではありません)、この場所を調査して、誰がこれほど長い時間をかけてロックオブジェクトを与えられなかったかを確認する必要があります。頭に灰をまき散らして、15年前にこの方法ですべてをきれいにすることができたのだと気づきました。「穴の深さ」を計算してホイールを作り直す必要はありませんでした。しかし、多分それはその時最高でした。
— , . , - . , - . SOS.dll. Son Of Strike WinDbg .Net , , . , .Net GC. - sos.dll 50. , , , . , — deadlock!
, . — . — , , , , ! , . SpinLock try/finally . , , — , SpinLock , , , , , . 8 , . , : , , “ ”. , . , , — .
, , Xeon 5130x2 8 . 2000, 2500, . , , , , -, . .
さて、それから4人目のライダーは、かつてのエミュレーターのように、新しいプロジェクトに来ました。彼だけが人気になる時間がありませんでした。それでも、プロジェクトの開始時に3つもの重大な問題があったため、彼はすぐに倒されました。そして、ゲームはまったく主流ではありませんでした。しかし、これもこの記事のトピックではありません。
PPSのシグネチャを持つ未知のアーティストParsakoiraによる記事が使用するイラスト「チョウ#227黙示録の::投票:: 4騎手」、おそらくすでに故人のウェブサイトconceptart.comから:
https://www.pinterest.com/pin/460141286926583086/
HTTPS ://www.pinterest.com/pin/490681321879914768/