| @ブログ

noisy-ads.jpg

スマートフォンで検索していてある大手ブログサービスでホストされているブログの記事に辿り着いた。記事を読んでいくと、ページ構造が「パーマリンクとは?」という感じになっていて驚いた。まるで蟻地獄で、下にスクロールしても終わりがなく、一度入り込んだら逃げられない感じだった。一つの URL に一つのコンテンツというインターネットのお約束を無視していて、 URL は個別記事のものだけど、下までスクロールすると次の記事の本文が読み込まれる。さらにスクロールするとそのまた次の記事が読み込まれる。 AutoPagerize がページャーのないところでも有効化されている感じで、読者の好みで無効化できない状態になっていた。

記事と記事の間には芸能ニュース記事へのリンクが差し込まれたり、そのブログサービス内で人気の記事ランキングが挟まれたりする。 A さんのブログを読んでいたはずなのに気がつくとゴシップニュースか他の人の炎上記事を読むことになっている。おまけにそこに広告も挟まれてくる。ページ内で URL が指し示すオリジナル記事の分量が 10% に満たないこともあるんじゃないだろうか。書き手にとっても読み手にとっても体験が良くない。大手のブログサービスはどこも同じような感じで、他のニュース記事や人気記事への誘導が激しい。

Twitter を追い出されたあとの Evan Williams が Medium を始めたときは意味がわからなかった。今時ブログサービスなんて始めてどうするんだろうと思った。はてなブログに関しても、なぜいま新しいブログサービスが必要なのかわからなかった。しかしいまならその理由がわかる気がする。

インターネット上でのコミュニケーションの場がブロゴスフィア(死語)から SNS へ移り、ブログの読者は SNS を流し読みしていてタイムラインに流れてきたコンテンツを消化するだけ。著者はコンテンツを提供するだけの存在となってしまい、両者の間でインタラクティブなやりとりが生まれなくなった。そんな状況でもう一度著者と読み手を中心に据えようとして始まったのが Medium だったのではないだろうか。

Medium もはてなブログも広告は表示されないか表示されても少しだし、 Medium の人気記事への誘導は控えめで、はてなブログは同一ブログ内の記事しかお勧めしてこない。

はてなブログを開始されて間もないときに jkondo が書いている記事の最後にこんなフレーズが出てくる。

「個」としての活動が、人生に新しい展開を持ち込み、より豊かな人生につながります。

SNS 、ブログ、あらゆるところで巨大なサービス(=プラットフォーム)が幅を効かせて個人を飲み込もうとしているいま、どこでブログを書くと一番記事の価値を毀損しないかまで考えてブログを書く場所を選んだ方がよいと思う。はてなブログや Medium 、 note も流行ってるっぽいが、自分としてはやっぱり自分のブログを持つのが最高だと思ってる。

独立自営のブログをやる人の選択肢を増やすために、今後も Lokka のメンテナンスはやっていきたい。やりたい・やると宣言して出来てないことだらけで申し訳ないけど、少しずつこのブログの機能を master ブランチに移植していって 2020 年でも常用できるブログにしていきたい。ちなみに以下は Markdown をオン・ザ・フライでプレビューする機能の Pull Request 。

あと Slack に Workspace を作った。何年か前に調べたときは lokka.slack.com が空いてたんだけど誰かに取られてたので lokkahq.slack.com となった。良かったら入ってください。ちなみにまだ僕しかいません。

追記 2020-04-13

cho45 さんの昔のブログ記事を読んでたらわかる(わかる)という記事があったのでリンクしておきます。

ノウハウ蓄積みたいなコンテンツってASP型で預けるのは不安がありすぎるので、自力で配信しようねみたいなウェブ縄文時代みたいな話になるんですが……

CGMサービスの矜持について - 氾濫原

ウェブ縄文時代 ってのは言い得て妙だと思いました。残念だけど一周回って個人ウェブサイトは縄文時代を迎えつつあるんだと思う。

| @ブログ

最近、やたらこのブログにスパムコメントが来るようになった。コメントがあったらメールで通知されるようにしてるのだけど、コメント通知メールが一日十件くらい届く。

スパムコメント

Google の reCAPTCHA を入れたことでほぼほぼスパムコメントは弾けていたのだけど、最近のスパムは reCAPTCHA をすり抜けるようになってしまったっぽい。加えて Akismet のスパム判定ロジックもポンコツになってしまったようで、ほぼほぼすべてのコメントをスパムと判定しなくなってしまった。

Lokka では Akismet でスパム判定されたコメントは一括削除できるようになっている(この機能は自分で作った)が、スパム判定されなかったコメントはちまちま一つずつ削除しなければならないのが非常に煩わしかった。

なのでコメント一覧にチェックボックスを表示して、チェックを入れたコメントを一括で削除できるようにしてみた。めっちゃ便利。

コメント一括削除

Akismet プラグインも少し改造して、自分で NG ワードを設定できるようにした。最近のスパムは露骨に Viagra とか Cialis みたいなキーワードが入ってて、どうしてこんなわかりやすいやつを Akismet は素通りさせてしまうのかわからないのだけど、自前の NG ワードフィルターで二重にチェックするようにした。

NG ワード

最後にアクセスログからスパムっぽいコメントの数を集計して管理画面のダッシュボードで閲覧できるようにした。引き続き監視していきたい。

スパムの状況

| @ブログ

Rubbish

ブログで使ってる Amazon S3 のバケットの画像のほとんどをミスって空ファイルにしてしまった…。 S3 に上がっている画像、 Cache-Control ヘッダーが付与されていないのですべての画像ファイルに Cache-Control ヘッダーを付与しようとしての事故だった。ファイル一覧を取得して AWS SDK Ruby で Cache-Control だけ付与するつもりだったのにファイルそのものを空で上書きしてしまって無となった。

大した操作じゃないと思って事前にバックアップを取っていなかった& S3 に上げておけば安心だと思って日常のバックアップも行っていなかった。スーパーアホ。写真ならアップロードし直すことは可能だけど、キャプチャとか、アニメーション Gif とか、ブログの内容に合わせて OmniGraffle で描いた画像は基本的には元のファイルが残ってなくて元に戻すことができなかった…。

CDN に残っていたものとローカルでキャッシュされていたものを探したが、 500 ファイル以上が失われてしまった。つらい…。

画像の吹っ飛び、過去にも何回か起こっている。レンタルサーバーの障害で消えたパターンもあったけど、自分のミスで消してしまうパターンが多い。自分が一番信用ならないので画像はプロに管理してもらうのが一番だなと身にしみて思った…。とりあえず S3 バケットのバージョンコントロールを有効にしたいと思います…

追記

AWS SDK Ruby での正しい Cache-Control ヘッダー付与の仕方は以下だった。

object.copy_from(object, cache_control: 'max-age=2592000,s-maxage=31536000', metadata_directive: "REPLACE")

#copy_from の引数に medata_directive: "REPLACE" を渡す必要がある。

🙅🏻‍♀️🙅🏻‍♀️🙅🏻‍♀️以下は NG なので注意されたし ☠️☠️☠️

object.put(cache_control: 'max-age=2592000,s-maxage=31536000')

これやるとファイルの body が空になります 🈳🈳🈳

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

キャッシュ

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 はアプリケーションレイヤーでキャッシュするなど。

まとめ

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

| @ブログ

今宿駅近くのバー

このブログのフィードを誰が読みに来ているのか調べてみた。一ヶ月間で 100 回以上見に来ている上位の UA は以下。

Feed Crawler Ranking

なんと一位はフィードリーダーの bot ではなく Slackbot だった。 Googlebot よりも多い。もはや Slack が一番のフィードリーダーになっているのかもしれない。こうなると PubSubHubbub とかの仕組みに乗っかって無駄なクローリングが発生しないようにして欲しい。フィードを購読する Slack の Workspace が増えるほどクローリング回数が増えてしまうと負荷がバカにならない。

三番目の Hatena::Russia::Crawler/0.01 ってのは Hatena と名前に入っているが本当にはてなのクローラーなのだろうか。 Russia という文字列が怪しい。

Feedly はもっと多いかと思ったが非常に少なかった。意外と Fastladder が多い。みんなどこかのサーバーで運用しているのだろう。ご苦労様です。

今回、ログを調べていて Feedeen といったサービスが存在していることを初めて知った。他に Feedbin などいくつか有料の RSS リーダーが存在しているようだ。 Google Reader や Livedoor Reader が終了した後、国内外で有料のフィードリーダーが開発されサービス提供されているのだろう。 Feedeen は料金が月 200 円で安い。日本人が作っているというのも安心感がある。自分は Google Reader 終了後はまず The Old Reader を使っていたけどいまは Inoreader を使っている。 Inoreader も開発元はルーマニアのインディー感あふれる会社で、フィードリーダーの世界は独立系の企業やデベロッパーによって活況を呈しているようだ。よいことだと思う。昔のインターネットを思い出す。

ちなみにフィードリーダー系の Crawler は UA にそのフィードの購読者数を表示しているのがおもしろい。こんだけ購読者数がいるんですよ、ということをブログ主に伝えて UA でブロックされないようにしているのだろう。

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

関連記事に画像を表示するようにして喜んでいたが、先月の 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 がわからなくなるのと埋め込み用の画像を取得する作業(公開用のアルバムを作ってそこに埋め込みたい写真を入れていく必要がある)が面倒くさいので移行に踏み切れない。

| @WWW

飾り瓦

以前、 OGP を読み込んでキャッシュする仕組みを作ってたけど、こいつをアップデートして iframe として静的な HTML を読み込むバージョンに作り直した。 URL をクエリパラメーターとして渡すと相手方のサイトにアクセスして OGP を読みに行き、プレビュー用の HTML を生成してキャッシュする。いまは Lokka Plugin として作ってるけどこいつはブログアプリケーションと密結合する必要はないので独立した Web アプリケーションにしてもよいかもしれない。

ブログにリンクを張ると OGP を展開する仕組み、いろんなサイトやサービスで独自実装されててもったいないと思う。 Facebook が OGP の仕様を作ったけど利用するかどうかはリンク元次第だし、 Twitter は Twitter Card という独自の仕組みを作ってる。この辺はいい感じに一本化して欲しさがあるが、大人の事情で多分できないだろう。

ということでグローバルな OGP 君があったら良いだろうと思う。 OGP 君は一枚噛ませるだけで OGP 関連の面倒なこと(リンク先サイトのOGP タグ読み込み、 OGP によるプレビュー生成、生成したパーシャル HTML のキャッシュ)などをやってくれる。様々なサイトでキャッシュが共有されるのでインターネット資源が有効活用される。OGP 君運営者は様々なサイトに script タグなり iframe タグなり1を埋め込めるので、そこでサイトの利用状況などを副産物としてゲットすることができる。 Google あたりだったらこの辺の情報を金にできそう。

ちなみに同じようなことを考えた人はすでにいて Embedly というサービスがあるようだが、これはリンクを張る側からお金を取る仕組みのようでいまいちイケてないと感じる。見栄えの良いリンクにしたいのはリンクする側ではなくどちらかというとされる側なはずなので、リンクされる側からお金をもらうような仕組みの方が良いはず。


  1. iframe はセキュリティ上、異なるドメインのものを埋め込むのはまずかった。やるなら script タグで動的に DOM を生成するタイプのものだろうなぁ。