【負荷試験】Amazon GameLiftで立てたゲームサーバーへ急激に多くの参加リクエストが来た際に何が起きるか検証してみた
こんにちは、ゲームソリューション部の入井です。
前回、様々なテストやシミュレーションが行えるAmazon GameLift Testing Toolkitについてご紹介しました。
今回は、このツールの仮想プレイヤーによる接続テスト機能を使用して、GameLiftで起動したゲームサーバー環境に対し一気にプレイヤー参加リクエストを送り込んだ時、稼働中のフリートインスタンスなどで何が起き、参加リクエストがどのように処理されるのかを検証してみました。
検証環境
Amazon GameLift Testing ToolKitのリポジトリに同梱されているサンプルゲームを使用して検証を行いました。
このサンプルゲームは、導入作業を行うことでGameLift上に独自のビルド、フリート、FlexMatch設定などが作成されます。また、Testing ToolKitで使用するための仮想プレイヤー用ECSタスクも作成され、Toolkitの様々なテスト機能を試すことができるようになっています。
FlexMatchルールセット
サンプルで作成された設定をそのまま利用しています。
{ "name": "SimpleRuleSet-2Player", "ruleLanguageVersion": "1.0", "teams": [ { "name": "MyTeam", "minPlayers": 2, "maxPlayers": 2 } ] }
このFlexMatchのルールセットは、プレイヤー2人の組み合わせを作成するシンプルなマッチングルールとなっており、1対1の対戦ゲームなどで使用できます。
フリート・キューの設定
基本的な設定は以下のようにサンプルのものをそのまま利用しています。
- キューのタイムアウト: 60秒
- コンピューティングタイプ: Managed EC2
- オペレーティングシステム: Amazon Linux 2023 (AL2023)
- フリートタイプ: オンデマンド
- EC2 インスタンスタイプ: c4.large
- インスタンス作成ロケーション: ap-northeast-1
一方で、フリートのスケーリング設定については検証のため以下のように変更しています。
- 初期インスタンス数: 1
- 最大インスタンス数: 3
- 使用可能なゲームセッションの割合: 15%
- 残りのゲームセッション数の余剰が15%を切ったらスケールアウトを開始する
なお、フリート設定の中には最大同時ゲームセッション数を指定する項目があります。1つのインスタンス内で起動できるゲームセッション数を決められるのですが、今回のサンプルフリートではこの項目が1になっているにも関わらず実際は1インスタンス内で10ゲームセッションまで起動可能になっていました。
この件、ドキュメントを一通り見ても特に解説が見つからなかったのですが、サンプルフリート内のランタイム設定で10個のゲームサーバープロセスが起動するよう設定されており、恐らくここで言う最大ゲームセッション数とは1プロセス内での制限を指しているのだと思われます。
1インスタンスあたり10個までゲームセッションを作成可能であり、1セッションあたりのプレイヤー数はFlexMatchルールセットで2名になっているので、最大インスタンス数設定が3である今回のフリートは最大60プレイヤーまで受け入れることが可能ということになります。
検証結果
ゲームセッション数に余裕があるパターン
Testing ToolKitの仮想プレイヤー作成機能を使い、まず最初にフリート内のインスタンスで利用できるゲームセッション数に余裕がある状態で仮想プレイヤーを2人分作成してみました。
以下は、そのうちの1人のログです。
2024-04-05T08:33:04.176Z Starting up 2024-04-05T08:33:04.251Z {"Type":"client","Port":2222,"ServerHost":"127.0.0.1","WSUrl":"wss://59sddn6nw7.execute-api.ap-northeast-1.amazonaws.com/prod","IdentityPoolId":"ap-northeast-1:e3bb0676-6e40-4cde-8918-f7977a2fd533","IdentityPoolRegion":"ap-northeast-1","AnywhereAuthToken":null,"AnywhereFleetId":null,"AnywhereWebSocketUrl":null,"AnywhereHostId":null,"LogFilePath":"/tmp/"} 2024-04-05T08:33:04.251Z Start loop! 2024-04-05T08:33:04.251Z Running client 2024-04-05T08:33:04.415Z {"AccountId":null,"IdentityPoolId":"ap-northeast-1:e3bb0676-6e40-4cde-8918-f7977a2fd533","UnAuthRoleArn":null,"AuthRoleArn":null,"CurrentLoginProviders":[],"LoginsCount":0,"PreemptExpiryTime":"00:00:00"} 2024-04-05T08:33:04.757Z Trying to connect 2024-04-05T08:33:05.744Z OnConnected! 2024-04-05T08:33:05.774Z SENT:{"Type":"StartMatchmaking","PlayerId":"114ac9f5-74db-41da-9f92-9f6f388b0d58"} 2024-04-05T08:33:08.955Z RECEIVED:{"Type":"GameRequested","PlayerId":"114ac9f5-74db-41da-9f92-9f6f388b0d58"} 2024-04-05T08:33:18.059Z RECEIVED:{"Type":"MatchmakingSucceeded","PlayerId":"114ac9f5-74db-41da-9f92-9f6f388b0d58","SessionId":"psess-9ecfda32-51ce-863c-2220-73844df2a935","IpAddress":"18.182.52.90","Port":1936}
最初に認証を行った後、FlexMatchへマッチメイキングへの参加リクエストを送り、その結果としてMatchmakingSucceeded
という結果情報と接続先サーバー情報などを受け取りました。
上記のログの時間部分を見ると、参加リクエスト送信から13秒程度でマッチメイキングが完了しその結果の受け取りができていることが分かります。インスタンスに余裕がある場合はこのくらいの時間でゲームセッションに参加ができるようです。
一般的なAPIリクエストと比べるとレスポンスを受け取るまでにかなりの時間を要しているように見えますが、マッチメイキングはその性質上他プレイヤーの参加状況確認処理等にどうしても時間がかかりがちです。
マッチメイキング完了後、仮想プレイヤーコンテナは受け取ったサーバー情報を元にマルチプレイに参加し、しばらくゲームの操作を行っていました。以下のログがその様子ですが、提示された計算式の答えをクライアントが送信する形式のゲームのようです。
2024-04-05T06:44:43.552Z RECEIVED:{"Type":"MatchmakingSucceeded","PlayerId":"993c8fb7-fe4e-4b42-8e1e-20747be2be37","SessionId":"psess-8558e064-9312-e55c-eb56-7340f13ba8af","IpAddress":"18.182.52.90","Port":1936} 2024-04-05T06:44:43.611Z SENT:{"Type":"Login","PlayerId":"993c8fb7-fe4e-4b42-8e1e-20747be2be37","SessionId":"psess-8558e064-9312-e55c-eb56-7340f13ba8af","Name":"Player 3259"} 2024-04-05T06:44:43.807Z {"Text":"Welcome Player 3259"} 2024-04-05T06:44:48.839Z {"Question":{"Number1":22,"Number2":24,"Operand":"+","Text":"What is 22 + 24?"},"Type":"Question"} 2024-04-05T06:44:48.845Z SENT:{"Type":"Answer","PlayerId":"993c8fb7-fe4e-4b42-8e1e-20747be2be37","SessionId":"psess-8558e064-9312-e55c-eb56-7340f13ba8af","Answer":46} 2024-04-05T06:44:48.898Z {"Type":"Result","AnswerResult":1}
マッチメイキングリクエストの送信やゲームサーバー参加後の処理は、仮想プレイヤーコンテナ内で自動的に実行されるようになっています。
ゲームセッション数が不足しているパターン
先ほどのテストで作成した仮想プレイヤーを削除した上で、今度は1分ごとに20人の仮想プレイヤーを作成するテストを試してみました。
最初の1回目の20人については、1つのインスタンスで20プレイヤーまで受け入れることができるため、いずれのプレイヤーも遅延なくゲームに参加できました。また、余剰セッション数が0となったので、オートスケールが実行され新しいインスタンスが起動し始めました。
1分経過後、更に20人の仮想プレイヤーを追加しました。が、2つめのインスタンス起動が間に合わず、全てのプレイヤーがキューで足止めを受けることとなりました。以下の画像はTesting Toolkitのダッシュボードですが、多くのプレイヤーがフリートに入れていないのが視覚的に分かります。
足止めを受けているプレイヤーのログを見ると以下のようになっていました。マッチメイキング結果としてMatchmakingTimedOut
を受け取った後、またすぐにマッチメイキングのリクエストを送っています。仮想プレイヤーのロジックとして、対象ゲームサーバーに入れるまでひたすらマッチメイキングリクエストを送る形になっているようです。
2024-04-05T08:35:11.812Z Starting up 2024-04-05T08:35:11.888Z {"Type":"client","Port":2222,"ServerHost":"127.0.0.1","WSUrl":"wss://59sddn6nw7.execute-api.ap-northeast-1.amazonaws.com/prod","IdentityPoolId":"ap-northeast-1:e3bb0676-6e40-4cde-8918-f7977a2fd533","IdentityPoolRegion":"ap-northeast-1","AnywhereAuthToken":null,"AnywhereFleetId":null,"AnywhereWebSocketUrl":null,"AnywhereHostId":null,"LogFilePath":"/tmp/"} 2024-04-05T08:35:11.888Z Start loop! 2024-04-05T08:35:11.888Z Running client 2024-04-05T08:35:12.057Z {"AccountId":null,"IdentityPoolId":"ap-northeast-1:e3bb0676-6e40-4cde-8918-f7977a2fd533","UnAuthRoleArn":null,"AuthRoleArn":null,"CurrentLoginProviders":[],"LoginsCount":0,"PreemptExpiryTime":"00:00:00"} 2024-04-05T08:35:13.001Z Trying to connect 2024-04-05T08:35:13.127Z OnConnected! 2024-04-05T08:35:13.129Z SENT:{"Type":"StartMatchmaking","PlayerId":"ebda8e85-42ba-4e5f-8086-d5104cddf921"} 2024-04-05T08:35:13.341Z RECEIVED:{"Type":"GameRequested","PlayerId":"ebda8e85-42ba-4e5f-8086-d5104cddf921"} 2024-04-05T08:36:30.523Z RECEIVED:{"Type":"MatchmakingTimedOut","PlayerId":"ebda8e85-42ba-4e5f-8086-d5104cddf921","Message":"Removed from matchmaking due to timing out."} 2024-04-05T08:36:30.524Z SENT:{"Type":"StartMatchmaking","PlayerId":"ebda8e85-42ba-4e5f-8086-d5104cddf921"}
この後、一旦仮想プレイヤーの作成は停止し、全てのプレイヤーがゲームに参加できるのを待ちました。
数分経過後、インスタンスの起動とゲームサーバーの準備が整い、足止めされていた仮想プレイヤー達が一気にゲームに参加し始めました。
ゲームサーバー参加までの仮想プレイヤーのログは以下の通りです。
2024-04-05T08:35:11.812Z Starting up 2024-04-05T08:35:11.888Z {"Type":"client","Port":2222,"ServerHost":"127.0.0.1","WSUrl":"wss://59sddn6nw7.execute-api.ap-northeast-1.amazonaws.com/prod","IdentityPoolId":"ap-northeast-1:e3bb0676-6e40-4cde-8918-f7977a2fd533","IdentityPoolRegion":"ap-northeast-1","AnywhereAuthToken":null,"AnywhereFleetId":null,"AnywhereWebSocketUrl":null,"AnywhereHostId":null,"LogFilePath":"/tmp/"} 2024-04-05T08:35:11.888Z Start loop! 2024-04-05T08:35:11.888Z Running client 2024-04-05T08:35:12.057Z {"AccountId":null,"IdentityPoolId":"ap-northeast-1:e3bb0676-6e40-4cde-8918-f7977a2fd533","UnAuthRoleArn":null,"AuthRoleArn":null,"CurrentLoginProviders":[],"LoginsCount":0,"PreemptExpiryTime":"00:00:00"} 2024-04-05T08:35:13.001Z Trying to connect 2024-04-05T08:35:13.127Z OnConnected! 2024-04-05T08:35:13.129Z SENT:{"Type":"StartMatchmaking","PlayerId":"ebda8e85-42ba-4e5f-8086-d5104cddf921"} 2024-04-05T08:35:13.341Z RECEIVED:{"Type":"GameRequested","PlayerId":"ebda8e85-42ba-4e5f-8086-d5104cddf921"} 2024-04-05T08:36:30.523Z RECEIVED:{"Type":"MatchmakingTimedOut","PlayerId":"ebda8e85-42ba-4e5f-8086-d5104cddf921","Message":"Removed from matchmaking due to timing out."} 2024-04-05T08:36:30.524Z SENT:{"Type":"StartMatchmaking","PlayerId":"ebda8e85-42ba-4e5f-8086-d5104cddf921"} 2024-04-05T08:36:30.694Z RECEIVED:{"Type":"GameRequested","PlayerId":"ebda8e85-42ba-4e5f-8086-d5104cddf921"} 2024-04-05T08:37:44.546Z RECEIVED:{"Type":"MatchmakingTimedOut","PlayerId":"ebda8e85-42ba-4e5f-8086-d5104cddf921","Message":"Removed from matchmaking due to timing out."} 2024-04-05T08:37:44.547Z SENT:{"Type":"StartMatchmaking","PlayerId":"ebda8e85-42ba-4e5f-8086-d5104cddf921"} 2024-04-05T08:37:44.716Z RECEIVED:{"Type":"GameRequested","PlayerId":"ebda8e85-42ba-4e5f-8086-d5104cddf921"} 2024-04-05T08:38:59.594Z RECEIVED:{"Type":"MatchmakingTimedOut","PlayerId":"ebda8e85-42ba-4e5f-8086-d5104cddf921","Message":"Removed from matchmaking due to timing out."} 2024-04-05T08:38:59.595Z SENT:{"Type":"StartMatchmaking","PlayerId":"ebda8e85-42ba-4e5f-8086-d5104cddf921"} 2024-04-05T08:38:59.761Z RECEIVED:{"Type":"GameRequested","PlayerId":"ebda8e85-42ba-4e5f-8086-d5104cddf921"} 2024-04-05T08:39:15.562Z RECEIVED:{"Type":"MatchmakingSucceeded","PlayerId":"ebda8e85-42ba-4e5f-8086-d5104cddf921","SessionId":"psess-ee13c5c1-11fd-8f30-aa84-bbac77efa3cb","IpAddress":"13.231.68.171","Port":1936}
最初の起動から約4分後にMatchmakingSucceeded
を受け取ることができています。この間、3回マッチメイキングリクエストを送っていますがいずれもタイムアウトで失敗しています。オートスケールによるインスタンス起動はこの仮想プレイヤー作成の1分前に始まったので、インスタンス起動開始からゲームサーバー準備完了まで約5分かかったことが分かります。
この結果から分かる通り、GameLiftのフリートはオートスケールに対応していますが、急激なプレイヤー増加には対応できません。また、今回のサンプルゲームはシンプルなプログラムなので起動がすぐにできましたが、実際のゲームサーバープログラムはもっと規模大きく起動まで時間がかかることもあり得ます。その場合、長時間プレイヤーがゲームに参加できない障害に発展する可能性があります。
改善案
オートスケールはあくまで緩やかなプレイヤー数の変動への対応策と考え、急激なプレイヤー数増加が予想される場合は手動でインスタンス数をあらかじめ増やしておく必要があります。正確な増加数の予想が難しい場合は、オートスケール開始の条件を緩くするなどの対応も考えられます。
上記の検証とは別に、10個のインスタンスを立てた状態で100人の仮想プレイヤーを一気に追加する検証を行ってみたところ、いずれのプレイヤーも遅延することなくゲームサーバーへ参加することができました。この結果から、瞬間的に100名規模のプレイヤーが参加しても、キューのセッション割り当て処理がボトルネックになることはないようです。
まとめ
Amazon GameLift Testing Toolkitと付属のサンプルゲームを使って、急激なプレイヤー増加時の負荷試験を試してみました。
オートスケール機能が用意されていても、それだけで急激なリクエストの増加に対応することはできないため、事前のプレイヤー数の予測やそれに合った適切なサーバー環境の構築が重要となります。