Twitter

@felipernb: @raynerholmes Parabéns! 13 é um ótimo número! :D

(Updated 6 hours, 56 minutes ago)

Caching em PHP com uma técnica “obscura” mas muito eficiente

Posted: December 25th, 2008 | Author: Felipe Ribeiro | Filed under: apache, php, web 2.0 | Tags: , , | 14 Comments »

Caching é fundamental para a escalabilidade de aplicações Web. Existem diversas ferramentas que oferecem diferentes maneiras de se fazer isso, seja no Smarty, no APC, nos diversos frameworks, ou Memcached para os mais drásticos…

Mas essa é a “maneira Rasmus Lerdorf” de se fazer cache com PHP sem nenhuma ferramenta externa e com uma sacada fenomenal.

Para um servidor Web é muito mais rápido servir arquivos estáticos do que esperar que aquele arquivo seja interpretado por algum módulo ou que seja executado em CGI e é isso que esse método faz, gera arquivos estáticos sob demanda, utilizando os recursos que o Apache oferece.

Agora imagine que você tem um site de notícias e quer que elas sejam tratadas como arquivos estáticos, e os links para cada notícia seria algo como:

http://meusite.com.br/noticias/000001.html

1º passo: Setamos nas configurações do Apache para que a página de erro 404 seja um arquivo .php (isso pode ser feito no .htaccess da pasta ou nas configurações do Apache propriamente dito). No caso do .htaccess, basta colocar isso:


ErrorDocument 404 /noticias/gera_cache.php

2º passo: Criamos o arquivo gera_cache.php, que irá tratar as requisições que teriam como resposta o erro 404 (Not Found) com o seguinte codigo:


<?php
    $id = basename($_SERVER['REDIRECT_URL'], '.html');

    /* Acessa a página dinâmica */
    $html = file_get_contents(sprintf("http://meusite.com.br/noticias.php?id=%d",$id));
    /* O ideal é fazer algum tratamento de erros, para evitar a
     criação de arquivos para ids inválidos */

    /* Exibe o conteúdo */
    header(sprintf('%s 200', $_SERVER['SERVER_PROTOCOL']));
    echo $html;

    /* Salva o conteúdo em um arquivo .html */
    $fp = fopen(sprintf(dirname(__FILE__)."/%d.html", $id), "w");
    fputs($fp, $html);
    fclose($fp);
?>

Sendo assim, o que vai acontecer:

Quando o usuário acessar pela primeira vez o link http://meusite.com.br/noticias/000001.html, o arquivo /noticias/000001.html não existirá e o usuário será redirecionado para o gera_cache.php. O gera_cache.php acessa a página dinâmica que exibe o conteúdo da página com o id passado (000001) e salva em um html. Nos acessos consecutivos ao http://meusite.com.br/noticias/000001.html o arquivo existirá e não passará mais pelo PHP.

É uma técnica bem “tricky” e que precisa de cuidados, por exemplo, você precisa ter uma rotina que expira os arquivos em cache após algum tempo e quando houver alguma alteração em determinada informação que interfere na página que está em cache, para garantir consistência dos dados. Uma maneira muito simples de se fazer isso é usando a função filectime do PHP para checar a idade dos arquivos em uma rotina que rodaria em background e apagaria os que fossem mais velhos que o tempo desejado. Mas funciona muito bem!

P.S.: Um artigo que li dizia que apesar dessa técnica ter se tornado pública através do Rasmus, ela foi criada mesmo pelo Stig Bakken


14 Comments on “Caching em PHP com uma técnica “obscura” mas muito eficiente”

  1. 1 Silas Ribas said at 5:48 pm on December 25th, 2008:

    Salve,

    Muito boa essa técnica, é quase um url rewrite com cache.

    O cara passa a url site.com.br/produto-00001.html, ai cai no 404 que gera o arquivo nesse formato do caching.

    Abraços.

  2. 2 aissegoo said at 10:09 am on January 2nd, 2009:

    Ola Felipe!
    É uma técnica bem interessante em seu conceito, mas pode gerar MUITA dor de cabeça da forma que foi feita. Alguns pontos:

    1 – Todo retorno de 404 irá gerar um html, mesmo que seja em branco, certo? Eu poderia fazer um script que ficaria acessando urls aleatorias no seu site, gerando assim milhares de arquivos.

    2 – Para SEO isso também complica as coisas. Primeiro que o Google não indexa qnd houver o 404, segundo que você pode gerar conteúdo pra qualquer url.

    3 – A manutenção disso seria um pouco complicada, apagar os arquivos depois de uma certa idade até pode ser válida, mas não seria a ideal.

    Irei tentar melhorar esse sistema e postar em meu blog, podemos gerar um debate para chegar em uma versão segura e eficiente dessa técnica. Que tal?

    []s e parabéns pelo blog.

  3. 3 Felipe Ribeiro said at 12:05 pm on January 2nd, 2009:

    Olá aissegoo!

    Vou responder teus questionamentos:

    1 – Sim, esse script que tá demonstrado aí é apenas conceitual, não incluí nele tratamento de erro, mas certamente é necessário ser feito no ambiente de produção.

    2 – Exato, o Google não indexaria caso ele fosse o primeiro a atingir aquela URL, caso não seja o primeiro, indexará normalmente. Caso ele tenha sido o primeiro, quando o bot chegar na tal url, o arquivo será gerado, aí na próxima passagem dele, provavelmente daqui a uma semana, aí sim indexará. Mas tudo isso pode ser evitado com a linha que adicionei agora de header.
    Não entendi o que você quis dizer com “segundo que você pode gerar conteúdo pra qualquer url.”

    3 – Mas é isso que o Smarty::is_cached() faz… ele checa se o arquivo existe e se a idade dele é menor que o Smarty::cache_lifetime, se for ele apaga para gerar de novo. A diferença é que no caso como o do Smarty as coisas são feitas sob demanda, e nesse caso pode-se dizer que seria um processamento “em lote”.

    Obrigado pelo comentário e sugiro esse artigo para leitura:
    http://phpadvent.org/2008/php-without-php-by-terry-chay

    Um grande abraço

  4. 4 aissegoo said at 6:24 pm on January 2nd, 2009:

    Ola Felipe!

    Ah sim, a idéia do script é uma boa, fazemos muito isso onde trabalho pois temos sites que tem muitos acessos e se for deixar tudo no banco de dados, haja servidores! Voltando a falar do sistema em questão, considerando alguns pontos novamente:

    1) Pode ser que somente o Google acesse uma certa url, aí ele recebe 404, o arquivo html é gerado, depois é apagado e novamente o Google volta depois de um tempo e ache o 404 novamente. A frequencia com que o crawler visita as urls não é exatamente definida.

    2) Uma das coisas que considero mais grave é que qualquer id digitada, ele gera um html, mesmo com conteúdo vazio. Alguem com má intenção poderia forçar o nosso site a ter qualquer tipo de conteúdo, pelo menos o arquivo e com o nome na url. Isso abre uma brecha para alguém atacar com SEO o site.

    3) Embora o tamanho do arquivo seja mínimo, ainda sim pode-se fazer um sistema que gere infinitos arquivos dentro do nosso site. Isso torna-se uma brecha de segurança.

    A parte de apagar os arquivos depois de uma certa idade eu viajei em dizer que seria ruim, seria mais apropriado dizer que existem outros meios para se atualizar o conteúdo. Mesmo porque uso dessa técnica em várias aplicações e ela funciona muito bem em vários casos.

    O conceito do sistema é muito boa, já vi algumas variações disso e funcionam muito bem. O usuário só deve se alertar que precisa usar isso com certos cuidados, aperfeiçoar bem a idéia para não ter dores de cabeça no futuro.

    Desculpe a redação hehehe, mas é importante trocar essas experiências.

    []s

  5. 5 Felipe Ribeiro said at 7:50 pm on January 2nd, 2009:

    Como eu te disse, essa linha de header resolve o problema do 404 para o Google, e o tratamento de erro pode ser feito para tratar ids inexistentes.

  6. 6 Lucas Fernando Amorim said at 11:27 pm on January 7th, 2009:

    Realmente, uma técnica muito obscura. Mas não entendi realmente o problema de se ter uma página 404 estática padrão, aonde os browsers que se possuem JavaScript alterem o conteúdo de determinado elemento para o nome da página procurada em questão, do contrário fica somente a página 404 convencional sem nome alterado. :D

    Ficaria algo como:

    ErrorDocument 404 /noticias/404_estatico.xhtml e o Apache geraria o log para estatísticas.

    Estou louco?

  7. 7 Felipe Ribeiro said at 11:35 pm on January 7th, 2009:

    Opa cara,

    Acho que você não entendeu bem a sacada. Inclusive é nesse esquema que o site php.net funciona.

    Vamos supor que você quer acessar a notícia de id 42, o link vai estar apontando para:
    http://meusite.com.br/noticias/42.html
    Quando você bater nesse endereço pela primeira vez, ele nao vai existir, então o que vai acontecer: O PHP vai ser chamado para gerar essa página. Ele vai carregar a pagina dinâmica de notícias com o id 42 e salvar como um arquivo estático html com o nome 42.html.

    Nos próximos hits a essa página, o arquivo 42.html vai existir e o PHP não precisará ser chamado, o que torna o sistema MUITO mais rápido.

    Não tem muito a ver com log e páginas de erro estáticas vs. dinâmicas não. É só uma maneira de fazer cache de maneira a aproveitar o que o ambiente proporciona. E se o ID que você quer não existir, você vai pegar no tratamento de erro e mandar a página de erro 404 mesmo.

    Abraço

  8. 8 Caching em PHP + Apache | DGmike said at 12:22 pm on January 14th, 2009:

    [...] Você pode conferí-la direto no site do cara: Caching em PHP com uma técnica “obscura” mas muito eficiente [...]

  9. 9 Everton said at 12:13 pm on January 15th, 2009:

    Felipe, muito bo o artigo. Testei mas $_SERVER['REDIRECT_URL'] nao funcionou e o meu PHP nao recupera esta variavel. Sera alguma configuracao do Apache?

  10. 10 Falcão said at 1:01 pm on February 11th, 2009:

    Acho que você deveria olhar o RewriteCond com a opção “!-f”, que vai fazer um redirecionamento quando o arquivo não existe. Usar o 404 para isso não é elegante nem correto.

  11. 11 Felipe Ribeiro said at 10:45 pm on February 11th, 2009:

    Valeu pelo comentário!

    Realmente é uma solução mais elegante. Mas não sei se podemos
    considerar o tratamento do 404 como “incorreto”, afinal o erro 404 é o
    código que também identifica a inexistência do arquivo. São duas
    maneiras de se dizer a mesma coisa, porém a sua é sim mais elegante.

    []’s

  12. 12 battisti said at 5:57 pm on March 26th, 2009:

    Idéia bem interessante essa idéia do erro 404!

  13. 13 Fabio said at 4:29 pm on August 26th, 2009:

    Felipe, uma idéia seria criar a pagina html no momento do insert no banco de dados, e criá-la novamente em caso de update.
    O que acha?

    []’s

  14. 14 Renato said at 4:36 pm on November 19th, 2009:

    Também penso como o Fabio, acho muito melhor criar a página estatica no momento do “INSERT” no banco de dados.


Leave a Reply