La configuration utilisée : Ubuntu 11.10, Varnish 3.0.0-4, Symfony 2.0
Installation
Ouvrir le terminal et taper :
sudo apt-get install varnish
Configuration de Varnish
Ouvrir et modifier le fichier de configuration de Varnish, par exemple :
sudo vi /etc/varnish/default.vcl
Surcharger les méthodes vcl_recv (réception des données), vcl_hash (construction de la clé unique qui correspondra à une entrée de cache donnée) et vcl_fetch :
sub vcl_recv {
set req.http.Surrogate-Capability = "abc=ESI/1.0";
if (req.restarts == 0) {
if (req.http.x-forwarded-for) {
set req.http.X-Forwarded-For =
req.http.X-Forwarded-For + ", " + client.ip;
} else {
set req.http.X-Forwarded-For = client.ip;
}
}
if (req.request != "GET" &&
req.request != "HEAD" &&
req.request != "PUT" &&
req.request != "POST" &&
req.request != "TRACE" &&
req.request != "OPTIONS" &&
req.request != "DELETE") {
/* Non-RFC2616 or CONNECT which is weird. */
return (pipe);
}
if (req.request != "GET" && req.request != "HEAD") {
/* We only deal with GET and HEAD by default */
return (pass);
}
if (req.http.Authorization || req.http.Cookie) {
/* Not cacheable by default */
#return (pass);
}
return (lookup);
}
set req.http.Surrogate-Capability = "abc=ESI/1.0";
permet de dire à Varnish de prendre en charge le langage ESI, qui est
un langage de balisage qui s'insère dans le code HTML, et déclenche des
traitement de construction de la page.set req.http.Surrogate-Capability = "abc=ESI/1.0";
if (req.restarts == 0) {
if (req.http.x-forwarded-for) {
set req.http.X-Forwarded-For =
req.http.X-Forwarded-For + ", " + client.ip;
} else {
set req.http.X-Forwarded-For = client.ip;
}
}
if (req.request != "GET" &&
req.request != "HEAD" &&
req.request != "PUT" &&
req.request != "POST" &&
req.request != "TRACE" &&
req.request != "OPTIONS" &&
req.request != "DELETE") {
/* Non-RFC2616 or CONNECT which is weird. */
return (pipe);
}
if (req.request != "GET" && req.request != "HEAD") {
/* We only deal with GET and HEAD by default */
return (pass);
}
if (req.http.Authorization || req.http.Cookie) {
/* Not cacheable by default */
#return (pass);
}
return (lookup);
}
sub vcl_hash {
### these 2 entries are the default ones used for vcl. Below we add our own.
hash_data(req.url);
hash_data(req.http.host);
if( req.http.Cookie ~ "locale" ) {
hash_data(regsub( req.http.Cookie, "^.*?locale=([^;]*);*.*$", "\1" ));
}
return (hash);
}
if( req.http.Cookie ~ "locale" ) {### these 2 entries are the default ones used for vcl. Below we add our own.
hash_data(req.url);
hash_data(req.http.host);
if( req.http.Cookie ~ "locale" ) {
hash_data(regsub( req.http.Cookie, "^.*?locale=([^;]*);*.*$", "\1" ));
}
return (hash);
}
hash_data(regsub( req.http.Cookie, "^.*?locale=([^;]*);*.*$", "\1" ));
}
permet de gérer un cache par langue, dans le cas où la langue n'est pas spécifiée dans l'URL. Par défaut, Varnish prend pour hash l'URL de la page, donc si votre langue est en session, vous obtiendrez le même contenu pour toutes vos langues. Mettre la locale en cookie, puis ici ajouter sa valeur au hash règle ce problème.
Nous verrons plus bas comment mettre votre locale en cookie.
sub vcl_fetch {
if (beresp.http.surrogate-control ~ "ESI/1.0") {
unset beresp.http.surrogate-control;
set beresp.do_esi = true;
}
if (beresp.ttl <= 0s ||
beresp.http.Set-Cookie ||
beresp.http.Vary == "*") {
/*
* Mark as "Hit-For-Pass" for the next 2 minutes
*/
set beresp.ttl = 120 s;
return (hit_for_pass);
}
return (deliver);
}
if (beresp.http.surrogate-control ~ "ESI/1.0") {
unset beresp.http.surrogate-control;
set beresp.do_esi = true;
}
if (beresp.ttl <= 0s ||
beresp.http.Set-Cookie ||
beresp.http.Vary == "*") {
/*
* Mark as "Hit-For-Pass" for the next 2 minutes
*/
set beresp.ttl = 120 s;
return (hit_for_pass);
}
return (deliver);
}
Après avoir modifié ce fichier, redémarrez Varnish :
sudo service varnish restart
Configuration de Symfony2
app/config/config.yml
framework:
templating: { engines: ['twig'] }
session:
default_locale: fr
auto_start: false # Pour ne pas générer le PHPSESSID à chaque page (cache multi user)
esi: true # Pour utiliser les tags ESI dans les templates Twig
templating: { engines: ['twig'] }
session:
default_locale: fr
auto_start: false # Pour ne pas générer le PHPSESSID à chaque page (cache multi user)
esi: true # Pour utiliser les tags ESI dans les templates Twig
app/config/routing.yml
_internal:
resource: "@FrameworkBundle/Resources/config/routing/internal.xml"
prefix: /_internal
-> pour la prise en charge des routes internes de Symfony2 dans les tags ESIresource: "@FrameworkBundle/Resources/config/routing/internal.xml"
prefix: /_internal
web/app.php
//$kernel = new AppCache($kernel);
-> On commente cette ligne pour passer par Varnish et
non plus par le système de cache de Symfony2. (voir s’il y a une
possibilité de gérer ça autrement car ce n'est pas très propre !)Mise en place des blocs et pages cachées dans Symfony2
Pour cacher un bloc, il faut que celui-ci soit rendu par une action :
{% render "NomDeBundle:Test:monAction" with {'locale': app.session.locale}, {'standalone': true} %}
La locale sert à gérer le cache par langue, du fait que ce paramètre se retrouve dans l’url interne et donc dans le hash utilisé par Varnish. Si la langue n’est pas encore dans le cookie, le le hash ne contiendra pas la locale ni fr, ni en, mais sera vide. Il est donc mieux de faire :
{% if app.session.locale %}
{% render "NomDeBundle:Test:monAction" with {'locale': app.session.locale}, {'standalone': true} %}
{% else %}
{% render "NomDeBundle:Test:monAction" with {'locale': app.session.defaultLocale}, {'standalone': true} %}
{% endif %}
{% render "NomDeBundle:Test:monAction" with {'locale': app.session.locale}, {'standalone': true} %}
{% else %}
{% render "NomDeBundle:Test:monAction" with {'locale': app.session.defaultLocale}, {'standalone': true} %}
{% endif %}
L’option 'standalone': true va faire que ce bloc deviendra un tag ESI.
Le tag ESI ressemblera à :
<esi:include src="http://example.com/1.html" onerror="continue"/>
Dans l’action qui rend le bloc :
/**
* Renders the user cart block on the top right of the page
* ESI block
*
* @Extra\Template("NomDeBundle:Test:esi_header_top.html.twig")
*/
public function cartBlockAction() {
$response = new Response();
$response->setContent($this->renderView('NomDeBundle:Test:esi_header_top.html.twig', array()));
$response->setSharedMaxAge(600); # en secondes, donc environ 10 minutes
$response->setPublic();
return $response;
}
-> On ajoute les bons headers à la réponse, où setSharedMaxAge est le temps en secondes de la durée de vie du cache.* Renders the user cart block on the top right of the page
* ESI block
*
* @Extra\Template("NomDeBundle:Test:esi_header_top.html.twig")
*/
public function cartBlockAction() {
$response = new Response();
$response->setContent($this->renderView('NomDeBundle:Test:esi_header_top.html.twig', array()));
$response->setSharedMaxAge(600); # en secondes, donc environ 10 minutes
$response->setPublic();
return $response;
}
-> Même fonctionnement pour les pages complètes.
Pour ne pas mettre en cache les blocs utilisant par exemple la session, mettre les headers adéquats dans la réponse :
$response->setMaxAge(0);
$response->setPrivate();
$response->setPrivate();
Cookie de langue
Comme on l'a vu plus haut, si votre locale est dans votre session et non dans l'URL, il faut créer un cookie contenant la locale, au moment où vous changez votre locale ; dans une action changeLocaleAction par exemple.
Lorsque vous vous apprêtez à retourner l'objet Response :
$response->headers->setCookie(
new Cookie(
'locale',
$votreLocale,
time() + 600));
new Cookie(
'locale',
$votreLocale,
time() + 600));
Pour faire vos tests
Créer un fichier de test qui fait un CURL sur une URL passée en paramètre GET avec le header “Surrogate-Capability:abc=ESI/1.0”
-> celà permet de voir les balises ESI dans le code source (par exemple en inspectant avec Firebug).
Par exemple web/testvarnish.php
<?php
$url = $_GET["url"];
$ret = curl_init($url);
curl_setopt($ret, CURLOPT_HTTPHEADER, array(
'Surrogate-Capability:abc=ESI/1.0'
));
echo curl_exec($ret);
?>
$url = $_GET["url"];
$ret = curl_init($url);
curl_setopt($ret, CURLOPT_HTTPHEADER, array(
'Surrogate-Capability:abc=ESI/1.0'
));
echo curl_exec($ret);
?>
Enfin, vous pouvez tester les performances avec un outil tel que JMeter.
Salut,
RépondreSupprimerMerci pour ce tuto.
Est-ce qu'avec cette configuration, par défaut toutes les pages sont mises en cache par Varnish ? J'ai l'impression que oui et c'est pas ce que je recherche. Je voudrais que par défaut, sans rien spécifier dans la Response, Varnish n'intervienne pas...
Merci
Non ici on ne met en cache qu'un "bout" de page. C'est l'option standalone qui va faire de ce bloc un bloc ESI.
RépondreSupprimerPour les blocs ESI c'est OK. Je parlais des pages "normales", qui sont renvoyées par les controllers via une Response. Dans mes essais, Varnish les stocke en cache alors que je ne le veux pas. Faut-il ajouter un setPrivate() ou setMaxAge(0) sur toutes les Response retournées ? Ca me paraît bizarre, car j'avais lu dans la doc S2 que par défaut les réponses sont private pour justement éviter une mise en cache non souhaitée. J'ai du rater quelque chose! Mais quoi?
RépondreSupprimerJ'ai trouvé: il fallait que j'enlève "set beresp.ttl = 120 s;" car sinon, par défaut, Varnish met en cache toutes les pages pour 2 minutes !
RépondreSupprimerSalut,
RépondreSupprimerLa config "auto_start" pour la session ne fonctionne pas en symfony2.4 et superieur.
J'ai donc forcement un Token Anonymous dans toutes mes pages. Cela marche tres bien avec le AppCache, mais des que je passe a Varnish cela ne marche pas plus ...
As-tu essaye de faire fonctionner ta conf varnish avec la nouvelle version de symfo ??
Salut, non désolé je n'ai pas retouché à Varnish depuis.
Supprimer