手軽に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((requndefined res) => {
// Create benchmarker
const benchmark = req.benchmark();
// Async function
const promiseFunction = () => {
return new Promise(resolve => {
setTimeout(() => resolve('response')undefined 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 [undefinedfunction])
のフォーマットとなっていて、引数が一つだけだった場合は引数を一つ受け取ってベンチマークを取り、与えられた引数をそのまま戻す関数を戻す。 …説明がややこしいけど、これはPromiseをChainする時に便利になっていて、上記の例で言えば、
const benchmark = req.benchmark();
promiseFunction()
.then(benchmark('promiseFunction'))
.then(data => res.send(data))
;
という感じで、promiseFunction()の実行結果に対してベンチマークを取って、Resolveされたデータはそのまま流すのでChainの途中、または非同期な関数にベンチマークを挟むのに便利になっている。もちろん第二引数をつければそのまま実行結果をベンチマークする:
const benchmark = req.benchmark();
benchmark('SyncFunction'undefined 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には感謝ですね。
現場からは以上です。