Cache de páginas PHP - Melhorando a performance

relogio

Uma das grandes vedetes dos portais de conteúdo da Internet hoje em dia é a rapidez na atualização de conteúdos. Para isso, são criados os mais diversos sistemas de publicação e / ou gerenciamento de conteúdo (explicaremos a diferença entre esses termos em outra oportunidade), que armazenam todo o portal em banco de dados, dando a opção para o usuário que não conhece nada de marcações HTML ou de estilos, cadastrar seu conteúdo formatado de forma rápida, sem depender de “técnicos” para a tarefa.

O grande “porém” é que, em portais muito acessados, o tempo de resposta de um banco de dados a uma requisição pode não ser satisfatório, mesmo em casos de queries simples, como a montagem de um menu dinâmico, mas que são executadas simultaneamente por diversos (até milhares) de usuários. O efeito em cascata então é sentido, fazendo com que todas as outras requisições se tornem lentas.

Páginas dinâmicas, em um portal que utiliza um sistema de publicação / gerenciamento de conteúdo, podem conter vários elementos pequenos onde são realizadas diversas queries para a montagem do seu conteúdo. Essas queries, por menores e simples que sejam, custam tempo de processamento do servidor de banco de dados e tempo de espera pelo servidor de aplicação pela resposta do BD. Tudo isso somado a um grande número de usuários simultâneos, em uma arquitetura de rede que não esteja devidamente preparada, pode fazer seu portal vir abaixo.

Uma ação a ser realizada para tentar dizimar este problema é o cache das páginas geradas. No caso, busquei por uma solução (santo Google) e encontrei essa classe do companheiro Antônio Campos, de Portugal, onde partimos de um princípio simples, mas que eliminaria boa parte da concorrência nas requisições ao servidor de Banco de Dados, fazendo assim a performance do sistema aumentar consideravelmente.

A classe consiste em se pegar o buffer de saída do php antes da maneira que ele envia para o browser e armazená-lo em um arquivo html, em um diretório de cache específico. Na próxima requisição à esta página, antes começar o processamento da mesma, o sistema verifica se esta página já existe no cache ou se a mesma não é antiga (o tempo do cache é configurável). Caso atenda às condições, ele envia para o browser o arquivo html armazenado e não processa novamente a página, economizando assim um tempo absurdo de processamento e resposta de servidor.

A classe ainda possui uma função para limpar o cache, mas ainda meio “rudimentar”, uma vez que limpa todo o diretório de cache, o que não é interessante em grandes portais. Para a próxima versão (que pretendo postar aqui) vou implementar a funcionalidade somente limpar partes determinadas do cache, por idade ou por nome do arquivo.

Segue o código-fonte da classe, contendo o original escrito pelo Antônio e as modificações realizadas por mim.

<?php
    
/*
    Criado por:     Antonio Campos
    Modificada por: Diogo Moreira
    CHANGELOG
    04-01-2008 -> Criação
    20-02-2008 -> adicionado o parametro do caminho do arquivo de cache para o construtor, para prever arquivos em niveis diferentes
               -> adicionado o tempo do cache em segundos
               -> adicionada função para limpar o buffer de saída, para não guardar lixo no arquivo de cache
    -> Segundo release!
    */
    
class Cache{
    
    function 
Cache($diretorio_cache "./cache/"){
        
        
//Diretório onde queremos guardar os arquivos de cache
        
$this->directorio_cache $diretorio_cache//Agora vamos começar a contruir uma string que irá conter o nome do script que vamos fazer cache
        
        
$this->pagina $_SERVER['SCRIPT_NAME']."?";
        
//se existir uma query string vamos concatenar a mesma com o nome do arquivo
        
        
if (isset($_SERVER['QUERY_STRING'])){
        
            
$this->pagina=$this->pagina.$_SERVER['QUERY_STRING'];
        
        }
        
    }
    
    function 
Inicio($tpo_cache 600){ //equivalente a 10 minutos
        
        //Gerar o nome do arquivo de cache correspondente à  página que estamos
        
        //O MD5 é só para evitar que o nome contenha careteres inválidos por exemplo ? e & comuns nas query strings
        
        //coloquei a extensão html podem colocar o que quiserem
        
        //limpa o buffer de saída antes de começar a execução
        
        
ob_clean();
        
        
$this->ficheiro_cache $this->directorio_cache md5($this->pagina) . '.html';
        
        
//Verificar se o ficheiro já existe na cache
        
        
if (file_exists($this->ficheiro_cache)){
            
            if((
time() - $tpo_cache) < filemtime($this->ficheiro_cache)){
        
                
//se já existir e não for antigo geramos o output deste arquivo
                
                
readfile($this->ficheiro_cache);
                
                
//como já fizemos output não queremos que mais nada seja executado!
                
                
exit();
            
            }
            
            
unlink($this->ficheiro_cache);
        
        }
        
        
//se chegar aqui é porque o arquivo não existia em cache e temos que o criar
        
        //esta função cria um buffer onde é colocado o outup que é igualmente enviado ao cliente
        
        
ob_start();
    
    }
    
    function 
Fim(){
    
        
// Como não exise o arquivo no cache vamos cria-lo e abrir em modo w (w de write)
        
        
$fp fopen($this->ficheiro_cache'w');
        
        
//Escrevemos o conteudo do buffer no novo ficheiro de cache
        
        
@fwrite($fpob_get_contents());
        
        
//escreve a data e hora do cache, apenas para controle
        
        
$hora date('h:i:s, j-m-Y');
        
        @
fwrite($fp"<!– Cache - $this->pagina - $hora â€“>");
        
        
//fechamos o file pointer
        
        
@fclose($fp);
        
        
//e fechamos tambem o buffer criado
        
        
ob_end_flush();
    
    }
    
    function 
Limpar(){
        
        
//ATENÇÃO: Este método apaga TODO o cache. Por isso, seu diretório onde ficam guardados os arquivos de
        //cache não deve conter nenhum outro tipo de dado. Esta função será aprimorada para apagar somente o
        //cache de determinada página
        //criamos um apontador para o diretorio onde esta guardado o cache
        
        
if ($handle $this->directorio_cache){
        
            
//um loop que percorre todos os ficheiros
        
            
while (false !== ($file readdir($handle))){
        
            
//evitar um erro!!
        
                
if ($file != '.' and $file != '..'){
            
                    
//somente uma informação
            
                    
echo 'Apagado > '.$file '<br>';
            
                    
//apagar cada um dos arquivos
            
                    
unlink($this->directorio_cache '/' $file);
                }
                
            }
            
//fechamos o apontador
            
            
closedir($handle);
            
            }
        }
    }
?>

Segue um exemplo de utilização da classe:

 <?php
//incluir o ficheiro onde temos a classe
include_once "cache.php";
$cache = new Cache();
//Iniciar o processo de cache
$cache->Inicio();
//Se o arquivo ja existir em cache nada mais sera executado abaixo desta linha
?>
<html>
    <head>
    <title>Exemplo de Cache em Php</title>
    </head>
    <body>
        <h1>Conteudo da pagina</h1>
    </body>
</html>
<?php
//Se chegou aqui é porque não existia em cache, por isso vamos chamar a função que escreve o arquivo no cache!
$cache->Fim();
?> 

Links úteis: Blog do Antônio Campos (autor original da classe): http://antoniocampos.no-ip.com/

Novidades, novidades…

p5120040.JPG

Post pessoal, para quebra o clima demasiado “sério” do blog. Não que não seja um blog sério. Mas é também um blog pessoal, como diz o título do mesmo.

Meu casamento, marcado para o ano de 2008, como diz na página “Sobre mim” está de fato marcado: dia 27/12/2008! Isso mesmo! Esse é o dia, para aqueles a quem possa interessar. Os preparativos estão caminhando e estou cada vez mais ansioso. Meu Anjo também está, pois os preparativos dela também são custosos hehehe.

Nossa futura casa está quase pronta (não vemos a hora de entrar ali… êta tempo que não passa). Cada dia que passa a despedida no domingo é mais difícil. Dá vontade de não deixar ela ir… de não ir… mas temos que ser pacientes… Vamos levando, na medida do possível e fazendo de tudo para o tempo passar mais rápido quando estamos longe e aproveitar ao máximo quando estamos juntos.

Eu amo demais minha futura esposa. Não canso de dizer isso. E digo mais…é bom demais amar assim!!!

;-)

TCO do MySQL em relação aos bancos mais utilizados do mercado

TCO MysQL

Gráfico de comparação entre TCO MySQL e outros BD’s do mercado (Fonte: www.mysql.com)

O MySQL é o banco de dados mais utilizado na WEB e vem ganhando terreno também em outros nichos pouco explorados anteriormente, tais como aplicações de missão crítica, financeiras e afins. Tudo graças à política mais agressiva de suporte e licenciamento adotada pela MySQL AB, a empresa que controla o desenvolvimento do software. No site do MySQL existe um sistema para ajudar a calcular o TCO (Total Cost of Ownership, ou Custo Total de Propriedade), onde vemos uma diferença gritante entre o MySQL e os demais bancos mais utilizados no mercado (MS SQL Server e Oracle 10g são alguns exemplos). Vale a pena dar uma olhada. O link para a página com o aplicativo é:

http://www.mysql.com/why-mysql/tco.html

Alô Mundo!

Começamos novamente. Estou saindo de vez do blogspot e entrando de cabeça no Wordpress. Já que todo mundo fala que é bom, vamos usar, não custa experimentar.

Os posts do blog antigo serão migrados conforme tempo e disponibilidade deste que vos escreve. Tenham paciência.

Abraços!