ネット系の大規模サービスでは通信量とレスポンスタイムがとても重要
ネットワーク必須のサービスとしてゲームやアプリ、サイトなど世の中にたくさんあると思います。
そういうサービスの開発者としてネットワークの通信量をいかに減らせるか、レスポンスをいかに良く出来るかは腕の見せ所です。
最近、やっつけで作ったとしか思えないサービス(HTML5ゲームベースのゲームや、そのアプリ版を含む)に出会い、「せっかく作ったのにもったいないな」と思ってしまいました。
ネット系の大規模サービスにおいて通信量やレスポンスタイムに関する主な注意点を書いていこうと思います。
主な通信量の削減箇所について
画像
主に通信量を圧迫するデータは画像が多いと思います。
無駄に大きい画像の読み込みはサーバにもクライアントにも百害あって一利なしです。
さらに、一般ユーザはいわゆる「ギガが減る」と言われる通信量の多いアプリを嫌いますので、「画像データはCDNに置いているのでキニシナーイ」とか言っていると評判的にも痛い目を見ます。
画像読込みに関してのポイントは以下のような物があります。
- 100×100の表示場所に200×200のような無駄に大きいサイズを読み込まず、画像サイズを最適化する。
- 同じ画像が異なるサイズで複数個所表示されるなら、最適化したサイズを表示か所分用意する
- 画像をしっかりと減色処理してクオリティをあまり下げずにサイズダウンする。
- 画像の動的生成は基本しないようにする。
- バージョン管理してクライアントにしっかりとキャッシュさせる
- キャッシュコントロールできるようにしておく(ブラウザならhoge.jpg?12345のようにパラメータを付与しておけば簡単に実現可能)
- 必要な画像以外の無駄な画像読込みはしない(画面外や非表示の画像を読むなど)
- ユーザのアクションにより同じ画面内で画面変化する場合、変化する部分の画像や要素だけ読んだり再描画する(ブラウザで画面ごとリロードしたりリダイレクトする仕組みは避け、javascriptを活用する)
- ブラウザの場合、javascriptで画像の遅延読込みできる部分では極力活用する。(スクロール部分など。リスト全体を一気に読み込むなどは避ける)
リストなどの大きなデータ
所持アイテムなど比較的大きくなっていくデータを画面に表示する場合、何も考えずに所持リストを引っ張ってくると、いずれサーバもクライアントも破綻してしまいます。
大きなデータの読み込みに関しては以下のような事を注意する必要があります。
- 最初は画面に表示する分だけ読む(リスト全体を読み込んで渡したりしない。例えば全体1000件のうち最初に必要な10件だけ、残りは必要になったら都度10件単位でロードする等)
- ページ切り替えやスクロールなどで新たに必要なデータはその時必要な分だけ読む(ブラウザならjavascriptなどでユーザーのアクションに応じて動的に効率よく読む)
- リスト全体を読んでからjavascriptでソートして一部を表示するようなことはしない。
- ソートが必要ならリストのソート順、リミット、オフセットを指定してサーバからリストを返せる仕組みにしておく。(ただしDB上でのソートはインデックスの絡みで注意。※これに限らずサーバに渡すパラメータはセキュリティにも注意。)
- 大きなテキストデータは場合によっては圧縮して送ることも検討する。(解凍はブラウザなどのクライアント側で行う)
リロードやリダイレクト
画像の項でも書きましたが、画面の一部を更新するだけの目的で画面全体のリロードやリダイレクトを行うのは極力避ける作りにする。
高頻度の画面全体の読み直しは通信量の増加のみならずサーバ負荷も増加させるので悪影響が大きすぎます。
その他
無駄なコメント部分や空白や改行などのインデントは削除するフィルターを通してファイルをサーバにデプロイするようにすると通信量をいくらか削減できます。(※ついでに、万一ヤバいコメントが残っていても安心)
javascriptやCSSなどはネット上に無駄な部分をシュリンクしてくれるサービス(難読度も向上)がありますので活用すると良いかもしれません。
さらに静的なテキスト関係のファイル(.jsや.cssなど)はgz圧縮してサーバをgzip圧縮転送に対応しておくといいかもしれません。
またサーバのレスポンスヘッダのキャッシュコントロール関係の調整も必須だと思います。
主なレスポンスタイム考慮箇所について
大きなデータや時間のかかる処理
大きなデータや時間のかかる処理のように読込み完了までに時間がかかる表示がある場合の注意点を以下に示します。
- 時間のかかる部分は画面全体表示とは別途に並列の遅延読込みにする(画面全体を空白や変化なしのままにしない)
- 返すデータを工夫し効率の良い形式にする。(無駄な情報は返さない、冗長なデータ形式は避ける)
- ユーザのアクションを伴う場合はすぐに変化を付ける(例えばボタン押下→すぐにボタン非活性化→「ローディング」や「処理中」のメッセージ表示→サーバーへリクエスト送信。※押下後すぐに画面に変化がないとユーザは必ずボタンを連打し始める。※非活性化したボタンのエラーやタイムアウト時の再活性化忘れに要注意)
- ある程度鮮度が低くても良い情報は処理結果をRedis等のKVSへキャッシュしておきサーバで再利用する。
- クライアントでも一定時間キャッシュして良いものは極力キャッシュして再利用する。
リトライ処理
サーバが高負荷時に一番問題になるのがリトライ処理の頻発です。
リトライ処理の頻発により輻輳が輻輳を呼び、サーバの負荷は一気にスパイクしてしまいます。
リトライ処理を行う場合の注意点を以下に示します。
- エラー後のリトライ処理には一定期間のインターバルを設ける。
- ユーザがリトライボタンを押すタイプは押下後必ずすぐにボタンを非活性にしメッセージを表示し連打できないようにする。(※レスポンスが遅いとユーザは必ずボタンを連打し始める。※サーバから結果がエラーで返り再度リトライが必要になった場合、インターバル後のボタンの活性化を忘れずに)
- ビジーコントロールやキューの仕組みを導入しサーバの負荷をコントロールする。(ユーザの入場を一定数に制限する。キューで順番に処理して同一ユーザーの同一処理をリトライで何度も複数走らないようにする等)
無理をしている仕様
まれに企画の無理な仕様をエンジニアが何も考えずやっつけでそのまま力技の実装している場合があります。
その場合はエンジニアと企画で相談して、サーバのレスポンスを悪くしている仕様を変更して無理のないサーバ処理の仕様に落とし込みましょう。
基本的にユーザが自分のデータを見て自分のデータを更新する系の処理なら問題ない場合が多いのですが、他人の情報を見たり更新する系の処理が入ってくると無理が生じてしまう事があります。
DBのクエリや発行数に無理が生じたり、参照するデータが多くなったり、デッドロックの危険性も高まりますので仕様を工夫して効率の良い処理でレスポンスが良くなるようにするべきです。
まとめ
ここに示した以外にもたくさんの注意点がありますので書いていたらキリがありません。
実際に処理や仕組みを見て初めてボトルネックなどの問題が分かることもあります。
しかし上記で挙げた注意点は一般的な事ですのでネット系の一般的なサービスでは必須の事項だと思ってもらってOKです。
若干HTML5に寄った内容になっていますがAPIを使うアプリなども基本的な考え方は同じです。
せっかく良いサービスやアプリをリリースしても、リリース日ありきでやっつけ開発で通信量が有り得ないくらい多かったり、いちいちレスポンスが遅かったりしたら売れるものも売れません。
サービスが一定レベルまでしか成功できないように物理的に制限がかかっているようなものです。
エンジニアとしての腕の見せ所なので社長や企画を説得し納得のいく仕組みを作る時間を確保して、通信量やレスポンスタイム、サーバの負荷を最適化していきましょう。