要旨
CloudFront+S3+Route53を用いて、CloudFrontによるキャッシングがどの程度応答時間を改善するか計測します。
また、以下のように、CloudFront キャッシュ統計レポートやディストリビューションメトリクスで概要を把握できますが、せっかくなので細かく測定してみました。
構成図
- S3バケットをオリジンサーバーとしてHTMLファイルを配信します。
- パブリックアクセスは不可にしています(OACで制御)。
パターン
- Hit
CloudFront エッジキャッシュからオブジェクトが提供された時
- Miss
オブジェクトがエッジキャッシュに存在せず、CloudFront でオリジンからオブジェクトを取得した時
- Error
エラーになり、CloudFront でオブジェクトを提供できなかった時
参考
https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/cache-statistics.html
パターン毎の計測対象
- appconnect_time: リモートホストと SSL/SSH の接続/ハンドシェイクを開始してから完了するまでにかかった時間
- connect_time: リモートホスト(またはプロキシ) と接続を開始して、完了するまでにかかった合計時間
- namelookup_time: 名前解決が完了するまでにかかった時間
- pretransfer_time: ファイルの転送が始まるまでにかかった時間
- starttransfer_time: 最初のバイトを受信するまでにかかった時間
- total_time_us: 名前解決, TCP 接続などを含む、以前の転送にかかった合計時間
検証
- PHP 8.1.15
- Route53で設定した独自ドメイン(⇒CloudFrontディストリビューション)宛てにリクエストを送信
- 負荷軽減の為、リクエスト間隔を0.1~1.0秒(ランダム)に設定
- リクエストは10分間継続
- パターン毎にかかった平均時間を算出
<?php
const URL = "https://xxxxx";
const BASETIME = [
"count" => 0,
"appconnect_time_us" => 0,
"connect_time_us" => 0,
"namelookup_time_us" => 0,
"pretransfer_time_us" => 0,
"starttransfer_time_us" => 0,
"total_time_us" => 0,
];
// CloudFrontから受け取るレスポンスヘッダーの一部 "x-cache"の種類
const KIND = ["Hit", "Miss", "Error"];
try {
$resTime = array_fill_keys(KIND, BASETIME);
// 10分間ループ
$start = time();
$limitSec = 600;
echo date("H:i:s");
while ((time() - $start) < $limitSec) {
$ch = curl_init(URL);
curl_setopt_array(
$ch,
[
CURLOPT_RETURNTRANSFER => true,
CURLOPT_NOBODY => true,
CURLOPT_HEADER => true
]
);
$result = curl_exec($ch);
$info = curl_getinfo($ch);
curl_close($ch);
// レスポンスヘッダー"x-cache"の値(種類)を取得
preg_match('/x-cache: ([a-zA-Z]+)/', $result, $matches);
if (isset($matches[1])) {
array_walk($resTime[$matches[1]], function (&$value, $key) use ($info) {
($key === "count") ? $value++ : $value += $info[$key];
});
} else {
throw new Exception("non x-cache");
}
// サーバー負荷を軽減
usleep(random_int(100000, 1000000));
// 残り秒数
print_r($limitSec - (time() - $start) . "sec" . "\n");
}
// 平均値算出 見やすくマイクロ⇒ミリ秒に変換
$msAvg = [];
foreach ($resTime as $kind => $val) {
$msAvg[$kind] = [];
foreach ($val as $uskey => $us) {
if ($uskey === "count") {
$msAvg[$kind]["count"] = $us;
continue;
}
$ms = $us / 1000;
$msAvg[$kind][explode("_time_us", "avg_" . $uskey)[0] . "_ms"] = $val["count"] ? $ms / $val["count"] : 0;
}
}
print_r($msAvg);
echo date("H:i:s");
} catch (Exception $e) {
echo $e . PHP_EOL;
exit;
}
結果
ミリ秒で出力
Array
(
[Hit] => Array
(
[count] => 946
[avg_appconnect_ms] => 58.846813953488
[avg_connect_ms] => 9.6629513742072
[avg_namelookup_ms] => 6.3244545454545
[avg_pretransfer_ms] => 59.013402748414
[avg_starttransfer_ms] => 78.120036997886
[avg_total_ms] => 78.149157505285
)
[Miss] => Array
(
[count] => 20
[avg_appconnect_ms] => 74.23295
[avg_connect_ms] => 28.34885
[avg_namelookup_ms] => 24.4625
[avg_pretransfer_ms] => 74.41085
[avg_starttransfer_ms] => 130.764
[avg_total_ms] => 130.804
)
[Error] => Array
(
[count] => 0
[avg_appconnect_ms] => 0
[avg_connect_ms] => 0
[avg_namelookup_ms] => 0
[avg_pretransfer_ms] => 0
[avg_starttransfer_ms] => 0
[avg_total_ms] => 0
)
)
各項目の整理
各項目の”Hit/Miss*100“は以下の通りです(小数点第二位以下切捨)。
- avg_appconnect_ms: リモートホストと SSL/SSH の接続/ハンドシェイクを開始してから完了するまでにかかった時間
- 79.2%
- avg_connect_ms: リモートホスト(またはプロキシ) と接続を開始して、完了するまでにかかった合計時間
- 34.0%
- avg_namelookup_ms: 名前解決が完了するまでにかかった時間
- 25.8%
- avg_pretransfer_ms: ファイルの転送が始まるまでにかかった時間
- 79.3%
- avg_starttransfer_ms: 最初のバイトを受信するまでにかかった時間
- 59.7%
- avg_total_ms: 名前解決, TCP 接続などを含む、以前の転送にかかった合計時間
- 59.7%
Miss項目が20回と少なかったので、リクエストを増やすなり、キャッシュ削除するなり(意図的にMissさせる)出来れば、結果が著変するかもしれません。
最後に
CloudFrontで、レイテンシーの改善とオリジンサーバーの負荷軽減を実現できます!上手く活用していきたいですね!