caching - ¿Cómo estructurar una aplicación Symfony2 con ESI?
varnish (1)
En un nuevo proyecto con mucho tráfico, estamos pensando en cómo estructurar nuestra aplicación Symfony2 para aprovechar los cachés y estar preparados para ser más agresivos en el futuro. Me encantaría saber tu opinión.
Digamos que un usuario solicita una página una lista de lugares. Esta página tiene:
- list
- common data (title, author, description)
- user data (the user likes the list + other data)
- first 20 places
- common data (title, photo of each place)
- user data (the rates of the user for those places)
El HTML podría ser como:
<html>...
<body>
<header>
...
<!-- Embed the top user menu -->
<esi:include src="http://example.com/profile/menu" />
...
</header>
<content>
...
common data of the list
...
<!-- Embed the common data of the first 20 places, the same for everyone -->
<esi:include src="http://example.com/lists/17/places" />
...
<!-- Embed the user data of the list (used in JS) -->
<esi:include src="http://example.com/lists/17/user" />
...
<!-- Embed the user data of the list of places (used in JS) -->
<esi:include src="http://example.com/lists/17/places/user" />
...
</content>
</body>
</html>
El HTML se almacenará en caché en la puerta de enlace (Symfony o Varnish). La lista de lugares se almacenará en caché la mayor parte del tiempo también en la puerta de enlace. Las solicitudes de datos del usuario serán las que se llamarán y no se almacenarán en caché (al menos no inicialmente).
Preguntas :
- ¿Cómo te sientes acerca de esta estructura?
- Si el usuario es anónimo, ¿puedo evitar realizar esi-includes para los datos del usuario? También si tengo una cookie para el usuario anon? ¿Cómo?
- ¿Tiene sentido esi-include para el menú de usuario?
- ¿O deberíamos olvidarnos de ESI y pasar siempre por el controlador (por ejemplo, almacenar en caché la vista renderizada de los datos comunes)?
- ¿Deberíamos mover las 2 solicitudes de ESI que solicitan que los datos del usuario sean llamadas AJAX, en lugar de esperar en el servidor?
- ¿Es este un buen enfoque a escala si necesitamos hacerlo rápido? ¿Qué sería lo mejor?
¡muchas gracias!
Hemos usado Varnish en un sitio para el almacenamiento en caché de toda la página y he estado usando Symfony2 durante algunos años, pero ten en cuenta que no he usado Varnish + Symfony2 + ESI en ningún entorno de producción.
Creo que la idea básica está bien. Si el menú es el mismo en muchas páginas y la lista de lugares también es la misma en muchas páginas, obtienes el contenido común almacenado en caché por Varnish o Symfony. Como Varnish generalmente guarda el caché en la memoria, obtiene su contenido más rápido y no tiene que llamar a la representación y al código de consulta de base de datos en cada solicitud.
La parte difícil es hacer que esas solicitudes de ESI se almacenen en caché si el usuario ha iniciado sesión. Como sé, en la configuración predeterminada de Varnish, las solicitudes con Cookie en ellas nunca se almacenan en caché. Si tiende a pasar cookies a las solicitudes de ESI, esas respuestas de ESI no se compartirán entre los usuarios.
Puedes intentar hacer algunas reglas a partir de la URL, pero si utilizas los ayudantes de ramita de Symfony predeterminados, las URL generadas son / _internal / ..., por lo que puede ser difícil diferenciar las públicas y las privadas.
También se puede configurar para que siempre ignore las cookies si se pasa
Cache-Control: public
. Esto se hace por defecto en Symfony:if ($this->isPrivateRequest($request) && !$response->headers->hasCacheControlDirective(''public'')) { $response->setPrivate(true); }
Como se ve en el código, si tiene
public
directivapublic
, la respuesta nunca será privada.No he encontrado cómo Varnish procesa esta directiva; como entiendo, no almacena en caché ninguna solicitud que tenga cookies de forma predeterminada. Así que creo que hay que modificar la configuración para lograr esto.
Si la página principal también se va a almacenar en caché, no veo cómo podría omitir las inclusiones.
Supongo que JS es obligatorio para sus usuarios registrados (no para los robots de búsqueda), por lo que sugeriría usar Javascript para diferenciar la carga de datos de usuarios.
El código de Javascript puede ver si el usuario tiene una cookie
session-id
etc., y solicita solicitar los datos solo en este caso. También podría ser una buena idea configurar alguna otra cookie, como_loggedin
para evitar que el código Javascript obtenga el ID de sesión.Los usuarios no registrados también pueden tener algunos datos en las cookies, como
_likedPost:1,2,132
. Javascript puede obtener esta cookie y hacer algunas correcciones de HTML sin siquiera realizar la solicitud adicional.Como hicimos con estas cookies: separamos las cookies solo de JS de las cookies de la aplicación. Lo hicimos por algún patrón, como
_/w
para las cookies de JS. Luego ajustamos la configuración de Varnish para dividir el encabezado de la cookie y eliminar estas cookies solo para JS. Luego, si no queda ninguna otra cookie, la respuesta se comparte con todos. La aplicación (Symfony) no obtiene esas cookies, ya que son eliminadas.Creo que lo hace si es lo mismo en todas las páginas.
Creo que ESI es bueno ya que Varnish puede mantener el caché en la memoria. Por lo tanto, es posible que ni siquiera haga ninguna consulta a su disco duro por el contenido. Como la memoria caché de tu controlador también puede estar en la memoria, creo que Varnish buscaría la memoria caché más rápido que el framework Symfony con todo el enrutamiento, el código PHP, la inicialización de servicios, etc.
Depende, pero creo que podría ser mejor enfoque. Tenga en cuenta, que los cachés viven vidas diferentes. Por ejemplo, si su lista de lugares se almacena en la memoria caché durante 2 horas, al final de este tiempo los lugares pueden haber cambiado: algunos elementos nuevos son nuevos en la lista y faltan algunos de ellos. Su lista para el usuario sigue siendo la antigua (almacenada en caché), pero usted proporciona los datos del usuario acerca de la nueva lista: algunos de los datos no son necesarios, algunos faltan.
Podría ser un mejor enfoque obtener lugares cargados por javascript, por ejemplo, buscar algún atributo HTML como
data-list-item-id
y luego hacer que ajax solicite información sobre estos artículos. En este caso, sus datos de usuario se sincronizarán con la lista actual en caché y puede realizar 1 solicitud ajax para ambas listas en lugar de 2.Si no se utiliza la invalidación de la memoria caché (solicitudes PURGE), todo el esquema de la memoria caché HTTP es realmente bueno para escalar. Puede escalar la aplicación a varios servidores y configurar Varnish para llamarlos al azar, por alguna regla o simplemente para usar uno de ellos como seguro contra fallas. Si el ancho de banda sigue siendo demasiado grande, siempre puede modificar los tiempos de espera de la caché y otras configuraciones.