Back-End

19 jul, 2013

Como utilizar Memcached com Zend Framework e Libmemcached

Publicidade

Este artigo ensina como utilizar o servidor de cache Memcached com o framework em PHP Zend Framework. O Libmemcached é uma biblioteca em C que faz a comunicação com o servidor de Memcached e para isso é necessário a biblioteca memcached do PECL (PHP Extension Community Library) – esta lib é instanciada através de código PHP na aplicação em Zend Framework. O ambiente utilizado é Linux (tanto para o servidor web quanto para o servidor de cache).

Passo 1: Instalação Memcached

No Ubuntu, para instalar o servidor Memcached usando o apt-get:

sudo apt-get install memcached

Por padrão, o servidor Memcahed irá atender na porta 11211. Caso você tenha problemas com o apt-get, será necessário baixar o Memcached do site oficial (ver link em Referências), descompactar, compilar e instalar.

Passo 2: Instalação Libmemcached

Instale a Libmemcached. Esta library é uma API em C para se comunicar com o servidor Memcached. Depois, baixe e instale diretamente do site oficial (ver link em Referências). Será necessário descompactar, compilar e instalar o arquivo .tar.gz disponibilizado para download no site oficial.

Uma opção mais fácil é usar o apt-get do Linux:

sudo apt-get install libmemcached5

Passo 3: Instalação lib memcached do PECL

Instale a lib cliente que se comunica com a Libmemcached, em nosso caso utilizamos a lib memcached do PECL. Para instalar, foi necessário compilar o arquivo fonte e executar alguns procedimentos:

  1. Faça o download da lib memcached no site oficial do PECL (ver link em Referencias);
  2. Faça a instalação e lembre-se de rodar o comando configure informando o diretório da Libmemcached (Passo 2) – geralmente ela é instalada sob o diretorio “/usr/lib”. No meu caso, utilizei o comando configure da seguinte forma: ./configure –with-libmemcached-dir=/usr/lib;
  3. Copiar o arquivo gerado “memcached.so”. Este arquivo será gerado na pasta “/modules”, para a pasta de libs do PHP, por exemplo: “/usr/lib/php5/20100525+lfs/”;
  4. Copiar o conteúdo do arquivo memcached.ini para o arquivo php.ini do PHP – geralmente fica em “/etc/php5”.

Opção mais simples, mas que não funcionou comigo, é utilizar o apt-get:

sudo apt-get install php5-memcached

Passo 4: Rode um exemplo PHP simples

Reinicie o servidor web e rode um exemplo em PHP simples para testar. Exemplo a seguir:

Arquivo “memcached.php”

$mc = new Memcached(); 
$mc->addServer("localhost", 11211); 

$mc->set("Nome", "Necessario para uma nova tentativa de ação!"); 
$mc->set("Quem", "Alguem..."); 
$mc->set("foo", "Hello!"); 
$mc->set("bar", "Memcached..."); 

$arr = array( 
    $mc->get("Nome"), 
    $mc->get("Quem") ,
    $mc->get("foo"), 
    $mc->get("bar") 
); 
var_dump($arr);

Rode no browser: http://localhost/memcached.php

Passo 5: Rode um exemplo com Zend_Cache

Exemplo com o Zend_Cache. Instanciando a classe de configuração do Zend_Cache no Bootstrap do Zend Framework e criando a classe que configura a instância do Zend_Cache da sua aplicação.

A classe a seguir, BootstrapCache, configura e instancia a classe Zend_Cache, responsável por inserir, recuperar e apagar dados do servidor de cache. A classe BootstrapCache deve ser chamada de forma estática no bootstrap (/application/Bootstrap.php) do Zend Framework, lembrando que este artigo cobre a versão 1.12 do ZF. Observe:

Arquivo “/application/Bootstrap.php”

    /**
     * Inicializa camada de cache: filesystem ou memcached
     */
    protected function _initCache()
    {
        //pattern Command
        BootstrapCache::execute();
    }

A classe BootstrapCache é responsavel pela configuração do cache da aplicação.

Arquivo “/lib/BootstrapCache.php”

class BootstrapCache {
    /**
     * host para instancia do server Memcached
     * 
     * @var string
     */
    protected $hostMemcache;

    /**
     * configuracoes para cache Conteudo site
     */
    protected $tipoCacheConteudo; //Libmemcached, File, None

    protected $lifetimeConteudo;  

    protected $pathCacheConteudo;

    public function __construct() {}

    /**
     * desgin pattern Command
     */
    public static function execute()
    {
        //instancia cache: 
        $camadaCache = new BootstrapCache();

        //recupera configuracao do Cache no config.ini
        $camadaCache->setHostMemcache(Zend_Registry::get('config')->cache->host->memcached);

        $camadaCache->setTipoCacheConteudo(Zend_Registry::get('config')->cache->Conteudo->tipo);
        $camadaCache->setLifetimeConteudo(Zend_Registry::get('config')->cache->Conteudo->lifetime);
        $camadaCache->setPathCacheConteudo(Zend_Registry::get('config')->cache->Conteudo->pathcache);

        $camadaCache->cacheConteudo();        
    }

    /**
     * @return array
     */
    private function configApp()
    {        
        $hostMemcache = $this->getHostMemcache();

        //dir do filesystem
        $path_cache = $this->getPathCacheConteudo();

        switch ($this->getTipoCacheConteudo())
        {
            case 'File':
                  $appfrontendConteudo = array(
                                'lifetime' => $this->getLifetimeConteudo(), //seconds 
                                'automatic_serialization' => true,
                                //'debug_header' => true,
                                );
                  $appbackendConteudo = array( 
                                'cache_dir' => $path_cache

                                );
                  break;
            case 'Libmemcached':  
            case 'Memcached':
                  $appfrontendConteudo = array(
                                  'lifetime' => $this->getLifetimeConteudo(), //seconds 
                                  'automatic_serialization' => true,
                                  'caching' => true,
                                  );
                  $appbackendConteudo = array(
                                 'servers' => array(
                                                array(
                                                'host'   => $hostMemcache,
                                                'port'   => 11211,
                                                'weight' => 1
                                                )
                                             ),
                                 'compression' => false
                                 );                
                  break;

        } //end switch

        return array ( 'Conteudo' => array(
                            'frontend' => $appfrontendConteudo,
                            'backend' => $appbackendConteudo
                            ),
                      );
    }

    /**
     * faz cache de conteudo site
     */
    public function cacheConteudo()
    {
        //seta configuracoes para o cache
        $arrConfig = $this->configApp();

        $appfrontend = $arrConfig['Conteudo']['frontend'];

        $appbackend = $arrConfig['Conteudo']['backend'];

        $appcache = Zend_Cache::factory('Core', 
                                        $this->getTipoCacheConteudo(), 
                                        $appfrontend, 
                                        $appbackend
                                        );
        // adiciona cache no Registry
        Zend_Registry::set('cache_Conteudo', $appcache);       
    }    

    //codigo omitido
    //getters e setters
}

Passo 6: Classe ConteudoCache

Para gravar e recuperar o conteúdo do Memcached utilize o exemplo da classe a seguir:

Arquivo “/lib/ConteudoCache.php”

/**
 * Metodos que instanciam Zend_Cache para fazer cache que areas do site
 *
 * @author esilva
 */
class ConteudoCache {

    /**
     * @var Zend_Cache_Core
     */
    protected $cache;

    public function __construct() 
    {
        //instancia Zend_Cache_Core        
        $this->cache = Zend_Registry::get('cache_Conteudo');
    }

    /**
     * faz cache de areas de conteudo do site
     * 
     * @param DbTable_ConteudoRow $dbtableConteudoRow
     * @return array
     */
    public function recuperaOuGravaConteudoNoCache(DbTable_ConteudoRow $dbtableConteudoRow, $idConteudo)
    {        
        $nameCache = "conteudo_".$idConteudo;
        $blocoCacheModel = $this->cache->load($nameCache);

        $msgCache = "pegou do cache";

        if (!$blocoCacheModel) 
        {
            $blocoCacheModel = $dbtableQuestionnaireRow->getAll($idConteudo);
            $this->cache->save($blocoCacheModel, $nameCache);
            $msgCache = "NÂO pegou do cache";
        }

        return $blocoCacheModel;
    }

}

O metodo “recuperaOuGravaConteudoNoCache” da classe “ConteudoCache” recebe como parâmetro uma instância DbTable, que é basicamente um fetchall de uma tabela do banco de dados. O parâmetro $idConteudo é o id único que identifica o conteúdo que está sendo cacheado ou se deseja recuperar do cache.

Informações de configuração, como o host do Memcached, tipo de cache (Libmemcached ou File), tempo de vida do cache e caminho para o filesystem, são armazenados no arquivo “config.ini” da sua aplicação Zend Framework. Se vocês observaram bem, o código também estaápreparado para cachear no filesystem da aplicação, sendo necessário apenas passar a string “FILE” na configuração do parâmetro “cache.conteudo.tipo”, no arquivo “config.ini”.

Passo 7: Arquivo config.ini

Os dados de configuração do cache estão no arquivo “config.ini” (/application/configs/config.ini):

Arquivo “/application/configs/config.ini”

cache.host.memcached = "localhost";
cache.conteudo.tipo = "Libmemcached"; //File //Libmemcached //None
cache.conteudo.lifetime = 900; //15min
cache.conteudo.pathcache = APPLICATION_PATH_CACHE "/cacheConteudo";

Repare que o método estático “execute” da classe BootstrapCache recupera os dados do arquivo “config.ini”, utilizando o Zend_Registry.

Passo 8: Classe Model_ConteudoQuestoes

Na classe model de conteúdo será necessário instanciar a classe ConteudoCache e informar o Id do conteúdo que deve ser recuperado.

Arquivo “/application/model/ConteudoQuestoes.php”

class Model_ConteudoQuestoes 
{
//
// codigo omitido
//
    public function cacheOrModelConteudoId($conteudoId)
    {      
        $conteudoCache = new ConteudoCache();

        $resultado = $conteudoCache->recuperaOuGravaConteudoNoCache($this, $conteudoId)

        return $resultado;
    } 

    public function getAll($idConteudo)
    {
	// codigo omitido
        // $fetchAll recupera dados de conteudo do banco de dados

        //retorno com conteudo do banco dados
        return $fetchAll;
     }

}

O parâmetro $this informado no metodo “recuperaOuGravaConteudoNoCache” refere-se a própria classe model onde se encontra o método “cacheOrModelConteudoId”. Ele é ‘injetado’ na classe ConteudoCache.

Dentro da classe ConteudoCache, no metodo “recuperaOuGravaConteudoNoCache”, é feita a chamada a “$dbtableQuestionnaireRow->getAll();” caso o conteúdo não seja encontrado no servidor de cache. Essa chamada é o mesmo que $this->getAll() na classe Model_ConteudoQuestoes.

Na action do controller que recebe o array (fetchall) de conteúdo do banco de dados faça uma chamada ao método “cacheOrModelConteudoId” informando o Id de conteúdo que será recuperado e enviado para a view.

Por exemplo:

$conteudoId = 101; //Informacoes das questões para o cliente
$this->view->conteudoParaCliente = $this->ModelConteudo->cacheOrModelConteudoId($conteudoId);

Esta chamada na action retorna o conteúdo de questões para a view, porém, este conteúdo poderá ser do banco de dados ou do Memcached. O método “cacheOrModelConteudoId” é responsável apenas por devolver o conteúdo para a action, que então disponibiliza o conteúdo para a view.

Conclusão

Este é um passo a passo para usuários com experiência em Linux e Zend Framework. O objetivo foi informar os passos necessários e o que é preciso para se chegar ao objetivo final, que é obter um servidor de cache Memcached configurado e recebendo requisições de uma aplicação web desenvolvida em Zend Framework.

Referências