memcachedでYoutubeAPI結果をキャッシュ化

環境

CentOS 6.5
PHP 7.0
CakePHP 3.3

インストールしたmemcachedのバージョンは 1.4.4

目的

Youtube APIはクウォータと呼ばれる仕組みで利用制限がかけらています。

TuListのようにYoutube検索をするリンクがある場合、コンテンツが増えるとサーチエンジンのbotアクセスなどにより、このクウォータが消費されてしまいます。

現在、TuListはGoogleから”低品質なサイト”として”手動対応”されているため、GoogleからのアクセスとGoogleBotからのアクセスがあまりない状態だが、このような状態でも30日間のアクセス数が45,000を超えてきています。

そろそろ、この”手動対応”に対して解除申請を考えているのだが、解除された場合数倍のアクセスにもなりうるため、YoutubeAPIのレスポンス結果をキャッシュ化してクウォータの節約を測ります。

memcachedとは

多くのWebサービスでは、DBやファイルを利用して情報の保存と取得を繰り返しています。

これらの情報の保存と読み込みはHDD(SSD)の処理性能に影響を受けてWebサービス全体の処理性能に影響を与えます。

そこで、出てくるのがHDDやSSDなどのような長期間の保存を目的とした媒体ではなく、一時的な情報の保存と読み込みを得意としているメモリを使った方法です。

memcachedは情報の保存先をメモリにする際にとても便利なミドルウェアでmixiやfacebookなどの大規模なサービスでも利用されています。

memcachedをPHPで使うためのセットアップ

memcachedのインストール

yum使って簡単一発インストール!

 yum -y install memcached

php-pecl-memcachedのインストール

phpからmemcachedを使うためのライブラリです。

これもyumで簡単インストール

 yum -y --enablerepo=epel,remi,remi-php70 --disablerepo=remi-safe install php-pecl-memcached

php7をインストールする際にremi-php70リポジトリを利用していたのでオプションで指定しています。

memcachedの起動

 chkconfig --add memcached
 chkconfig memcached on

で自動起動設定をして

 /etc/init.d/memcached start

で起動。

いや~実に簡単!yumって素晴らしい!!

mecachedの確認

memcachedの確認はtelnetで行います。

接続

 telnet localhost 11211

メモリに保存

key_hogeというキーで、圧縮せずに(0)10byteのデータを20秒間保存

 set key_hoge 0 20 10

enter押して10byte分の情報を入力

 value_fuga

メモリから取り出し

 get key_hoge

出力結果

 get key_hoge
 VALUE key_hoge 0 10
 value_fuga
 END

20秒過ぎて実行した出力結果

 get key_hoge
 END

取得できないことが確認できる

key一覧を取得

これはちょっとめんどくさい

下記で保存されている一覧を取得

 stats items

これが出力結果

 stats items
 STAT items:2:number 2
 STAT items:2:age 35887
 STAT items:2:evicted 0
 STAT items:2:evicted_nonzero 0
 STAT items:2:evicted_time 0
 STAT items:2:outofmemory 0
 STAT items:2:tailrepairs 0
 STAT items:29:number 2
 STAT items:29:age 41493
 STAT items:29:evicted 0
 STAT items:29:evicted_nonzero 0
 STAT items:29:evicted_time 0
 STAT items:29:outofmemory 0
 END

つづけて中身を確認
指定したid(2)を10件まで表示

 stats cachedump 2 10

これでようやくkeyが出力される
以下は出力結果

 stats cachedump 2 10 
 ITEM myapp_cake_default_test2 [10 b; 1503616873 s]
 ITEM myapp_cake_default_test [9 b; 1503616681 s]
 END

どのidにどのkeyが格納されているかは不明という、コマンドラインでは何とも使いづらい・・・
導入時に正しく保存されているかどうかが確認できればよいので困りはしないけどね。

切断

 quit

ほかにもいろいろあるけど、ここでは省略

memcachedの設定

設定ファイルは下記のとおり
/etc/sysconfig/memcached

デフォルトの中身はこんな感じ

 PORT="11211"
 USER="memcached"
 MAXCONN="1024"
 CACHESIZE="64"
 OPTIONS=""

デフォルトでは64mbが上限値

本番環境では様子を見ながら調整が必要そうだ

CakePHPの設定

before

 'default' => [
   'className' => 'File', 'path' => CACHE, 'url' => env('CACHE_DEFAULT_URL', null)
 ],

after

// デフォルトのキャッシュ設定
'default' => [
  'className' => 'Cake\Cache\Engine\MemcachedEngine',
  'path' => CACHE,
  'servers' => [env('127.0.0.1:11211')],
  'prefix' => 'myapp_cake_default_',
  'username' => env('memcached'),
  'duration' => '+1440 minutes',
],

// YoutubeAPIのキャッシュ設定
// とりあえず10分間保持
'youtube_api' => [
  'className' => 'Cake\Cache\Engine\MemcachedEngine',
  'path' => CACHE,
  'servers' => [env('127.0.0.1:11211')],
  'prefix' => 'myapp_cake_yuapi_',
  'username' => env('memcached'),
  'duration' => '+10 minutes',
],

YoutubeAPIのレスポンスをキャッシュ

ながかった~~~><
ようやく本題

YoutubeAPIを実行しているメソッドを下記のとおり修正

/**
 * @param AbstractYoutubeCondition $condition
 * @return string|boolean
 */
private static function __callApi(AbstractYoutubeCondition $condition) {

  $responce = Cache::read($condition->hash(), 'youtube_api');
  if (!empty($responce)) {
    return $responce;
  }

  $url = $condition->getUrl();
  $responce = @file_get_contents($url);
  $responce = preg_replace("/ {2,}/", " ", $responce);

  Cache::write($condition->hash(), $responce, 'youtube_api');

  return $responce;
}

APIレスポンスのキャッシュ化は見越していたので、AbstractYoutubeCondition::hach()メソッドで、APIパラメータをハッシュ化。

動作確認

動作確認しやすくするためmemcacheを再起動して内容をすべてクリア

 [vagrant@localhost ~]$ sudo /etc/init.d/memcached restart 
 memcached を停止中: [ OK ]
 memcached を起動中: [ OK ]
 [vagrant@localhost ~]$ telnet localhost 11211 
 Trying ::1...
 Connected to localhost.
 Escape character is '^]'.
 stats items
 END

検索後にmemcachedの内容を確認
うん!バッチリ!!!

 stats items
 STAT items:25:number 1
 STAT items:25:age 25
 STAT items:25:evicted 0
 STAT items:25:evicted_nonzero 0
 STAT items:25:evicted_time 0
 STAT items:25:outofmemory 0
 STAT items:25:tailrepairs 0
 STAT items:29:number 1
 STAT items:29:age 24
 STAT items:29:evicted 0
 STAT items:29:evicted_nonzero 0
 STAT items:29:evicted_time 0
 STAT items:29:outofmemory 0
 STAT items:29:tailrepairs 0
 END
 stats cachedump 25 10 
 ITEM myapp_cake_yuapi_d8d1033736642ec6d33edfe8c2faae19ea7bb0f5 [19217 b; 1503579276 s]
 END
 get myapp_cake_yuapi_d8d1033736642ec6d33edfe8c2faae19ea7bb0f5
 VALUE myapp_cake_yuapi_d8d1033736642ec6d33edfe8c2faae19ea7bb0f5 0 19217
 {
  "kind": "youtube#videoListResponse",
 ・・・中略・・・
  "https://en.wikipedia.org/wiki/Sport"
 ]
 }
 }
 ]
}
END