ウェブサイトの高速化手法 (CDN活用)

# 概要
- 費用の関係から Private Spaces を使えずUSリージョンを使う際、「おそくなるんでしょ？」ってたずねられる際には、CDN利用をオススメするので、その内容をご紹介
- US リージョンの Heroku をそのまま使うケースと、CDNを利用したケースでどのくらいレイテンシーが向上するか？のテストを実施


# はじめに
Common Runtime で Heroku を運用しているとき、どうしても気になるのが、データセンターのロケーション。米国かヨーロッパのみが選択可能なので、どうしてもレイテンシーが気になっちゃうところです。

とはいえ、東京リージョンを使うとなると、Private Spaces 利用ということになり、少し?!費用もかさんでしまうため、躊躇してしまうこともあるかと。。。ということで、Private Spaces に頼らず、CDN を使って高速化してみましょうという内容です。

# どのくらいのレイテンシーなのか？
Heroku のデータセンターである米国・ヨーロッパへはどのくらいのレイテンシーなのか、まず、調べてみましょう。ところで、米国・ヨーロッパといってもかなり広いですよね。リージョンについては、以下のコマンドで確認できます。
`heroku regions`
<img width="757" alt="heroku_regions.png" src="https://qiita-image-store.s3.amazonaws.com/0/3816/9a7d09c0-acb9-9b11-947f-70d36810a266.png">
うーん。結局、場所は特定できないわけですが、Private Spaces のロケーションなんかをみると、なんとなく、場所が想像できるのではないかなーｗという気がします。
ということで、米国リージョンにアプリケーションをデプロイして、pingしてみましょう。ping 元は東京某所です。

>C:\Users\admin>ping advent2018.herokuapp.com
>
us-east-1-a.route.herokuapp.com [34.202.247.40]に ping を送信しています 32 バイトのデータ:
要求がタイムアウトしました。
要求がタイムアウトしました。
要求がタイムアウトしました。
要求がタイムアウトしました。
>
>34.202.247.40 の ping 統計:
    パケット数: 送信 = 4、受信 = 0、損失 = 4 (100% の損失)、

うーん。pingが帰ってきませんねー。Heroku のロードバランサーは、ICMP は話せないので、ping してもタイムアウトしてしまいます。ということで、TCP でpingできる [PsPing](https://docs.microsoft.com/en-us/sysinternals/downloads/psping) (Windows) を使います。

C:\Users\admin>psping advent2018.herokuapp.com:80

>PsPing v2.10 - PsPing - ping, latency, bandwidth measurement utility
Copyright (C) 2012-2016 Mark Russinovich
Sysinternals - www.sysinternals.com
>
TCP connect to 34.192.68.110:80:
5 iterations (warmup 1) ping test:
Connecting to 34.192.68.110:80 (warmup): from 10.0.2.15:50022: 174.76ms
Connecting to 34.192.68.110:80: from 10.0.2.15:50023: 171.98ms
Connecting to 34.192.68.110:80: from 10.0.2.15:50024: 259.04ms
Connecting to 34.192.68.110:80: from 10.0.2.15:50025: 171.05ms
Connecting to 34.192.68.110:80: from 10.0.2.15:50026: 169.55ms
>
TCP connect statistics for 34.192.68.110:80:
  Sent = 4, Received = 4, Lost = 0 (0% loss),
  Minimum = 169.55ms, Maximum = 259.04ms, Average = 192.90ms

ということで、5回の ping で平均192.90ミリ秒、つまり、約0．2秒かかるということがわかります。

# Heroku Add-on の CDN
CDN の説明は割愛するとして、10年くらい前に CDN というと、費用も高く、大規模サイト向けという感がありましたが、ここ数年は、従量課金な CDN も多数登場し、小規模サイトでも手軽に導入できるようになりました。Heroku でも

- [Fastly](https://elements.heroku.com/addons/fastly)
- [Edge](https://elements.heroku.com/addons/edge)

の2種類の add-on が提供されています。違い等はそれぞれのウェブページを参照していただくとして、それぞれ、エッジロケーションの確認してみます。

- [Fastly のエッジロケーション](https://www.fastly.com/network-map)
- [Edge のエッジロケーション](https://aws.amazon.com/jp/cloudfront/features/)

詳細な場所は地図から読み取れないが、どちらも日本にエッジがあるようなので、CDN を活用できれば、その分レイテンシーを短縮できるはずです。

# CDN 効果測定
### Fastly
>C:\Users\admin>psping advent2018-herokuapp-com.global.ssl.fastly.net:80
>
PsPing v2.10 - PsPing - ping, latency, bandwidth measurement utility
Copyright (C) 2012-2016 Mark Russinovich
Sysinternals - www.sysinternals.com
>
TCP connect to 151.101.229.194:80:
5 iterations (warmup 1) ping test:
Connecting to 151.101.229.194:80 (warmup): from 10.0.2.15:50089: 6.90ms
Connecting to 151.101.229.194:80: from 10.0.2.15:50090: 4.73ms
Connecting to 151.101.229.194:80: from 10.0.2.15:50091: 4.50ms
Connecting to 151.101.229.194:80: from 10.0.2.15:50092: 3.62ms
Connecting to 151.101.229.194:80: from 10.0.2.15:50093: 3.51ms
>
TCP connect statistics for 151.101.229.194:80:
  Sent = 4, Received = 4, Lost = 0 (0% loss),
  Minimum = 3.51ms, Maximum = 4.73ms, Average = 4.09ms

### Edge
>C:\Users\masam>psping d3aolqo5e8tj6j.cloudfront.net:80
>
PsPing v2.10 - PsPing - ping, latency, bandwidth measurement utility
Copyright (C) 2012-2016 Mark Russinovich
Sysinternals - www.sysinternals.com
>
TCP connect to 13.33.174.56:80:
5 iterations (warmup 1) ping test:
Connecting to 13.33.174.56:80 (warmup): from 10.0.2.15:50115: 4.69ms
Connecting to 13.33.174.56:80: from 10.0.2.15:50116: 6.09ms
Connecting to 13.33.174.56:80: from 10.0.2.15:50117: 3.90ms
Connecting to 13.33.174.56:80: from 10.0.2.15:50118: 4.50ms
Connecting to 13.33.174.56:80: from 10.0.2.15:50119: 8.05ms
>
TCP connect statistics for 13.33.174.56:80:
  Sent = 4, Received = 4, Lost = 0 (0% loss),
  Minimum = 3.90ms, Maximum = 8.05ms, Average = 5.64ms
 
Fastly だと平均4.09ミリ秒、Edgeだと平均5.64秒。CDN を使うと桁違い、今回のケースでは、少なくとも**30倍**以上高速化されることがわかります。使わない手はないですよねー。
# CDN 利用時の注意点
**キャッシュのコントロール**。これがCDNを使いこなす肝と言ってよいでしょう。
CDNはエッジでコンテンツをキャッシュするから高速なレスポンスを実現しているので、キャッシュからコンテンツを送信するのか？それともオリジンから再度コンテンツを読み込むのか？といった、キャッシュのコントロール（＝キャッシュ期間の設定)が重要です。キャッシュ期間を長くしすぎてしまうとユーザーに古いコンテンツを表示してしまったりすることがあります。逆に、キャッシュ期間を短くすると、元々のサーバー(＝オリジンと呼ばれる事が多い)の負荷が増えますので、適切なキャッシュ期間を考慮します。
# キャッシュをコントロールする
保存期間の設定は、httpヘッダのCacheｰControlで制御するのがシンプルな方法の1つです。また、Fastlyには、キャッシュをフラッシュ(=削除)するAPIが用意されているので、CMSなどの構築の場合、コンテンツを公開したときに、フラッシュすような仕組みにしておくのも1つの解決方法です。

また、画像などを修正した際には、元々のファイル名でアップロードするのではなく、ファイル名自体を変更して新しくキャッシュさせるという方法もキャッシュコントロールの1つです。その場合、リンク元のHTMLのキャッシュクリアのタイミングを考慮しておく必要もあります。
# 動的なページでも CDN は有効か？
**有効です!!**
確かに動的ページの場合、例えば、個人情報を表示・編集するようなページの場合、個人情報は、キャッシュするべきでないので、そういったページでは、CDN 不要の様にも思えますが、共通の画像(アイコン、バナーなど)、スタイルシート、.jsファイルなど、動的に生成されていないファイルも多々あります。こういったファイルはCDNから配信すると、ウェブページ全体の速度の体感は向上するはずです。
# デメリットは無いのか?
前述のキャッシュコントロールの必要性は、手間が増えるという点で、CDN利用のデメリットです。それ以外に考えられる、とくに、Heroku ならではのデメリットが1つあります。それは、CDNがトラフィック量による従量課金な点です。Herokuでは、Dynoとクライアント間のデータの送受信、データ転送量での課金はしていません。Heroku が利用しているAWS は、データ転送量で課金しているのにも関わらず、です。つまり、CDNを利用すると、データ転送の費用が、追加で発生するということを覚えておく必要があります。とはいえ、CDNを導入すれば、必要なDyno数の削減になる場合が多く、また、レイテンシー向上のために、Private Spaces の導入も不要になるので、コストメリットは大きいはずです。