All Articles

Server Timing APIを使ってサーバーサイドのベンチマークを手軽にやる

手軽にServer Sideのベンチマークを可視化していく

Chrome65からServer Timing APIのサポートが入ったので手軽にサーバサイドのベンチマークをDevToolから可視化できるようになった。

Server Timingの仕様は下記: Server Timing

で、nodeのHTTP(HTTPS)モジュール、またはexpressから手軽に使えるnpmを作って公開している。

ysugimoto/server-timing-benchmark

詳しい使い方はREADMEに書いたけど、リクエスト単位で手軽にベンチマークを行える。下記はexpressのサンプル:

const express = require('express');
const serverTimingBenchmark = require('server-timing-benchmark');

const app = express();
app.use(serverTimingBenchmark());

// Handler
app.use((req, res) => {
  // Create benchmarker
  const benchmark = req.benchmark();

  // Async function
  const promiseFunction = () => {
    return new Promise(resolve => {
    setTimeout(() => resolve('response'), 1000);
    });
  };

  promiseFunction()
    .then(benchmark('promiseFunction'))
    .then(data => res.send(data))
  ;
});
app.listen(8888);

これでサーバを起動してhttp://localhost:8888にアクセスすると、responseという文字と共にServer-Timingヘッダが付加される。

この状態でDevtoolを開き、Network -> localhost を選択、右ペインのTimingタブを選択すると、promiseFunctionという名前のグラフが確認できる。

req.benchmark()

req.benchmark()で得られるベンチマーク用の関数をコールする度に経過時間が更新され、その差分を溜めていき、最後にヘッダとして送出するため、 必要なタイミングでコールするだけで、その間のベンチマークが得られるようになっている。

また、この関数は

benchmark(name [,function])

のフォーマットとなっていて、引数が一つだけだった場合は引数を一つ受け取ってベンチマークを取り、与えられた引数をそのまま戻す関数を戻す。 …説明がややこしいけど、これはPromiseをChainする時に便利になっていて、上記の例で言えば、

const benchmark = req.benchmark();
promiseFunction()
  .then(benchmark('promiseFunction'))
  .then(data => res.send(data))
;

という感じで、promiseFunction()の実行結果に対してベンチマークを取って、Resolveされたデータはそのまま流すのでChainの途中、または非同期な関数にベンチマークを挟むのに便利になっている。もちろん第二引数をつければそのまま実行結果をベンチマークする:

const benchmark = req.benchmark();
benchmark('SyncFunction', someSyncFunction())

この2つのパターンでだいたいの計測はできると思う。また、Server-Timingのヘッダは主にデバッグ用途で通常Production環境では送出しなくても良いので、送出を抑制するメソッドも用意している。具体的には、

if (process.env.NODE_ENV === 'production') {
  req.disableBenchmark();
}

という感じにすれば開発環境でだけベンチマーク結果が得られる。

まとめ

サーバーサイドのベンチマークって結構面倒で、ログに吐いたりFluentを挟んで…みたいな手間が必要だったけど、Server Timingを使えばレスポンスのヘッダにそのまま結果を載せて、かつChromeのDevToolで可視化できてとても便利だと思う。

また、実装に当たっては既存のserver-timingのnpmモジュールを参考にさせていただいた。最初はそのまま使ってたけど、Total Timeのヘッダ送出が抑制できなかったりしたのとベンチマーク用途ではなかったので一から書いた次第。

今回はnode用に書いたけど、基本的にHTTPなので他のHTTPサーバを持ってる言語ならどれでも同じようにできる。 この仕様提案したIlyaには感謝ですね。

現場からは以上です。