サーバータイミングによるパフォーマンスの測定

公開: 2022-03-10
クイックサマリー↬サーバータイミングヘッダーは、バックエンドサーバーのパフォーマンスタイミングをブラウザーの開発者ツールに伝達するための個別の便利な方法を提供します。 アプリケーションにタイミング情報を追加すると、バックエンドとフロントエンドのパフォーマンスをすべて1か所で監視できます。

あらゆる種類のパフォーマンス最適化作業を行う場合、私たちが最初に学ぶことの1つは、パフォーマンスを向上させる前に、まずそれを測定する必要があるということです。 何かが機能している速度を測定できなければ、加えられた変更がパフォーマンスを改善しているのか、効果がないのか、さらには事態を悪化させているのかを判断することはできません。

私たちの多くは、あるレベルでパフォーマンスの問題に取り組むことに精通しているでしょう。 これは、ページのJavaScriptがすぐに機能しない理由や、ホテルのWi-Fiに画像が表示されるまでに時間がかかりすぎる理由を理解しようとするのと同じくらい簡単なことかもしれません。 この種の質問に対する答えは、ブラウザの開発者ツールという非常に馴染みのある場所でよく見られます。

長年にわたり、開発者ツールは、アプリケーションのフロントエンドでこの種のパフォーマンスの問題をトラブルシューティングするのに役立つように改善されてきました。 ブラウザにはパフォーマンス監査が組み込まれています。これはフロントエンドの問題を追跡するのに役立ちますが、これらの監査は、ブラウザで修正できない別の速度低下の原因を示す可能性があります。 その問題は、サーバーの応答時間が遅いことです。

「最初のバイトまでの時間」

サーバー上での構築が単純に遅いページを改善するためにブラウザーを最適化することはほとんどできません。 そのコストは、ブラウザがファイルを要求してから応答を受け取るまでの間に発生します。 開発者ツールでネットワークウォーターフォールチャートを調べると、「待機中(TTFB)」のカテゴリでこの遅延がわかります。 これは、ブラウザが要求を行ってから応答を受け取るまでに待機する時間です。

ジャンプした後もっと! 以下を読み続けてください↓

パフォーマンスの観点から、これは最初のバイトまでの時間として知られています。これは、サーバーがブラウザーの処理を開始できるものの送信を開始するまでにかかる時間です。 その待機時間には、サーバーがページを作成するために必要なすべてのことが含まれています。 一般的なサイトの場合、アプリケーションの正しい部分へのリクエストのルーティング、リクエストの認証、データベースなどのバックエンドシステムへの複数の呼び出しが含まれる場合があります。 これには、テンプレートシステムを介したコンテンツの実行、サードパーティサービスへのAPI呼び出しの作成、さらには電子メールの送信や画像のサイズ変更などが含まれる場合があります。 サーバーがリクエストを完了するために行うすべての作業は、ユーザーがブラウザーで体験するTTFB待機に押しつぶされます。

単一ページリクエストの検査を示すChromeDevToolsのネットワークパネル
ドキュメントリクエストを調べると、ブラウザがサーバーからの応答を待つために費やす時間がわかります。

では、どうすればその時間を短縮し、ユーザーへのページの配信をより迅速に開始できるでしょうか。 それは大きな質問です。答えはアプリケーションによって異なります。 それはパフォーマンス最適化自体の仕事です。 最初に行う必要があるのは、パフォーマンスを測定して、変更のメリットを判断できるようにすることです。

サーバータイミングヘッダー

サーバータイミングの仕事は、サーバーでのアクティビティの時間を実際に計るのを助けることではありません。 バックエンドプラットフォームで利用できるツールセットを使用して、自分でタイミングを調整する必要があります。 むしろ、サーバータイミングの目的は、これらの測定値をブラウザーに伝達する方法を指定することです。

これを行う方法は非常にシンプルで、ユーザーに対して透過的であり、ページの重みへの影響は最小限です。 情報は、HTTP応答ヘッダーの単純なセットとして送信されます。

 Server-Timing: db;dur=123, tmpl;dur=56

この例では、 dbtmplという名前の2つの異なるタイミングポイントを通信します。 これらは仕様の一部ではありません。これらは、データベースとテンプレートのタイミングをそれぞれ表すために選択した名前です。

durプロパティは、操作が完了するまでにかかったミリ秒数を示しています。 開発ツールのネットワークセクションでリクエストを見ると、タイミングがチャートに追加されていることがわかります。

新しいサーバータイミングセクションを表示するChromeDevToolsのページリクエストのタイミングパネル。
新しいServerTimingセクションが表示され、Server-TimingHTTPヘッダーで設定されたタイミングが示されます。

Server-Timingヘッダーは、コンマで区切られた複数のメトリックを取ることができます。

 Server-Timing: metric, metric, metric

各メトリックは、3つの可能なプロパティを指定できます

  1. メトリックの短縮(この例ではdbなど)
  2. ミリ秒単位の期間dur=123として表されます)
  3. 説明desc="My Description"として表されます)

各プロパティは、区切り文字としてセミコロンで区切られます。 次のように、例に説明を追加できます。

 Server-Timing: db;dur=123;desc="Database", tmpl;dur=56;desc="Template processing"
Chrome DevToolsのページリクエストの[タイミング]パネルに、サーバータイミングメトリックに使用される説明が表示されます。
名前は、提供されると説明に置き換えられます。

必要なプロパティはnameだけです。 durdescはどちらもオプションであり、必要に応じてオプションで使用できます。 たとえば、あるサーバーまたはデータセンターで発生していて、別のサーバーでは発生していないタイミングの問題をデバッグする必要がある場合は、タイミングを関連付けずにその情報を応答に追加すると便利な場合があります。

 Server-Timing: datacenter;desc="East coast data center", db;dur=123;desc="Database", tmpl;dur=56;desc="Template processing”

これは、タイミングとともに表示されます。

Chrome DevToolsのページリクエストの[タイミング]パネルに、時間が設定されていないサーバーのタイミングが表示されます。
タイミングはありませんが、「東海岸データセンター」の値が表示されます。

お気づきかもしれませんが、タイミングバーがウォーターフォールパターンで表示されないことです。 これは、サーバータイミングがタイミングのシーケンスを伝達しようとせず、生のメトリック自体のみを伝達しようとするためです。

サーバータイミングの実装

独自のアプリケーション内での正確な実装は、特定の状況によって異なりますが、原則は同じです。 手順は常に次のようになります。

  1. いくつかの操作の時間を計る
  2. タイミング結果をまとめる
  3. HTTPヘッダーを出力します

擬似コードでは、応答の生成は次のようになります。

 startTimer('db') getInfoFromDatabase() stopTimer('db') startTimer('geo') geolocatePostalAddressWithAPI('10 Downing Street, London, UK') endTimer('geo') outputHeader('Server-Timing', getTimerOutput())

これらの方針に沿って何かを実装するための基本は、どの言語でも簡単なはずです。 非常に単純なPHP実装では、タイミング操作にmicrotime()関数を使用でき、次のようになります。

 class Timers { private $timers = []; public function startTimer($name, $description = null) { $this->timers[$name] = [ 'start' => microtime(true), 'desc' => $description, ]; } public function endTimer($name) { $this->timers[$name]['end'] = microtime(true); } public function getTimers() { $metrics = []; if (count($this->timers)) { foreach($this->timers as $name => $timer) { $timeTaken = ($timer['end'] - $timer['start']) * 1000; $output = sprintf('%s;dur=%f', $name, $timeTaken); if ($timer['desc'] != null) { $output .= sprintf(';desc="%s"', addslashes($timer['desc'])); } $metrics[] = $output; } } return implode($metrics, ', '); } }

テストスクリプトは、以下のようにそれを使用します。ここでは、 usleep()関数を使用して、スクリプトの実行に人為的に遅延を作成し、完了に時間がかかるプロセスをシミュレートします。

 $Timers = new Timers(); $Timers->startTimer('db'); usleep('200000'); $Timers->endTimer('db'); $Timers->startTimer('tpl', 'Templating'); usleep('300000'); $Timers->endTimer('tpl'); $Timers->startTimer('geo', 'Geocoding'); usleep('400000'); $Timers->endTimer('geo'); header('Server-Timing: '.$Timers->getTimers());

このコードを実行すると、次のようなヘッダーが生成されました。

 Server-Timing: db;dur=201.098919, tpl;dur=301.271915;desc="Templating", geo;dur=404.520988;desc="Geocoding"
テスト値が正しく表示されているChromeDevToolsのページリクエストの[タイミング]パネル。
例で設定されたサーバータイミングは、テストスクリプトで構成された遅延とともに[タイミング]パネルに表示されます。

既存の実装

サーバータイミングがどれほど便利かを考えると、私が見つけることができる実装は比較的少ないです。 サーバータイミングNPMパッケージは、ノードプロジェクトからサーバータイミングを使用するための便利な方法を提供します。

ミドルウェアベースのPHPフレームワークを使用する場合、tuupola / server-timing-middlewareも便利なオプションを提供します。 私はこれをNotistの本番環境で数か月使用していますが、実際の例を見たい場合は、常にいくつかの基本的なタイミングを有効にしておいてください。

ブラウザのサポートについては、私が見た中で最高のものはChrome DevToolsであり、それがこの記事のスクリーンショットに使用したものです。

考慮事項

サーバータイミング自体は、ネットワーク経由で返送されるHTTP応答に最小限のオーバーヘッドを追加します。 ヘッダーは非常に最小限であり、内部ユーザーのみをターゲットにすることを心配せずに送信するのが一般的に安全です。 それでも、不要なオーバーヘッドを追加しないように、名前と説明を短くすることは価値があります。

さらに懸念されるのは、ページまたはアプリケーションの時間を計るためにサーバー上で実行している可能性のある余分な作業です。 タイミングとログを追加すると、それ自体がパフォーマンスに影響を与える可能性があるため、必要に応じてこれをオンまたはオフにする方法を実装する価値があります。

サーバータイミングヘッダーを使用すると、アプリケーションのフロントエンドとバックエンドの両方からのすべてのタイミング情報に1か所でアクセスできるようになります。 アプリケーションがそれほど複雑でない場合は、実装が簡単で、非常に短時間で稼働させることができます。

サーバータイミングについて詳しく知りたい場合は、次のことを試してみてください。

  • W3Cサーバーのタイミング仕様
  • サーバータイミングのMDNページには、ブラウザサポートの例と最新の詳細があります
  • サーバータイミングの使用に関するBBCiPlayerチームからの興味深い記事。