「ストライク」に関するレポート:「永続的なデータベースの依存関係を取り除く方法」

4月10〜11日に、私たちのチームはロシア地域で最大のストライキ会議に参加し、ウリヤノフスクで4回目の開催となりました。 IT企業は、自社の製品に精通し、空席について学び、競争に参加できるスタンドを提示しました。



今年、XIMADは、モバイルセクションの開発に関連して、カンファレンスで製品のスタンドを発表することも決定しました。 これは私たちのプロファイルの方向です。 レポートに耳を傾け、同僚と経験を交換し、ゲームで使用されているテクノロジーに関する多くの質問に答えました。



2日間で、130の専門家が8つの会場で講演し、さまざまな分野で合計150の関連レポートが配信されました。 開発者のAlexey Klyuchnikovは、当社のフラッグシップマジックジグソーパズル(2M MAU、600K DAU、最大50Kのインタラクティブインタラクションを備えたオンラインプレイヤー)を使用して、トピック「モバイルゲームでインタラクティブにする、または永続的なデータベース依存を取り除く方法」に関するレポートを発表しました。



以下は彼のスピーチのテキストです。





ゲーム開発のサーバー側の主な問題は何ですか? そして、それがhiloadであるという事実! プロジェクトが完了していない場合、これは1つの場合にのみハイロードではありません。 ゲームが市場に参入すると、広告が始まり、プレイヤーの流れが進み、最初の数日間は、小規模ではありますが非常に現実的な負荷になります。 そして、あなたが成功したら...



会議で高負荷に関するレポートを訪れたところ、全員がほぼ同じ方向で作業していることが明らかになりました。 負荷の高いプロジェクトが最初に依存するのはデータです。ほとんどのスピーカーは、複製、シャード、非正規化などの方法について話しました。 私たちはこの運命を逃れませんでしたが、選択された道はわずかに異なっていました。 データベースでの作業を最小限に抑えることが提案されています。 実際、彼女を追い払ってください。 どうやってやるの? しかし、非常に簡単です。



アイデア

プレーヤーがログインするサーバーを作成し、選択されたプロセスがプレーヤーに対して起動され、プロファイルがデータベースからそこにロードされ、このプロセスですべてのプレーヤーのアクションが実行されます。 プレーヤーはしばらくアクティビティを表示しないので、10分間待ってデータベースにプロファイルを保存し、プロセスを完了します。 その結果、ログインを使用してデータベースから1つを読み取り、ログアウトを使用してデータベースに1つのレコードを取得しました。



何が欲しい?

ゲームセッションごとに1つの読書と1つのレコードを取得しました。 したがって、私たちの決定が何人のプレイヤーを引き寄せるかを計算し、予測することができます。 多くの人が、たとえばmysqlで1秒あたりの読み取り回数を記録する場合に、単純なキー/値プレートがどれだけ与えることができるかを理解できると思います。 そして、1日あたり何人のプレーヤーがそのようなベースを伸ばすか。 この数字は印象的であることが判明し、この数字についてはデータベースの問題から身を守ることができました。 何が良いでしょうか?



実装

実装には、Erlangを使用します。Erlangでプロセスを操作するのが良いので、...それだけです。

Erlangが提供するもの:箱から出してすぐにプロセスを開始、停止、およびそれらの間でメッセージを送信できます。 これは、あるプレーヤープロセスが別のプレーヤーの別のプロセスにメッセージを送信できることを意味します。 また、同じ原則に従って、ゲームロジックを提供するプロセスと対話します。 この場合の対話機能もすぐに使用できます。



ニュアンスを順番に処理します。





ここではすべてが標準的であり、各プレーヤーには登録時に一意の識別子が割り当てられ、すべてのアドレッシングは後で実行されます。 アドレス指定に追加のキーを使用したい場合がありますが、これは次の理由で回避するのが最善です:メッセージがプレーヤーにオフラインで送信されるときにメッセージを送信するには、プロセスを開始し、このプレーヤーのプロファイルをロードしてからメッセージを送信する必要があります。 ただし、アドレス指定に異なるキーを使用すると、1人のプレーヤーで2つ以上のプロセスが開始されたときに、追跡が困難な衝突が発生する可能性があります。





Erlangに組み込まれたロガーには、プロセスを動的に開始および終了するために使用できない重大な欠点があるため、gprocロガーを使用します。 レジストラは、プロセスを登録し、要求に応じてPidを発行する必要があります。 そして、プロセスの終了時、または「登録解除」を行うことを拒否したとき。





上記のように、メッセージがプレーヤーに到着すると、どのPidからメッセージを送信するのかという質問があり、そのようなプレーヤーにプロセスがない場合は、それを開始する必要があります。 各操作には短い時間がかかりますが、2つのメッセージが同じプレーヤーに届き、ほぼ同時に両方がレジストラーから否定的な応答を受け取り、プロセスを開始しようとする場合があります。 その結果、そのうちの1つが最初に起動し、2番目が例外を受信し、メッセージが失われます。 プロセスを非同期的に開始することはできず、それらを開始するためにキューを配置する必要があります。 これを行うには、すべての申し立てをレジストラに向け、プロセスを開始するプロセスを開始します。 しかし、ボトルネックが発生するため、このような「process_starting_worker」ではなく、少なくとも分割の残りの部分で、便利なアルゴリズムを使用してユーザーIDへのすべての呼び出しを分配する100ワーカーなどのプルが必要です。





プロセスを停止することも同様に興味深いことです。 プロセスを完了するときは、データベースへのプロファイルの保存、レジストラのチェックアウト、すべての友人への送別メッセージの送信、実際のプロセスの完了など、いくつかのアクションを実行する必要があります。 これらのすべてのアクションを次々に実行することはできません。プレーヤーが突然生き返るか、プロファイルの保存中にメッセージを受信する可能性があるためです。 したがって、各操作の後、メッセージキューを読み取り、その中に何かが見つかった場合、それを処理し、レジストラから退出する前の場合はプロセスを通常の状態に戻し、レジストラから退出した後、プロセスが未登録であることを送信者に正直に返信する必要があります。送信者がメッセージを再送信する必要があること。





ご覧のように、すべてのメッセージにレジストラが使用されており、これがボトルネックになるため、プロセスでPidをキャッシュすることは理にかなっています。 プロセス間でメッセージを最初に交換した後、各プロセスは相手のPidを記憶し、将来、レジストラに連絡することなく通信します。 そのため、プロセスの最後に、キャッシュからすべてのPidに完了を通知するアクションが追加され、誰もが完了中のプロセスからキャッシュをクリアできるようになります。



2番目に考えることは、読み取りの最適化です。 プレーヤーは友達のプレイを見るべきですか? そして、彼はこの情報をどのように受け取るべきですか? 毎回、結果に達すると、すべての友人または各友人にインタビューし、この結果をプロファイルに書き込むすべての友人に「自慢」し、友人の結果を表示するクエリを生成しませんが、プロファイルからすぐにそれを提供します。 どのアプローチを取るかは、データ使用の性質によって異なります。 読むことは書くことよりも頻繁である場合、この方法で行うのは理にかなっています。





まず、負荷を推定し、データベースの下のサーバー+コードの下のサーバーが1日あたり数十万人のプレイヤーを伸ばすことを取得できます。 Erlangはかなり公平にメモリを使用するため、プレーヤープロファイルごとに平均100kbを使用する場合、5万人のプレーヤーにサービスを提供するには5GBのRAMが必要になります。 言い換えれば、サーバーを32〜64 GBで使用し、高い確率で、プロジェクトの大成功に対応する必要性を忘れています。



第二に、それでも大きな成功がもたらされた場合、プレイヤーのIDによってデータベースを「ダンプ」し、CSNを使用してプレイヤーを異なるErlangノードに配布することを妨げるものはありません。 ここでの問題はレジストラのみにあり、クラスタモードで作業できる必要があります。 Gprocはそれを実行できますが、テストが示しているように、それは最後までではありません。 必要なのは、それを少し修正するか、別のレジストラを取得することだけですが、これは別のトピックであり、おそらく別の記事です。



おわりに

解決策は見た目ほど単純ではありませんでした。 メッセージの交換、配信を保証する方法、ロールバックする方法、たとえばメッセージチェーン、同期的に送信するメッセージ、非同期的に送信するメッセージなどについては、まだ多くの質問があります。



しかし、そのようなアーキテクチャの使用からの最も重要な結論は、プッシュ不可能なものを押し込み、実行可能なサービスを受けたということです。 データベース内のクエリで各プレーヤーのくしゃみの古典的な実装を使用して不可能なこと。



All Articles