無料サービスのみでCDNヒット率を上げるための一工夫

#動機
早いWebサイトを作るには、CDNを有効活用することが重要です。
よく言われていることですが、CDNを上手く利用するにはHIT率を上げることが大切です。
特に個人開発などでは、上記を無料サービスで実現することが望ましいと思っています。
今回は、HIT率を上げるために無料サービスのみで利用可能な方法と、実際にやってみてハマったところを記載します。

#CDNのおさらい
そもそものところで、簡単にCDNを利用するとWebサイトが早くなる理由をおさらいしておきます。

## CDNキャッシュなしの場合
![nochache.png](https://qiita-image-store.s3.amazonaws.com/0/202800/d10372b7-fd14-a6f0-1513-a1100e591090.png)

CDNサーバにキャッシュがない場合は、ブラウザからCDNサーバへリクエストを送ると、CDNサーバはオリジンサーバへコンテンツを取得しに行きます。
オリジンサーバはCDNサーバへレスポンスを返し、CDNサーバではその結果をキャッシュします。
その後、CDNサーバはブラウザへレスポンスを返却します。

**つまりブラウザから見ると、リクエストを送ってからレスポンスを得るまでに以下の4ステップを踏んでいることになります。**

1. ブラウザ→CDNサーバ
2. CDNサーバ→オリジンサーバ
3. オリジンサーバ→CDNサーバ
4. CDNサーバ→オリジンサーバ

## CDNキャッシュありの場合
![cached.png](https://qiita-image-store.s3.amazonaws.com/0/202800/7caa2d57-1f11-66dd-fb2a-aeff33694403.png)
CDNサーバにキャッシュがある場合は、ブラウザからCDNサーバへリクエストを送ると、CDNサーバはキャッシュ済みのレスポンスを返却します。

**つまりブラウザから見ると、リクエストを送ってからレスポンスを得るまでに以下の2ステップしか踏んでいないことになります。**

1. ブラウザ→CDNサーバ
2. CDNサーバ→ブラウザ

両者を比較すると、CDNキャッシュ有りの場合は、CDNキャッシュなしの場合と比較すると、以下のような違いがあります。

- レスポンスが返ってくるまでの経路が短いので、その分速くコンテンツを取得できる。特に、地理的に遠い場所にサーバがある場合に有効である。
- オリジンサーバにアクセスが発生しないので、オリジンサーバで行っていた処理が行われない。その分の待ち時間が発生しないために、速くコンテンツを取得できる。特に、動的アプリケーションの場合に有効である。

**つまり、リクエストに対してレスポンスがCDNキャッシュから返却される率(=CDNヒット率)を極力上げることがアプリケーション全体の速度改善に大きく寄与します。**

<font color="red">**ということは、CDNヒット率を上げるためには、キャッシュが切れるタイミングを見計らって、自動でリクエストを送信してあげればよいということになります。**</font>

# 自動でリクエストを送信する方法
無料サービスのみでリクエストを送信する方法として検討した中で、実際に使えそうなものは以下2つでした。
1. **[puppeteer](https://github.com/GoogleChrome/puppeteer)**
2. **[Google Apps Script](https://developers.google.com/apps-script/)**

## 方法1:puppeteer
![puppeteer_icon.png](https://qiita-image-store.s3.amazonaws.com/0/202800/3170eff5-a8b5-be53-85f1-6866b569a3ce.png)
**[puppeteer](https://github.com/GoogleChrome/puppeteer)**

キャッシュが切れるタイミングでリクエストを送信するとは言っても、実際のWebアプリケーションは1画面から複数のリクエストを送信しています。
それらすべてに対してリクエストを送信することはちょっと面倒くさい。。。
なので、puppeteerを利用することで、ChromeのHeadlessモードで画面を開いてしまい、必要なリクエスト送信を行います。
※利用方法に関しては、puppeteerの公式ページを参照してください。

puppeteerはNode.jsで動くので、Node.jsを無料かつ自動で利用できる環境も合わせて用意する必要があります。
今回はとりあえず以下4つを検討しました。

- [Heroku](https://jp.heroku.com/)を利用する。
    - 無料枠では「550時間/月」の利用制限が有り、月中ずっと動かす(24時間 * 31日 =744時間)ことはできない。
    - キャッシュの保持期間によっては使えるかもしれないが、今回は断念。。。
- [Now](https://zeit.co/now)を利用する。
    - 普通にNode.jsでうごかそうとすると、「[Google's Puppeteer doesn't work in now.sh](https://github.com/zeit/now-cli/issues/861)」のため動きません。
    - では、Docker上で動かそうとすると、[100MiBのサイズ制限](https://github.com/zeit/now-cli/issues/1523)に引っかかり動きません。
    - コレも断念。。。
- [Glitch](https://glitch.com/)を利用する。
    - Nowと同様、Node.jsで動かそうとすると動きません。(無料サービスだとこのあたりが難しい。。。)
    - コレも断念。。。
- ローカルマシンで動かす。
    - ローカルマシンでは動かすことができるので、それを定期的に実行する。
    - ずっとローカルマシンを動かし続けなければいけない。。。

**結果として、いい方法がなかったのですべて断念しました。**

## 方法2:Google Apps Script
<img width="250" alt="google-apps-script-logo.png" src="https://qiita-image-store.s3.amazonaws.com/0/202800/0396cee0-1d0d-d59d-a072-08e8f8d1d199.png">
[Google Apps Script](https://developers.google.com/apps-script/)
[こちら](http://shinsuke789.hatenablog.jp/entry/2015/01/03/164203)を参考にGoogle Apps Scriptで淡々とやるのみです。
URL毎にfetch、fetch、fetch。。。

**ただしGoogleAppsScriptは、cronのように処理を定期実行することが可能な点とても魅力的です。
です。(参考は[こちら](https://kijtra.com/article/cron-by-google-apps-script/))**
URLをすべて記載しなければならないという不便さはありますが、それを差し引いても、とても良いものかと思います。

# 結果
<img width="725" alt="speedtracker.png" src="https://qiita-image-store.s3.amazonaws.com/0/202800/6b515b0b-39ba-64cf-a62b-ec219f1a5f88.png">
https://zonbitamago.github.io/qiita-trend-pwa-speedtracker/
Google Apps Scriptを活用してのCDNキャッシュ対策を行い始めてからは、上記グラフの通りレスポンスタイムの向上が、安定して確認できています。
※上記は、「[無料で爆速なWebアプリケーションを作ろう！](https://qiita.com/zonbitamago/items/4e215e305062dde016bb)」で作成したサイトのレスポンスタイムを集計したグラフです。

# まとめ
- CDNヒット率を上げるためにはキャッシュが切れるタイミングでリクエストを送信することが大切。
- 無料サービスで自動リクエスト送信を行うには、Google Apps Scriptが便利。
- (puppeteerを無料サービスで動かす方法を教えてほしい。。。)

# 参考
[GoogleAppsScript](https://developers.google.com/apps-script/)
[FirebaseのCloud Functionでpuppeteerを動かす解説](http://kesin.hatenablog.com/entry/firebase_gcf_puppeteer)
[GoogleスプレッドシートでGET/POST通信を行いコンテンツを取得する](http://shinsuke789.hatenablog.jp/entry/2015/01/03/164203)
[無料でお手軽Cron！Google Apps Scriptを使ってみる](https://kijtra.com/article/cron-by-google-apps-script/)