Upgrade to Pro — share decks privately, control downloads, hide ads and more …

リアルタイムサーバーチームが初の大規模ローンチに向けてやってきたこと/ColoplTech-0...

COLOPL Inc.
February 10, 2023

 リアルタイムサーバーチームが初の大規模ローンチに向けてやってきたこと/ColoplTech-02-02

※資料内の参照リンクを選択し閲覧する場合は、ダウンロードをお願いいたします

\積極的に技術発信を行なっております/
▽ Twitter/COLOPL_Tech
https://twitter.com/colopl_tech

▽ connpassページ
http://colopl.connpass.com

▽ COLOPL Tech Blog
http://blog.colopl.dev

COLOPL Inc.

February 10, 2023
Tweet

More Decks by COLOPL Inc.

Other Decks in Technology

Transcript

  1. 3 • 少し前に Prizm チームが開発に関わったゲームがリリースされました ◦ 内製リアルタイム通信フレームワーク Prizm を用いた PvP

    ◦ Open Match という k8s native な OSS を利用したマッチメイキング • Prizm(リアルタイム通信フレームワーク)を開発しているチームのメンバー としてゲームのリリースに向けてやってきたことを話します ◦ リリースに役立った Prizm の機能 ◦ 負荷対策、負荷試験で得られた知見 今日話すこと
  2. Prizm とは • コロプラで開発している対人や協力などリアルタイム性のある ゲームのためのフレームワーク ◦ サーバー(Go): いわゆるリアルタイムサーバーを作るためのフレームワーク ◦ クライアント(Unity):

    リアルタイムサーバーとの通信の基盤ライブラリ • サーバーを介してクライアントがメッセージを送り合うことを想定した作り ◦ 物理演算などはしない 5 Room A Room B Room C こんなイメージ
  3. • 複数のプレイヤーをルーム単位でまとめ、ルーム内でメッセージを送り合う ◦ クライアント→サーバー / サーバー→クライアント の両方の経路が存在 ▪ サーバーから送信者以外のクライアントに受信したメッセージを送信することで リレーサーバーを実装できる

    • サーバー側に任意のロジックを実装 ◦ メッセージを受信した際のメッセージハンドラー ◦ 一定時間後に処理を実行(Go 言語の time.AfterFunc のような機能) • ……and more!詳しくは2022/03/17の発表をご参照ください 6 Prizm の基本機能 今回はリリースにあたって特に役立った機能や リリースを受けて開発した機能を紹介
  4. • Prizm のクライアントに通信の遅延・ロスをシミュレートする機能を実装 ◦ メッセージを送信する前やメッセージを受信した後にライブラリで処理を挟む ◦ アプリ的にはネットワーク側の遅延・ロスと同様の挙動に見える 7 通信の遅延・ロスをシミュレートする機能 ア

    プ リ 実 装 ラ イ ブ ラ リ ライブラリが 送信前や受信後に 遅延やロスを挟む 送信されたパケットが 遅れて届く / 届かなくなる 受信したパケットが 遅れて届く / 届かなくなる クライアント Prizm サーバー
  5. • Prizm ではリアルタイム通信を実現するためコネクションを張り続けている ◦ TCP ではコネクションを張りっぱなしにする ▪ 送信元、送信先の IP:Port と

    IP 層のプロトコル番号で区別している ◦ UDP はコネクションレスなので送信元を IP:Port の組で区別して擬似的に実現 • しかしこのようなコネクションはゲームの途中でかんたんに切れてしまう ◦ Wi-Fi ↔ モバイル回線の切替 ◦ モバイル回線での基地局のハンドオーバー ◦ Wi-Fi ルーターの調子が悪くて通信が瞬断 ◦ …… • 開発中にコネクションの切断が問題となったのでライブラリ側に コネクションが切断してもセッションを維持する機能を実装 9 コネクション切断への対応
  6. • TCP や UDP のコネクションが切れたときにも内部的に再接続して Prizm レイヤーのセッションを維持する仕組みが実装されている ◦ ゲーム開発者はL4のコネクションを気にしなくて良くなる •

    切断している間送れなかった Reliable なメッセージを再送する機能も実装 ◦ Unreliable なメッセージはロスしたものと扱うことにして再送しない 10 セッションを維持する機能 再接続の要求が来たぞ メッセージを送り直して あげよう コネクション切れてそう だから再接続しよう メッセージも送り直そう
  7. • コネクションの再接続に時間が数秒程度かかる ◦ コネクションの切断をはっきりと取れない場合があり ping を送り合って状態を推測 ◦ 切断したと判断するまでの猶予時間を何秒か持つ必要がある ▪ たまたまネットワークの調子が悪い場合を排除

    ◦ リリースしたゲームはターン制のゲームなので数秒の猶予は許容できた ▪ アクション性の高いゲームだと他の対応を考える必要がある 11 セッションを維持する機能の問題点 ping が来るはずなのに 来ないな…… コネクション切れてそう ping を送ったのに 返ってこないな…… コネクション切れてそう
  8. • マルチプレイゲームにおける通信頻度はできるだけ最小化したい ◦ インフラコストが減る ◦ クライアントの消費電力が減る • しかしゲーム開発している中で通信頻度を意識することはとても難しい ◦ 負荷対策より

    PoC(面白いゲームになるかの検証)を優先 ◦ 開発環境(社内ネットワーク)では問題が顕在化しづらい ◦ とりあえず OnUpdate() で送信すれば動く • 通信頻度をリリース直前に減らすのは大変なので計測する仕組みを強くした ◦ 意外な理由で通信量が増えることがあるのでそもそも気をつけるより計測した方がいい ◦ 通信頻度を落とせない仕様は早めになんとかしたい ◦ フレーム間の補完など追加の実装が必要な場合もあり早期発見が大事 12 通信頻度の計測
  9. • サーバー: クライアントが通信しすぎていたらワーニングログを出す機能 ◦ クライアントごとの秒間リクエスト数(Request Per Sec, RPS)を 計測し一定の値を超えたらログ出力 ◦

    しきい値はデフォルト10RPSだが設定で変更可能 ◦ 詳細よりも大まかな傾向を掴むためのもの • クライアント: 通信周りのプロファイルを取得・表示する機能 ◦ メッセージごとの通信回数や頻度 ◦ ↑を表示する Unity の拡張 ◦ 複数のクライアントが繋がるサーバーでは取得・表示しづらい詳細な情報を ハンドリングできる 13 通信の頻度を計測する仕組み
  10. • リリースに向けて実施する負荷試験の一部としてマルチプレイ部分に 注目した負荷試験も実施した ◦ Prizm サーバー、Open Match を含めた試験を実施し負荷に耐えるかを検証 ▪ Open

    Match: レートマッチングなどに向いた k8s native なマッチメイキング OSS 16 マルチプレイの負荷試験 DBなど API server Prizm server Open Match bot ここも試験!!
  11. • シナリオを作成するために実施したログの収集方法が原始的すぎた ◦ Unity にログを仕込む→ログを awk する→更に手で不要な情報を消す といった流れ ▪ 他のメンバーに作業を依頼するときに伝える情報量が増える

    ▪ 本質的ではないポイントでつまづく • Prizm サーバーにメッセージ受信時に共通で呼ばれる処理をフックできる 機能を実装し GCP Cloud Logging でログを収集できるようにした ◦ サーバーにログを仕込む→Cloud Logging でログを絞り込む といった流れで 通信の流れを追えるようになった ▪ 他のメンバーに作業依頼するときも Cloud Logging のクエリだけ共有すれば OK ◦ 本当にうまく機能するかは次の負荷試験で検証予定だが リリースに向けて必要な機能を発見できたのは収穫と感じた 19 シナリオを作成するためのログ収集について
  12. • マッチング / インゲームや自ターン / 相手ターンといったゲームの 進行状態を負荷試験 bot で再現し適切に動作させるのが大変だった ◦

    状態遷移図を書いてから実装するなどで対策はした ▪ ターン制のゲームだったこともあり正常系の整理はうまく行った • 正常系の実装より異常系のハンドリングが大変 ◦ マッチングに失敗した後の挙動 ◦ 対戦相手の通信が不調になって待ちがタイムアウト ◦ メッセージが届く / 届かないに依存して遷移が変わる際の挙動 ▪ e.g., 「移動した」の後「目的地に到着した」が送られてくる場合 ▪ 「目的地に到着した」が遅れて届いた場合次の受信待ちのタイミングで 「目的地に到着した」だけが送られたように見えるケースがある • 正常系だけ考えていると「移動した」だけを待ってしまう 20 負荷試験 bot のゲーム進行状態のハンドリング
  13. • 負荷をかける規模を大きくすると動かなくなる ◦ 異常系の想定が甘くて想定していない状態・動作になってしまう ▪ botのデバッグは本質ではないので減らしたい…… ◦ 規模を大きくするたび問題が出てくる可能性を想定してスケジュールを組む必要が あると感じた ▪

    コスト的にも問題の切り分け的にもいきなり本番想定規模の試験は 動かしづらいのも難しいポイント • マッチングがうまくいかない ◦ インゲーム・リアルタイムサーバーの問題よりマッチングの問題を解消する 時間の方が長かった印象がある ◦ マッチングも規模を大きくするたび問題が出てくる可能性がある ◦ Open Match を使う場合はマッチングロジックがボトルネックになってないか注意 21 負荷試験を実施する過程でよく出会った課題
  14. • Pod に割り当てるリソース量を決定するために1Pod あたりにかかる負荷を 増やしていたとき、Pod 単位ではリソースに余裕がある場合でも Node 単位で はリソースが逼迫していることがあった ◦

    リアルタイムサーバーの処理負荷が小さかったこともあり Pod へのリソース割り当てを 小さくしていたので1台の Node に多くの Pod が乗った ◦ Node 単位で捌くリクエスト数が多すぎたりコンテキストスイッチがたくさん発生した ために Pod より先に Node のリソースが逼迫したのではないかと考えている • サーバーのリソース調整の際は Pod だけではなく Node 単位でもリソースの 使用状況を確認する必要があることが分かった 23 リソースの割り当てについて
  15. • Prizm では TCP の暗号化に TLS を用いており接続時にハンドシェイクを実行 • 負荷試験の結果この TLS

    ハンドシェイクが思った以上に重いことが分かった ◦ 断続的に bot で計測している ping の RTT が遅くなるケースがあった ▪ 99.9%値で50ms → 15,000ms ◦ リソースは余っているように見えたがサーバーの立ち上げ直後に CPU usage が高くなる 傾向があった ◦ Cloud Profiler を見たところ TLS ハンドシェイクの負荷が高いことが分かった ▪ ゲームの処理が軽くメッセージ頻度も小さいため相対的に負荷が高くなった • TLS ハンドシェイクの負荷に合わせたスペック調整やプレイヤーの 流入ペースの調整によって対策した ◦ ハンドシェイクがたくさん来るとつらい →ユーザーの流入ペースが減ればハンドシェイクを同時に扱う量が減る 24 TLSハンドシェイクについて
  16. • マルチプレイゲームを問題なくプレイできるかを確認するための負荷試験を 実施した ◦ 実際のゲームプレイに近いシナリオを用意 • 負荷試験の準備、実施の過程でいくつかの問題や知見があった ◦ ログ収集や異常系のハンドリングなど ◦

    規模が大きくなることで bot の挙動やマッチングがうまくいかなくなる • 負荷試験の結果意外なところに落とし穴があったのが分かった ◦ Node 単位での負荷 ◦ TLS ハンドシェイクの負荷 25 負荷試験周りのまとめ
  17. • ゲームづくりにもっと強く関わっていきたい ◦ 今までは Prizm というリアルタイム通信の基盤を作る役割が強かった ◦ ゲームのリリースが近づくにつれてマルチプレイゲームを作るには 基盤だけでなくアプリの仕様や実装も難しいし大切だということを感じた ▪

    マルチプレイゲームの難しさをなんとかハンドリングするアプリの作りが必要 • Prizm チームそのものが基盤を作るチームからマルチプレイ全体を いい感じにしていくチームに意識を変えていきたい ◦ 自他ともに「リアルタイム通信の基盤を作るチーム」「リアルタイムサーバーの チーム」といった認識になっていた ◦ ゲームづくりにもっと強く関わるためにこの認識を変えていきたい 26 終わりに: ゲームのリリースに向き合って考えたこと