ソーシャルゲームでのチーム処理の遅延

みなさんこんにちは!



今日は、ソーシャルゲームを開発する際に直面した問題の解決策を紹介します。 ゲームクライアントはフラッシュで記述され、バックエンド用にphpが選択されました。 ゲームは時間管理ゲームを指します。

作業スキームは次のように選択されました。

  1. プレーヤーはクライアントでアクションを実行します
  2. クライアントはアクションの可能性をチェックします
  3. サーバーにコマンドを送信します
  4. サーバーはアクションの可能性を確認し、コマンドを実行してデータベースに変更を加えます
  5. クライアントは、すべてが正常であることを通知されるか、エラーについて通知されます




プレーヤーの数が急激に増加するまで、すべてが完全に機能しました。

まず、PHP側でブレーキが開始されました。 この実装の主な問題は、各プレイヤーのアクションに対してサーバーがひきつり、コマンドを実行する前にマップ上のオブジェクトを計算する際に非常に多くの計算を実行することでした。 この問題は、PHPハンドラーを使用してサーバーを追加することで解決しました。

その後、mysqlのパフォーマンスに遭遇しました。 リクエストが多すぎました。 シャーディングはシステムに組み込まれていなかったため、可能な限り最善を尽くしました。 何かがmongodbに転送され、どこかでキャッシュの処理が改善されました。



ちなみに、mongodbは一見すると単純なストレージではありませんでした。 シャーディングが有効になっていて、正しいインデックスがオンになっていることを考えると、その時点では理解できなかったブレーキがまだかかっています。 時々、ログ内の一連のクエリが十分なスリープ状態になっただけで、突然退屈になりました。 1秒後、同じリクエストは同じリクエスト数で正常に機能しました。 しかし、これは別の投稿のトピックです。





実際、新しい同様のプロジェクトでは、異なるクライアント/サーバー相互作用スキームを使用することが決定されました。

それが私が話したいことです。







動作原理は次のとおりです。

  1. ゲームを開始すると、プレーヤーの完全なゲーム状態と現在のゲーム残高を受け取ります
  2. プレーヤーはクライアントでアクションを実行します
  3. クライアントはそれらを検証しますが、サーバーに送信しませんが、蓄積します
  4. いずれかの条件に達すると、クライアントはこれらすべてのコマンドをバンドルでサーバーに送信します
  5. サーバーは、コマンドが正しい形式であることを確認し、すべてが正常であることをクライアントに単に通知します
  6. すべてのコマンドはキューに保存されます
  7. デーモンもサーバー上で実行されており、定期的にこのキューからコマンドのパックをプルして実行します




これは、コマンドの遅延処理を備えたスキームであり、次の利点があります。

  1. サーバー要求の数が大幅に削減されます
  2. データベースへの保存はパックのすべてのコマンドを処理した後にのみ行われるため、コマンドの処理中にmysqlの呼び出し回数が減少します




もちろん、タスクを解決する可能性が高い欠点もあります。 そして、ここでプロジェクトの要件を確認する必要があります。

リストは次のとおりです。

  1. このユーザーのキュー全体が処理されていない時点での現在のゲーム状態の取得
  2. 一部のコマンドでは、リアルタイムでの実行が必要です(たとえば、本物の購入)
  3. クライアントで不正行為をしているプレーヤー(チームはクライアントで実行できましたが、サーバーでは失敗しました)
  4. マルチスレッド処理




プロジェクトはGearmanキューサーバーを使用します。 それ以外の場合、すべてが標準です:php + mysql + memcached。

現在の実装には、mysqlおよびmemcachedのシャーディングが含まれています(1つのmemcachedサーバーでは不十分です)。



上記のタスクがどのように解決されるかについて話しましょう。



コマンドのパックがサーバーに送られました。 プレーヤーはゲームを閉じてから、再び開始しました。 キューはまだ処理されていません。


Gearmanはデータベースではなく、内部に保存されているデータで何らかの方法で検索できないため、データベースをコマンド処理に接続し、特定のユーザーがどの状態にあるかを常に把握できるphpで記述されたモジュール処理中のコマンドと処理中にエラーがあったかどうか。

クライアントが、キュー全体がまだソートされていないプレーヤーのプロファイルを要求すると、10秒後に待機してプロファイルを要求するという要求に応じてメッセージを受信します。 これは、プロファイルが取得されるまで繰り返されます。

フィッティングにより、プレーヤーのすべてのコマンドを完了するのに最大20秒かかりますが、これは許容されます。



特定のコマンドをリアルタイムで実行する


これは問題ではない可能性が高いです。 ここで特定のコマンドを実行する機会は、現在実現されています。 キューの処理で問題が発生した場合に、プレイヤーがゲームに持ち込むお金を失うことがないようにする必要があります。



クライアントをだましているプレイヤー。 サーバーとクライアントのロジックの違いによるエラー


状況:プレーヤーはクライアントをひねり、お金を投げました。 その後、クライアントは彼に建物の購入を許可します。 次に、プレーヤーは建物でいくつかのアクションを実行します。 プレーヤーには実際にはお金がないため、発生し得ない一連のイベントが作成されます。

サーバーは4パケットのコマンドを受け取ります。 2番目のものには、建物コマンドの購入が含まれます。

キュー処理が開始されます。 コマンドの最初のパックは正常に処理され、2番目のコマンドでは論理エラーが発生し、建物が購入に失敗するという事実につながります。

現時点では、データベース内のこのユーザーに対して、エラーが発生したときにタイムスタンプが付けられます。 すべてのコマンドパックには、いつ到着したかに関する情報が含まれているため、ハンドラーが3パックと4パックを受け取った時点で、作成時間はエラー時間よりも短いため、それらをスキップします。

この時点で、クライアントは5パックのコマンドを送信しますが、すべてが正常であると応答する代わりに、ゲーム状態を再起動する要求を受信します。 クライアントが最後にゲームの状態を受け取ったのはいつかを知っています。 そして、この時間が最後のエラーの時間より短い場合、サーバーは処理コマンドの受け入れを拒否しません。



マルチスレッド処理


3つのスレッドがあります。 さまざまなユーザー向けの3つのコマンドパック。 ハンドラーは、これらのパケットを同時に選択して処理するだけです。 すべてが素晴らしいです。

1人のユーザーに3パック、キューに複数の無料ハンドラーがある場合、状況はより複雑になります。 1人のプレイヤーのチームの2つ以上のパックの並列処理が開始される状況を回避するために(順番に処理する必要があります)、ユーザーが現在処理されていると言うことができる一種の信号機が実装されています。 その場合、2番目のハンドラーはデータをキューに戻しますが、 優先順位より高くなります 。 2つのタスクが3より先に残るように優先順位が変更されます。そうでない場合、1パケットを処理した後、プロセッサーは3パケットを受信します。 この信号機の制御は、ハンドラーによって処理されます。 コマンドを受け取った後、ユーザーが処理され、処理の最後に-準備ができたと言います。



これが実際に私が伝えたかったことのすべてです。 実装について掘り下げたくありません。なぜなら、そこに重要なものは何もないからです。

フレームワークはYiiフレームワークです。 デーモンの場合、yiicのコマンドが使用され、次のように開始されます

nohup ./yiic que work> / dev / null&



チームは子孫の数を監視します。 何らかの理由で落下した場合、新しいものを起動します。

子孫はライン分析のためにGearmanWorkを登録します。



ご清聴ありがとうございました! 親指を立ててサインアップしてください:)



All Articles