本日も乙

ただの自己満足な備忘録。

セッションをMemcachedに格納してみる

かなり久しぶりの更新です。
前回では、HerokuでSymfony2を動かすようにしてみましたが、セッションをMemcachedに格納するところがどうしてもできずにいました。

しかし、PHP session handling on Herokuを見つけまして、試したらちゃんと動きました。
結局は、設定ミスや勘違いが原因だったのですが、同じようなことにハマっている人もいるかもしれないので方法を紹介します。

今回の目標

  • HerokuのMemcachierアドオンを入れてHerokuからMemcachedを動かせるようにする
  • Symfony2のセッション管理にMemcachedを使うようにする

環境

  • Mac OS X 10.9.2
  • Git 1.8.5.2
  • Symfony2 (2.3.16)
  • PHP 5.5.14

前提条件

  • Herokuのアカウントを登録、アドオン追加のため、クレジットカードも入力済みとする
  • Git, Heroku Toolbelt, Composer はインストール済みとする
  • PHPMemcached(memcacheではない)モジュールをインストールする
    • $ php -mmemcachedと表示されていれば大丈夫です

Memcachierアドオンをインストール

HerokuでMemcachedを使うアドオンとして、Memcachierを使います。

無料枠もありますが、使うにはクレジットカードの登録が必要です。

Memcachierアドオンをインストールします。

$ heroku addons:add memcachier:dev
Adding memcachier:dev on heroku-symfony2-test0515... done, v17 (free)
Please allow up to three minutes for MemCachier credentials to sync.
Use `heroku addons:docs memcachier` to view documentation.

設定を確認してみます。

$ heroku config | grep MEMCACHIER
MEMCACHIER_PASSWORD: xxxxxxxxxx
MEMCACHIER_SERVERS:  xxx.dev.ec2.memcachier.com:11211
MEMCACHIER_USERNAME: xxxxx

Memcachedを使うための設定が環境変数に格納されています。

composer.jsonMemcachedを追加する

composer.jsonMemcachedを追加して、composer updateします。

// composer.json
{
    "require": {
       ...,
        "ext-memcached": "*"
    }
}
$ composer update

PHPMemcachedモジュール(memcacheではない)をインストールしていないと、以下のようなエラーがでます。

Your requirements could not be resolved to an installable set of packages.

  Problem 1
    - The requested PHP extension ext-memcached * is missing from your system.
  Problem 2
    - Installation request for symfony/framework-standard-edition 2.3.x-dev -> satisfiable by symfony/framework-standard-edition[2.3.x-dev].
    - symfony/framework-standard-edition 2.3.x-dev requires ext-memcached * -> the requested PHP extension memcached is missing from your system.

フロントコントローラ(app_xxx.php)に、ini_set()を追記する

先ほどのMemcachedの設定をPHPの設定に組み込むために、ini_set()を使います ((.user.iniを使う方法もあります))。
今回は、web/app_heroku.phpに以下のように追記します。

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Debug\Debug;

+ini_set('session.save_handler', 'memcached');
+ini_set('session.save_path', 'PERSISTENT=pool ' . getenv('MEMCACHIER_SERVERS'));
+ini_set('Memcached.sess_binary', 1);
+ini_set('Memcached.sess_sasl_username', getenv('MEMCACHIER_USERNAME'));
+ini_set('Memcached.sess_sasl_password', getenv('MEMCACHIER_PASSWORD'));

Memcachedの設定値はgetenv()環境変数から取得するようにしています。

Symfony2でセッションの保存先をMemcachedに変更する

先ほどのweb/app_heroku.phpでセッションの保存先をMemcachedに変更したので、Symfony2の設定ファイルでは以下のように修正します。

# app/config_heroku.yml

 imports:
    - { resource: config.yml }
    - { resource: parameters_heroku.yml }

 framework:
     router:
         resource: "%kernel.root_dir%/config/routing_heroku.yml"
         strict_requirements: true
     profiler: { only_exceptions: false }
+   session:
+      handler_id: ~

 web_profiler:
     toolbar: true

handler_id: ~とすることで、php.iniSessionセクションの設定値をそのまま読み込むようになります。
Symfony2では、この設定をしないとini_set()で設定を変更してしまいます *1

デプロイ

Gitのリポジトリに追加して、git pushすればデプロイ完了です。

$ git add -A && git commit -m "Add Memcached support"
$ git push heroku master

最後に

HerokuでMemcachedを有効にして、セッションを格納できるようになりました。
デプロイする度にSymfony2のキャッシュがクリアされてしまい、app/cache/<env>/sessionsに格納されていたセッションファイルも削除されるためセッションが切れてしまっていましたが、Memcachedに格納することでデプロイしてもセッションを保つことができるのでより実用的に使えるのではないかと思います。

*1:僕がここが一番ハマったところです