| @技術/プログラミング

キャッシュ

CloudFront 転送量削減の試みで色々やっていて、そういやウェブサイトのキャッシュについての説明記事っぽいの読んだことないし、開発現場だと「じゃあキャッシュすればいいじゃん」みたいな発言が行き交うけど、ひとくちにキャッシュと言っても色々あって、ウェブ開発始めたばかりの人にはよく分からないんじゃないかと思ったので書いておきます。

キャッシュの目的

キャッシュの目的.png

キャッシュの目的は大きく二つあって、コンテンツ表示の高速化とコストの削減がある。コンテンツの表示高速化は、時間のかかる処理の結果を捨てずに再利用して二回目以降の表示を高速化すること、コストの削減は、処理結果の再利用によってコンピューターの利用時間を減らしたり、データの通信量を減らすことをそれぞれ目的としている。表示高速化とコスト削減はどちらか一方だけを達成するものではなく、多くの場合で両方が同時に実現される。

キャッシュに向いているコンテンツ

キャッシュには向いているコンテンツと向いていないコンテンツがある。

キャッシュに向いているコンテンツ.png

ウェブサイトで配信するものに関して何でもキャッシュできるわけではなく、多くの人が共通して閲覧するものか、更新頻度が低いものでないとキャッシュしてはいけない。更新頻度が低くて多くの人が共通して閲覧するもの(画像、 CSS 、 JavaScript など)はキャッシュしやすい。だがショッピングサイトの購入履歴などは人それぞれなので一律にキャッシュしてはいけない。

キャッシュの種類

キャッシュにはブラウザーキャッシュとサーバーサイドのキャッシュがある。

キャッシュの種類.png

ブラウザーキャッシュ

ブラウザーキャッシュは、同じ人が繰り返し訪問するサイトで繰り返し利用されるサイト内画像や JavaScript 、 CSS (静的コンテンツ)に対して提供するのに向いている。二回目以降のアクセス時に前回端末にダウンロードした内容を使い回し、転送量を削減してユーザーの閲覧体験を高速化する。

ブラウザーキャッシュ図解.png

同じ人が繰り返し訪問しないタイプのサイト( SNS でバズって多くの人が訪れるが、一ページ見ただけで離脱するようなサイト)ではブラウザーキャッシュをフルに効かせても転送量の削減効果はない。逆に一度訪れた人が何度も訪れるような再訪率の高いウェブサイトでは、静的コンテンツに対して Cache-Control ヘッダーを付与することでブラウザーキャッシュを効かせ、転送量の削減とコンテンツの表示速度を高速化させることができる。

ブラウザーキャッシュを聞かせるには、適切な HTTP ヘッダー( Cache-Control ヘッダーなど)を付けてレスポンスを返せばよい。

サーバーサイドキャッシュ

サーバーサイドでキャッシュできるのは多くの人が共通して閲覧する、更新頻度の低いデータだけということに注意が必要。ログインが必要なサイトで、アクセスする人によってコンテンツを出しわけないといけないようなシステムではサーバーサイドのキャッシュはあまり利用できない。

サーバーサイドキャッシュ図解.png

サーバーサイドのキャッシュには転送量の削減効果はない(少なくとも自サイト訪問者との通信では)が、ランキング集計処理や外部 API の呼び出しなど CPU への負荷や時間がかかる処理の結果をキャッシュし、コンテンツの表示を高速化したいときに有効。

例えばこのサイトでは Amazon のアフィリエイトを利用しているが、 Amazon の API にリクエスト回数制限やリクエスト間隔制限があるので適度にキャッシュを行い、リクエスト回数制限に引っかからないようにしている。

サーバーサイドのキャッシュのやり方は色々ある。アプリケーションにキャッシュするためのコードを書いてメモリ上に保持したり、テキストファイルに保存したり、データベースに保存したり、 Redis などを使ったり。ウェブサーバーのキャッシュを使う方法もある。このブログでもいくつかのキャッシュ機構を組み合わせている。

アプリケーションキャッシュ.png

表示に関わる部分の一部をキャッシュする場合や、キャッシュの無効化をアプリケーションでコントロールしたい場合はアプリケーションでキャッシュする必要がある。

ウェブサーバーキャッシュ

レスポンス全体をキャッシュしてよい場合にはウェブサーバーのキャッシュを使うとよい。その方がアプリケーション層までリクエストが届かず、コンピューターリソースを節約できる。

様々なキャッシュの組み合わせ

更新頻度が低く、多くの人が共通して閲覧するコンテンツはサーバーサイドでもキャッシュできるしブラウザーキャッシュを効かせることができる。画像、 CSS 、 JavaScript などがそれで、これらのファイルはよく CDN ( Content Delivery Network )から配信される。 AWS だと CloudFront というのがある。この手のサービスはサーバーサイドのキャッシュとブラウザーキャッシュを効かせることに特化したもので、コンテンツの転送量を抑えつつウェブサイトの表示速度を高速化してくれる。

CDN もそうだが、サーバーサイドキャッシュとブラウザーキャッシュを併用すると、コンテンツを更新したいときに問題が出てくる。画像ファイルを新しいものに差し替えたが CSS が更新されず古いキャッシュが参照され、画像が非表示になってしまったり、ということが発生する。そういうことが起こらないように、 HTTP にはいくつか仕組みがある。

CDN だと ETag も自動付与してくれて、コンテンツの衝突を抑えてくれる。 CDN はよくできているのでよく分からない人はまずは CloudFront を使って勉強してみるとよいだろう。

特に注意が必要なのがアプリケーションキャッシュとウェブサーバーキャッシュの組み合わせだ。異なるレイヤーでキャッシュを効かせてしまうと、キャッシュを無効化したいときに狙い通りに無効化されず、意図しない障害になってしまったりする。コンテンツの特性に応じて、キャッシュは一つのレイヤーで行うようにするとよいだろう。画像や CSS はウェブサーバーでキャッシュし、更新頻度が低いが動的に生成される JSON はアプリケーションレイヤーでキャッシュするなど。

まとめ

  • ウェブサイトのコンテンツは何でもキャッシュすれば良いわけでない
  • サーバーサイドのキャッシュとブラウザーキャッシュの違いを理解する
  • 多段キャッシュに注意(特にアプリケーションキャッシュとウェブサーバーキャッシュの併用)

| @技術/プログラミング

DSC_4022.jpeg

Amazon Product Advertising API ( PA API )が 5.0 になるらしい。 4.0 は 3 月で廃止になるそうだ(当初は 2 月 11 日と言われていたが、 3 月まで伸びたみたい)。

最近 4.0 に対応させたのにな、と思って調べてみたら何と 4.0 対応したのは 10 年以上前だった。

PA API 4.0 までは AWS アカウントで利用する感じ1だったが、 PA API 5.0 では AWS から独立して Product Advertising API 専用のアカウントを登録しなければならないようだ。

AWS は US Amazon でアカウントを作るのに PA API のレポート画面へのログインでは Amazon Japan のアカウントを使うのは変だなと思っていたけど、 PA API 専用アカウントを設けることでその辺のねじれも解消されるだろう。

クライアントライブラリにはこれまで ecs という gem を使ってきたが、 PA API 5.0 対応はされてなくて、別の人が作った vacuum という gem に乗り換えた。

ecs という gem の名前に違和感をもつ人がいるかもしれない。いまでは Amazon で ECS といえば AWS の ECS ( Elastic Container Service ) のことを指すが、昔は Amazon 自身が PA API という名前ではなく Amazon ECS という名前でアフィリエイト用のシステムを提供していた。 Amazon は命名が色々紛らわしい。

PA API 5.0 は RESTful API ではなく GraphQL のような感じで、欲しいフィールド名を指定して API リクエストする感じになっている。レスポンスのサイズが小さくなって便利になった。

ちなみに移行ガイドは以下にあります。

DSC_4119.jpeg


  1. 10 年前にペパボの面接を受けに行ったときに「 AWS 使えますか?」と聞かれて「はい、使えます。アフィリエイトで小銭を稼いでいます」と答えてしまった。 

| @散財

ペーパードリッパーホルダー

概要

ドリップバッグのコーヒーはお湯に浸かって味が薄くなるが、ドリップバッグホルダーを導入してドリップをしやすくなりコーヒーの味が改善(濃く抽出される)。付属の受け皿で水切りができ、片付けも楽になった。


以前は会社で豆を挽いてコーヒーをいれて飲んでいたが、最近忙しくて豆から挽いてコーヒーいれて飲んでるような余裕がない。とはいえ全自動マシンのやつは飽きてしまったのでドリップバッグのやつを買って飲むようになった。まずはドトールのものを飲んでみてなかなか悪くないとは思ったが、どこまでお湯を注げばよいかがわかりづらい点や、お湯に完全に浸ってしまうことにより片付け時に水がポタポタと垂れてしまう点でいまいち体験が良くなかった。

そんななかコーヒードリップバッグホルダーというアイテムの存在を知った。

試しに買ってみるにはちょっと値段が高いなと思ったがリストカット感覚で注文してみた。テレビか何かで紹介されたばかりのようで Amazon には在庫がなく、届くまで二週間くらい待った。

使うときはこんな感じで使う。カップの上にドリップバッグホルダーをセットし、その上にドリップバッグをかける。

カップの中の様子を見ながらお湯を注げるのでどこまで注げば良いかわからないという問題や、ドリップバッグがお湯にどっぷり浸かってびちゃびちゃになるという問題が解決する。

おまけに受け皿も付いているので、まだ少し水がしたたる状態でカップから引き上げてもとりあえず置いておく場所を確保できる。とても便利。

さらにこれは期待していなかったことだが、ドリップバッグをお湯の中にドボンとつけないことで味もおいしくなる気がする。これまでドリップバッグではコーヒーの味が薄めになるのが不満だった。大抵のドリップバッグは豆の量が 7g 程度で少なめなので仕方がないのかなと諦めていたが、ドリップバッグホルダーを使うようになってから濃いめに抽出されるようになった。ドボンとお湯に浸からないおかげでお湯がコーヒー豆の中を通り、コーヒーの成分がちゃんと抽出されるのだろう。

図で説明するとこんな感じ。

ドリップバッグホルダーなし: お湯がすぐにフィルターの中から抜け出してしまい、十分にコーヒーの成分が抽出されない ドリップバッグホルダーあり: お湯が豆の中を通り抜けてからフィルターの外に出て行き、コーヒーの成分がよく抽出される

というわけでドリップバッグホルダー、めちゃおすすめです。

ドリップバッグは以下を使ってます。 100 パックで 2000 円くらいで安い!

| @技術/プログラミング

関連記事に画像を表示するようにして喜んでいたが、先月の AWS の請求額を見てビックリ。普段の 15 倍くらいの金額になっていた。デイリーの利用料金を見ると関連記事に画像を表示するようになった日から高くなっている。

CloudFront 転送量

このブログの画像は S3 に置いてあって CloudFront から配信している。これまでたくさん写真を掲載しても特にコストは高くなかった( Route 53 の費用など含めても $3 くらい、転送量だけだと $1.5 くらいだった)のが、転送量だけで $30 オーバーになっていた。ブログのサーバー代は Adsense 広告と Amazon アフィリエイトでまかなうつもりでやっているので、これでは完全に赤字になってしまう。

なぜ高くなったのかというと関連記事にサムネイル画像を表示することで、 imageproxy から CloudFront へのアクセスが発生するようになったからのようだった。こんな感じ。

image-data-transfer-infrastructure-1.png

imageproxy にもキャッシュの仕組みはあるが、 CloudFront が返す Cache Control ヘッダーの内容を理解せず決め打ちの時間でキャッシュを Expire させるので効率が悪い。

恐らく以下のように画像関連のインフラは AWS に寄せるのが一番効率的だと思う。Amazon の優秀なエンジニアが作ってる CDN が一番前段に出てブラウザーからのリクエストに答えるのがもっとも効率的に画像を配信できると思う。

image-data-transfer-infrastructure-2.png

ただ個人のブログレベルでここまでやるのは割に合わない感じがしたのでとりあえずは以下のような構成にした。

image-data-transfer-infrastructure-3.png

Nginx の proxy cache を使う。

キャッシュ時間は長めにとって 30d にしておいた。

あわせてキャッシュの HIT 率を計測するようにした。ログに $upstream_cache_status を書き出すようにして、 awk で定期的に集計するようにした。こんな感じ。

cat log/access.log \
  | grep 'cache_hit:' | grep -v 'cache_hit:-' | cut -f16 | sort | uniq -c \
  | awk '{
      if ($2 ~ /HIT/) {
        hit = $1
      };
      if ($2 ~ /EXPIRED/) {
        expire = $1
      };
      if ($2 ~ /MISS/) {
        miss = $1
      };
      sum += $1
    } END {
      hit_rate = hit/sum*100;
      expired_rate = expire/sum*100;
      miss_rate = miss/sum*100;
      print "HIT\t"hit_rate"%\nEXPIRE\t"expired_rate"%\nMISS\t"miss_rate"%"
    }'

こいつを Lokka の Dashboard に表示させる。

キャッシュヒット率

加えて、 Google の以下の記事を参考に、画像の遅延読み込みを行うようにした。

とりあえずはこれで様子を見たい。いまのところ、ちょびっとずつ転送量は下がってきているような感じがする。もうちょい下げたいところ。

しかし、画像の配信で毎月 $30 もかかるようであれば自前で画像をホストするのは諦めて Flickr に金払って PRO プランを継続した方が安いなと思い始めてしまった…。 Google Photos でも良いが、 Exif がわからなくなるのと埋め込み用の画像を取得する作業(公開用のアルバムを作ってそこに埋め込みたい写真を入れていく必要がある)が面倒くさいので移行に踏み切れない。

| @読書

DSC_6351

おもしろかった。 Twitter 、 CEO がコロコロ交代してて誰が中心人物なのかよく分からなかったがだいたいわかった。成立の過程が結構複雑で、 Podcast 配信会社だった Odeo を Noah Glass が創業し、 Blogger で一発あてたあとの Evan Williams が出資して会社を乗っ取り、 Evan Williams に憧れて入ってきたアルバイトの Jack Dorcey が Noah Glass とブレインストーミングして Twitter の原型を生み出し、 Odeo を飛び出して Twitter という会社を創業し Jack Dorcey が最初の CEO になる、という感じ。 2007 年から 2008 年頃にかけて自分が面白おかしく使っていた Twitter の中では群像劇が繰り広げられていたことがわかり興味深かった。

特に興味深かった箇所は以下で、自分も 2008 年頃、実家に住んでて周りにインターネットのことを話せる友だちはほとんどいなかったけど、 Twitter 越しにインターネットユーザーと交流することができて孤独を癒やされていた気がする。

 このステータスは、その場にいない人々を結びつけるのに役立つ。どんな音楽を聴いているか、いまどこにいるかということを、共有するだけではない。人々を結びつけ、孤独感を癒すことが重要なのだ。パソコンの画面を見つめているときに、どんな世代でも味わう感情を、消し去ることができる。ノア、ジャック、ビズ、エブは、そういう感情を味わいながら成長し、パソコンのモニターに安らぎを求めた。結婚生活と会社がだめになりつつあるとき、ノアはその感情を毎夜味わっている。孤独感を。

 エブがブロガーに熱中した原動力も、そういう感情だった。アパートメントに独りでじっと座り、孤独で、友だちもなく、キーボードを通じて世界とつながっていた。何年も前にビズが母親の家の地下室でブログをはじめた理由もおなじだ。ジャックもおなじ理由から、セントルイスにいるころにライブジャーナルのアカウントを取り、掲示板をうろついて結びつきを求めている人々とやりとりするために、コーヒーショップで何時間も粘った。ステータスという構想は、そういったことすべての解毒剤になり、孤独感を癒せるかもしれない、とノアは考えた。

ニック・ビルトン. ツイッター創業物語 金と権力、友情、そして裏切り (Japanese Edition) (Kindle Locations 1017-1026). Kindle Edition.

ただその後、 Evan Williams が CEO になって Twitter はステータス共有(投稿欄のプレースホルダーは "What are you doing?" )から情報発信メディア(投稿欄のプレースホルダーは "What's happening?" )への変革を図った。個人のステータスではなく、その人の周囲の状況を伝えて欲しいということだ。確かに 2010 年くらいから Twitter の様子が変わったように思う。東日本大震災のあとは日本の Twitter もニュース寄りになっていって、 2008 年頃のジャンプ放送局のような雰囲気はなくなってしまった。

Twitter はビジネスとしては成功したが、創業者たちはお互いの人間関係を悪化させ、大事な友人を失いながら莫大な富を手にした。最終章に出てくる、ビズ・ストーンが貧乏だった時代のエピソードがとても心温まる。この部分だけでも読んで良かったと思った。


Noah Glass 、 Twitter の最初期を支えた人物だと思うけど会社を追い出されて Twitter 社の歴史からもいなかったことにされ、ひたすら可哀想。おまけに Noah の Twitter アカウントは「不審なアカウントです」と表示されたりする。

| @技術/プログラミング

Archive ページ をリファクタリングした。

これまで gulp をビルドに利用していた(Archive ページを React Router 化)が、 webpack を使うように変えた。

React のコードも見直して、 DOM の状態に依存して表示・非表示を切り替えるコードがあったりして( 🐙 Archive ページにカテゴリごとの記事件数を表示する機能を追加 )ごちゃごちゃしてたので DOM を直接ごちゃごちゃ操作するのをやめて React で管理するように変えた。親コンポーネント、子コンポーネント、孫コンポーネント、子コンポーネントの兄弟コンポーネント間で状態を共有する必要があって、結構難儀した。

Archives React Component 1.png

実際の画面を見るとこんな感じ。

Archives React Component 2.png

App というコンポーネントがルートにあって、子に CategoryListCategoryList の子コンポーネント( App からすると孫)に Category コンポーネントがある。記事一覧自体は CategoryList と兄弟コンポーネントである Archive コンポーネントが担当している。

Archives React Component 3.png

こんな感じで特定の Category が選ばれたことを Category のクリックイベントをトリガーに CategoryList に伝達し、 CategoryList はさらにそれを App コンポーネントに伝える。その結果が App から Archive コンポーネントに伝えられ、表示内容が変更される。

この辺を参考にして実装した。コールバック関数を props として引き回し、状態を回収する感じ。

ただこういう込み入った状態の管理を React で行う場合は Redux などを利用するのが良いようだった。

前職のとき、 Redux とか Flux が出てきた頃に F/E のエンジニアの人たちが熱狂してたけど自分はいまいち理解できなくて、傍観するだけだったが、いまさらにして何となく Flux アーキテクチャの概要的なものを把握することができた気がする。ただ自分の場合は深みに入り込まず極力シンプルに作りたいと思っていたので Redux などには手を出さず、 Callback で愚直に状態を親コンポーネントに伝達していく方法をとった。

React 、やっぱり大分良いものだとという感じがした。 jQuery でクラスや CSS で show - hide を Toggle していた頃とは隔世の感がある。

| @Mac/iPhone

Castro

以前書いたことがある Castro🎧 Overcast と Castro - portal shit!)がすごく便利になっている。

以前の記事では、 Overcast にあるチャプター移動機能やオーディオエンハンスメント・無音カット機能が Castro にはないと述べていた。しかし一年ちょい前にリリースされた Castro 3 により、有料ではあるがこれらの機能が提供されることになった。

これがめっちゃ便利。 UI は前にも述べたとおりすぐれているし、さらに購読中の Podcast のショーノートを検索する機能も今年の 1 月についた。

これもスーパー便利で、「あの Podcast の誰々がゲストで出てる回を検索して聞きたい」というありがちな要望を満たすことができる。以下は Rebuild のエピソード一覧で "higepon" で検索し、 higepon さんが出てるエピソードだけを絞り込んでる図。

Rebuild.fm で higepon さんが出演してる回で絞り込んでる様子

めっちゃ便利で最高なので是非お試し下さい。