Caché de contenidos en PHP 4

Tratando de buscar un método para el PostRev que me permitiese bajar el uso de procesador en el server pensé en implementar un sistema de caché como tiene el Wordpress. Pero, cabezadura como soy, no podía copiarlo directamente, primero hay que entenderlo, luego, hacerlo uno mismo :D

Así que me puse a pensar, buscar funciones y encontré como hacerlo. Todavía no está implementado en este sitio, pero lo estoy armando como plugin para el PostRev 0.7, así que mientras tanto lo voy a instalar antes de publicarlo. Les cuento como hacer uno.

Aclaración: no hagan preguntas de otros temas aquí, para eso hay otros posts sobre PHP, este sólo tratará de cacheo.



La idea

Bueno, primero el concepto, lo que siempre hay que definir antes de hacer algo, qué, porqué y cómo.

Necesitamos guardar el contenido HTML de una página del weblog que seguramente se cargará muchas veces y que requiere muchas consultas de SQL a la base de datos. Si cada vez que entra un visitante tenemos que rearmar cada página, hacer las consultas de la nota, los comentarios, combinarlo con cada cosita que aparece en pantalla, etc. se transforma en mucho tráfico interno, entre el PHP y MySQL, y por ende mucho uso de disco y procesador.

En un sitio web con poco tráfico esto no es preocupante, no significa nada, apenas unas conexiones a la BD y listo, pero en un sitio como este, que hay más de 6500 visitantes diarios, empieza a ser un problema, varios habrán visto errores de MySQL por "too many connections" y es eso lo que tengo que evitar, tantas conexiones en un mismo momento pueden impedir que se muestre la página en un momento dado.

A la vez existe cada día más el riesgo de un ataque de DoS, o mejor, un efecto "slashdot" que se puede dar por un foro, digg.com, meneame.net, barrapunto.com, etc. Muchas visitas, si, gracias, pero muchísimo tráfico para ver exactamente el mismo contenido.

Entonces ya tenemos el problema, la solución estaría en mantener guardado en un archivo HTML el contenido que vuelve a repetirse una y otra vez.

Si para renderizar una página tenemos unas 10 consultas a la base de datos, para cargar un archivo cacheado tendríamos una sola, aumentaría la capacidad de recibir visitas 10 veces, no es nada despreciable, ¿no?

La cuestión está en guardar el contenido mientras no existan cambios y actualizarlo apenas se realiza un cambio. Sólo en ese caso, por ejemplo, cuando alguien deja un comentario, se actualiza el archivo cacheado, no sé cuanto será la efectividad, pero no tengo dudas que aumenta y mucho.

Funciones

Lo primero que pensé fue en ¿donde cuernos se guarda todo antes de enviarlo al cliente? hay un buffer, sin dudas, pero ¿como manejar dicho contenido?, gracias a PHP podemos controlar el envío de los datos al visitante, primero tenemos que avisarle al motor del PHP que no envíe hasta que se lo digamos:

ob_start();

Esta función, la pueden consultar en el manual de PHP, habilita el uso de búferes de salida, lo que nos da, de ahora en más, el control de lo que enviemos al usuario.

Para finalizar y enviarle al usuario el contenido HTML simplemente usamos:

ob_end_flush();

Esta función termina de enviar el contenido del buffer. Esto nos permite hacer lo que querramos entre medio de estas dos funciones, todo lo que se "imprima", sean "print" o "echo" quedará almacenado en el buffer hasta que demos la orden de enviarlo.

¿Donde queda el contenido antes de enviarlo? se lo puede obtener de la siguiente manera:

$contenido = ob_get_contents();

Cacheando

Para guardar el contenido hay que crear una carpeta con los permisos suficientes donde alojemos cada archivo .html, además tenemos que guardar de alguna forma una constancia de qué archivos fueron cacheados y cuando, esto servirá para poder comparar entre el contenido nuevo, si es que existe, y lo último guardado.

Una vez consultada esta tabla, si existe cacheo, simplemente hay que compararlo, por ejemplo, con los comentarios que se escribieron. ¿Se escribió alguno nuevo durante el lapso de tiempo de lo último cacheado y ahora? si no hay ninguno, simplemente se carga el archivo cacheado.

Si hubo cambios se borra el archivo anterior, si existe, y se graba uno nuevo.

Aprovechando que cada nota tiene un ID, que es un numero entero y único, guardo los archivos con el número_de_nota.html, así de simple.

Las funciones para grabar archivo no las voy a explicar aquí, lean los manuales de PHP para "fopen" y listo

Conclusión

Es recomendable crear un módulo de administración para poder "limpiar" el caché cada vez que el administrador lo desee, de esta forma si hubo algún error podrá reactivar todo sin problemas. También se puede escribir más código para indicar qué post tiene caché y cual no, no se si sirve de mucho, pero para sitios donde sólo algunos posts tienen muchas consultas, por un efecto slashdot por ejemplo, puede servir. Sólo cachear lo que se necesita.

Personalmente creo que lo utilizaré para todos los posts, el único que se actualizará más seguido es el index.php ya que en el mismo no sólo cambian las noticias y el número de comentarios de cada una, además cambia el listado de últimos comentarios y la gente lo revisa mucho y seguido, no puedo dejar cacheado eso y que se comenten notas viejas y que no aparezca el dato en portada. Así que también hay que revisar eso. Si uno quiere un caché muy eficiente o muy generalizado dependerá de las necesidades de cada uno.

Todo esto lo publicaré como un plugin para la versión 0.7 del Post Revolution, por el momento va sólo esta nota introductoria, se aceptan sugerencias de diseño y programación, si conocen fallas de estas funciones avisen, todavía no pude testearlas en "productivo", sólo en "desarrollo".

Categoría: Programación Etiquetas:  
Otros posts que podrían llegar a gustarte...

Comentarios

  • LocoMaxi    

    Yo recuerdo haber instalado uno de esos plugins de cacheo para alivianar un cacho las cosas, pero como tenía contenido dinámico en la página no me servía mucho .
    A la larga se me murió la máquina

    • Responder
    • Citar
    • Comentado:
  • edwin    

    Gracias por el articulo, hace rato que deberia revisar mis manuales de php4 y php5 solo que nunca tengo tiengop tiempo, por eso me viene al pelo Guiño

    • Responder
    • Citar
    • Comentado:
  • Fabio    

    Edwin, más adelante publico el plugin para PR0.7 , asì que podrás usarlo en tus sitios si te interesa Guiño

    LocoMaxi, por eso tuve que hacerlo de esta forma, porque la generación de contenidos del PR es dinámica, no mando todo a una variable y después la imprimo como hacen la mayoría de los sistemas de templates, el mío simplemente va imprimiendo, es cómodo pero si querés cachear necesitás estas funciones.

    • Responder
    • Citar
    • Comentado:
  • Edwin    

    gracias Fabio, espero ansioso diciembre asi largo un poco y me dedico a estudiar. Porque en este campo rara vez las cosas son estaticas.

    • Responder
    • Citar
    • Comentado:
  • chalin    

    che pero q onda? como detectas que se hubo un cambio? y como redireccionarias la posta con lo cacheado? (o sea... por ahora los links traen un php que hace un query a la db; pero con el cacheo quien dice que hay que ir al archivo cacheado ?) Igual con agregar un par de variables se arregla todo... pero la mas grande de mis dudas es como hacer que se de cuenta que tiene que hace cache..., y como comparar el cache con la posta q hay en la db; se me vienen soluciones a la cabeza pero son medios ridiculos) igualmente no conozco nadade php asi q capas mis dudas se contestarian aprendiendo php (cosa que pienso hacer en breve cuando termine de instalar mi gentoo)

    • Responder
    • Citar
    • Comentado:
  • Fabio    

    bueno, para darte cuenta de si hubo un cambio lo que hago, primero, es guardar la fecha del último cacheo, si para esa nota en particular hubo algún comentario nuevo desde el último cacheo, hay qeu renovar el archivo, caso contrario, se carga el HTML.

    el primer cacheo de todos se hace si no hay nada cacheado, por eso uso una sola tabla, ¿està ahí? si no está, creo el caché y lo guardo en archivo, si está pregunto si hay algún comment nuevo desde que se grabó ese archivo ¿lo hay? borro y cacheo de nuevo ¿no lo hay? no hago todo el proceso de consulta y creación de la página, simplemente hago un include del archivo HTML guardado, evito unas cuantas consultas y tráfico

    • Responder
    • Citar
    • Comentado:
  • myNick    

    Primero existieron las páginas estáticas y se dieron cuenta que estaba bien y que servían. Pero luego se dieron cuenta que las páginas dinámicas eran mucho mejor y que servían, pero luego se dieron cuenta que era todo muy bonito, pero que los servidores se sobrecargaban. Entonces volvieron a las páginas estáticas (o casi).

    ¿Y luego? Estemos un paso adelante y pongamos fichas a las páginas dinámicas que se comen el servidor... :P

    • Responder
    • Citar
    • Comentado:
  • Fabio    

    y si, es algo así, pero tiene que ver, principalmente, con que antes no había interacción con los visitantes y ahora sí, antes no tenías el efecto slashdot y cosas semejantes. Antes te revendían el server tanto como ahora :D solo qeu era todo estático y no se notaba!!

    igualmente los más consumidores son los sistemas de foros, hacen dos millones de consultas al pedo para mostrar estadísticas innecesarias, además que todavía no hay un paso a MySQL 5 serio en los proveedores, etc.

    pero si, es como una vuelta atrás, aunque este cacheo dinámico lo que busca es obtener el mejor rendimiento de ambos enfoques.

    • Responder
    • Citar
    • Comentado:
  • Nombre    

    ¿De verdad merece la pena tanto lio? No se, pero creo que serializando los objetos adecuados se simplifica enormemente los consumos de consultas sql.

    • Responder
    • Citar
    • Comentado:
  • Fabio    

    aja, serializando los objetos, podrías explicarte para que la gente entienda, no?
    además... estamos hablando de PHP 4... no 5... los objetos no persisten, no podés hacerlo de otra forma ya que cada visita es una nueva instancia...

    • Responder
    • Citar
    • Comentado:
  • Nombre    

    Juraría que había contestado ya pero parece que no. Bueno mira como tiene implementada la cache drupal. Es uno de los mejores sistemas cache que hay. Y es php4.

    • Responder
    • Citar
    • Comentado:
  • Ignacio    

    no seas cabeza dura, usa smarty!!!!

    • Responder
    • Citar
    • Comentado:
  • Fabio    

    ¿para que? para no aprender nada? lo hago de hobby, no para vender un producto :| meterle smarty a esto lo haría tan aburrido que no me darían ganas de actualizarlo para eso me paso a Wordpress

    "Nombre" y alguna idea de como lo implementa Drupal?

    • Responder
    • Citar
    • Comentado:
  • ignacio    

    prefiero colaborar con smarty.

    • Responder
    • Citar
    • Comentado:
  • Enmanuel    

    Hola amigos, me gusto tanto el tema que les puedo dar un tip con respecto a las consultas que se hacen al server para paginas dinamicas, mysql tiene a capacidad tambien de cachear las consultas, para no volverlas a ejecutar y simplemente llamarlas cuando estas ya se hallan producido, lo unico que tienen que hacer es setear en el manejador la cache de las querys, un ejemplo de seteo seria asi:
    set global query_cache_size = 128*1024*1024;

    Espero les sirva de algo como me ha servido a mi el articulo y sus comentarios. Saludos

    • Responder
    • Citar
    • Comentado:
  • Carlos    

    Andaba necesitado de esto justo, gracias.

    • Responder
    • Citar
    • Comentado:
  • Gracias, me has dado una gran idea, trabajo con Zend Framework, que renderiza html usando el método render de la clase view, retorna un string, y haces echo para mostrar la pagina. Es muy cómodo cachear este String. Siempre pensé en cachear los resultados en un objeto cargado en memoria como hago para Java y c# pero para PHP es imposible

    • Responder
    • Citar
    • Comentado:

Deje su comentario:

Tranquilo, su email nunca será revelado.
La gente de bien tiene URL, no se olvide del http/https
Para evitar bots, si se tardó mucho en leer la nota seguramente no sirva y tenga que intentar dos veces

Negrita Cursiva Imagen Enlace


comentarios ofensivos o que no hagan al enriquecimiento del post serán borrados/editados por el administrador