2013年12月13日金曜日

ZendFrameworkでMemcachedを使用する

Zend_Cache_Memcachedのサンプル

ZendFrameworkにあるZend_Cache_Memcachedを使用するサンプルです。

ZendFrameworkにはZend_Cacheという仕組みがあります。
特徴としては、保存するデータの形式を指定する フロントエンド と、データの保存の仕方を指定する バックエンド があります。
今回は、フロントエンドに Core を指定し、バックエンドに Memcached を指定しました。
(Core とはフロントエンドの基本的な機能を実装したものになります)
まず、下記のようにキャッシュにデータを追加するアクションを作成します。

/**
 * データの追加アクション
 */
public function addAction()
{
    // フロントエンド(Core)の設定
    $frontend = array(
        'lifetime' => 300,
    );
    // バックエンド(Memcached)の設定
    $backend = array(
        'servers' => array(
            array(
                'host'             => 'localhost',
                'port'             => 11211,
                'persistent'       => true,
                'timeout'          => 300,
                'weight'           => 1,
                'retry_interval'   => 15,
                'status'           => true,
                'failure_callback' => null,
            ),
        ),
        'compression' => true,
    );
    $cache = Zend_Cache::factory("Core", "Memcached", $frontend, $backend);
    
    // データをキャッシュ
    $cache->save("value1", "key1");
    $cache->save("value2", "key2");
    $cache->save("value3", "key3");
    
    echo "key1 : " . $cache->load("key1") . \n";
    echo "key2 : " . $cache->load("key2") . \n";
    echo "key3 : " . $cache->load("key3") . \n";
}

上記のアクションを実行し、キャッシュしたデータが画面に出力されていればキャッシュ機能は実装完了です。
次にキャッシュしたデータを取得するアクションを作成します。

/**
 * データ取得アクション
 */
public function getAction()
{
    // フロントエンド(Core)の設定
    $frontend = array(
        'lifetime' => 300,
    );
    // バックエンド(Memcached)の設定
    $backend = array(
        'servers' => array(
            array(
                'host'             => 'localhost',
                'port'             => 11211,
                'persistent'       => true,
                'timeout'          => 300,
                'weight'           => 1,
                'retry_interval'   => 15,
                'status'           => true,
                'failure_callback' => null,
            ),
        ),
        'compression' => true,
    );
    $cache = Zend_Cache::factory("Core", "Memcached", $frontend, $backend);
    
    echo "key1 : " . $cache->load("key1") . "\n";
    echo "key2 : " . $cache->load("key2") . "\n";
    echo "key3 : " . $cache->load("key3") . "\n";
}

上記の追加アクションを実行後、データの取得アクションを実行してデータが取得できれば実装完了です。
実は、キャッシュ機能を実装する際にハマったことがあって、バックエンドのMemcachedにホスト情報を指定すると
何故かキャッシュされないという現象が起きていました。
問題点を調べたところ、servers内の配列にあるfailure_callbackキーに指定するパラメータ値が原因でした。
はじめは、ZendFrameworkのドキュメントを見ながらパラメータを指定していたのですが、初期値が空と書いてあったので同じように設定していました。
実はこれが大間違いで、failure_callbackの初期値はNULLでした。
空の値を設定していたので不正な引数として処理されていたようです。

ついでに他の初期値も確認してみたところ、タイムアウトも5ではなく1になっていました
ドキュメントの初期値

$backend = array(
    'servers' = array(
        array(
            'host'             => "localhost",
            'port'             => 11211,
            'persistent'       => true,
            'weight'           => 1,
            'timeout'          => 5,
            'retry_interval'   => 15,
            'status'           => true,
            'failure_callback' => "",
        ),
    ),
    'compression'   => false,
    'compatibility' => false,
);

実際のライブラリに設定されていた初期値

/**
 * Default Values
 */
const DEFAULT_HOST = '127.0.0.1';
const DEFAULT_PORT =  11211;
const DEFAULT_PERSISTENT = true;
const DEFAULT_WEIGHT  = 1;
const DEFAULT_TIMEOUT = 1;
const DEFAULT_RETRY_INTERVAL = 15;
const DEFAULT_STATUS = true;
const DEFAULT_FAILURE_CALLBACK = null;

protected $_options = array(
    'servers' => array(array(
        'host' => self::DEFAULT_HOST,
        'port' => self::DEFAULT_PORT,
        'persistent' => self::DEFAULT_PERSISTENT,
        'weight'  => self::DEFAULT_WEIGHT,
        'timeout' => self::DEFAULT_TIMEOUT,
        'retry_interval' => self::DEFAULT_RETRY_INTERVAL,
        'status' => self::DEFAULT_STATUS,
        'failure_callback' => self::DEFAULT_FAILURE_CALLBACK
    )),
    'compression' => false,
    'compatibility' => false,
);

さらによく見てみると、実はこのZend_Cache_MemcachedMemcachedではなくMemcacheのインスタンスを生成しているようです。

/Zend/Cache/Backend/Memcached.php

/**
 * Constructor
 *
 * @param array $options associative array of options
 * @throws Zend_Cache_Exception
 * @return void
 */
public function __construct(array $options = array())
{
    if (!extension_loaded('memcache')) {
        Zend_Cache::throwException('The memcache extension must be loaded for using this backend !');
    }
    parent::__construct($options);
    if (isset($this->_options['servers'])) {
        $value= $this->_options['servers'];
        if (isset($value['host'])) {
            // in this case, $value seems to be a simple associative array (one server only)
            $value = array(0 => $value); // let's transform it into a classical array of associative arrays
        }
        $this->setOption('servers', $value);
    }
    $this->_memcache = new Memcache;

おわり