Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Cache HTTP et Symfony sur les sites à fort trafic

Cache HTTP et Symfony sur les sites à fort trafic

Exemples de VCL Varnish.

Jérôme Tamarelle

April 20, 2016
Tweet

More Decks by Jérôme Tamarelle

Other Decks in Programming

Transcript

  1. Qui sommes nous ? Lead Dev Télé-Loisirs (Prisma Média) Mes

    technos: - PHP - Symfony - RabbitMQ - ElasticSearch - MongoDB - Varnish Lead Dev Lemonde.fr (Eleven Labs) Mes technos: - PHP - Symfony - Nodejs - RabbitMQ - PgSQL @GromNAN /GromNAN @CaptainJojo42 /captainjojo
  2. Sommaire ✘ Cache Http késako ? ✘ Le cycle de

    requête Symfony ✘ Les annotations du SensioFrameworkExtraBundle ✘ Un peu de code: ✗ Use Case 1: headers spéciaux pour piloter varnish ou un CDN ✗ Use Case 2: conversion de cookie pour l'utilisation des vary ✗ Use Case 3: découper votre page en ESI ✘ Points d'attention: ✗ Effet google, stratégie stale content ✗ Mesure d'efficacité du cache ✗ Résolution séquentielle des ESI Varnish
  3. Cache HTTP késako ? ✘ Pourquoi du Cache ? ✘

    Les technologies de cache. ✘ Comment le contrôler ?
  4. Les annotations du SensioFrameworkExtraBundle @Cache(expires="tomorrow") @Cache(smaxage=15) @Cache(maxage=15) @Cache(vary:{"Cookie"}) @Cache(public=true) @Cache(lastModified="post.getUpdateAt()")

    @Cache(Etag=234) expires: Wed, 19 Apr 2016 16:00:00 GMT cache-control: smax-age=15 cache-control: max-age=15 vary: "Cookie" cache-control: public=true last-modified: Wed, 19 Apr 2016 16:00:00 GMT etag: 234
  5. # Ban - Catalogue decache # # How to ban

    all objects referencing <my_tag> in the X-Cache-Varnish-Catalog header # curl -X BAN http://<varnish_hostname>/uncache/<my_tag> # Object header ex. : X-Cache-Varnish-Catalog: |1235|9756|546|687|37436543|<my_tag>|region- centre| if (req.method == "BAN") { if (!client.ip ~ invalidators) { return (synth(405, "Ban not allowed")); } if (req.url ~ "^/uncache/") { ban("obj.http.X-Cache-Varnish-Catalog ~ |" + regsub(req.url, "^/uncache/([-0-9a-zA-Z] +)$", "\1") + "|"); return (synth(200, "Banned with " + req.url)); } return (synth(200, "Nothing to ban")); }
  6. sub vcl_backend_response { # Happens after we have read the

    response headers from the backend. # # Here you clean the response headers, removing silly Set-Cookie headers # and other mistakes your backend does. # Serve stale version only if object is cacheable if (beresp.ttl > 0s) { set beresp.grace = 1h; } # Objects with ttl expired but with keep time left may be used to issue conditional (If-Modified- Since / If-None-Match) requests to the backend to refresh them #set beresp.keep = 10s; # Custom headers to give backends more flexibility to manage varnish cache if (beresp.http.X-Cache-Varnish-Maxage) { set beresp.ttl = std.duration(beresp.http.X-Cache-Varnish-Maxage + "s", 3600s); } if (beresp.http.X-Cache-Varnish-Grace && beresp.ttl > 0s) { set beresp.grace = std.duration(beresp.http.X-Cache-Varnish-Grace + "s", 3600s); } }
  7. Cas 2 : Cacher par profil utilisateur Si : ✘

    Page totalement différente suivant que l’ utilisateur est authentifié ou non. ✘ Contenu de la page lié aux permissions de l’ utilisateur. Mais : ✘ Toutes les versions de page doivent être en cache avec une même URL.
  8. GET /protected HTTP/1.1 Host: example.com Accept-Encoding: gzip,deflate Cookie: roles=ROLE_SUBSCRIBER-1461014676-3deb8143cb7c045... GET

    /protected HTTP/1.1 Host: example.com Accept-Encoding: gzip,deflate X-Roles: ROLE_SUBSCRIBER roles timestamp signature Cookie de permissions signé, décodé par le serveur de cache Cache Proxy
  9. import std; import digest; # https://github.com/varnish/libvmod-digest sub vcl_recv { #

    Reset the header to ensure it isn't defined by the client. set req.http.X-Roles = ""; # Checks if the permission cookie is present and correctly formatted if (req.http.cookie ~ "roles=([^-;]+)-([^-;]+)-([^-;]+)") { set req.http.cookieValue = regsub(req.http.cookie, ".*roles=([^-;]+)-([^-;]+)-([^-;]+).*", "\1"); set req.http.cookieExpiry = regsub(req.http.cookie, ".*roles=([^-;]+)-([^-;]+)-([^-;]+).*", "\2"); set req.http.cookieSign = regsub(req.http.cookie, ".*roles=([^-;]+)-([^-;]+)-([^-;]+).*", "\3"); # Checks the expiration date contained in the cookie if (std.integer(req.http.cookieExpiry, 0) > std.time2integer(now)) { # Checks the signature of the cookie to ensure it was not modified by the client if ("0x"+req.http.cookieSign == digest.hmac_sha256("<secret>", req.http.cookieValue+"-"+req.http.cookieExpiry)) { set req.http.X-Roles = req.http.cookieValue; } } unset req.http.cookieValue; unset req.http.cookieExpiry; unset req.http.cookieSign; } unset req.http.cookie; VCL Varnish 4 (sans doute améliorable)
  10. Vary : Le p’tit header qui fait varier la réponse

    ✘ X-Roles : pour que Varnish stocke une page différente pour chaque valeur de ce header ✘ Cookie : pour “invalider” le cache navigateur lors d’un changement de rôles (connexion) ✘ Accept-Encoding : dépend du support de gzip Lire aussi : https://www.fastly.com/blog/best-practices-for-using-the-vary-header HTTP/1.1 200 OK Cache-Control: max-age=86400 Content-Encoding: gzip Vary: Accept-Encoding,X-Roles,Cookie
  11. Cas 3 : Découpage d’une page avec les ESI Contenu

    du cache Page <html> <esi:include> <section> <esi:include> </html> Bloc 1 <img src="1.jpg"/> Bloc 2 <a href="2.html"/> Page <html> <section> </html> <img src="1.jpg"/> <a href="2.html"/> Réponse App 1 App 2 1 min 1h 1h 1 min
  12. Cas 3 : Découpage d’une page avec les ESI Disponible

    dans : ✘ Varnish ✘ Akamai ✘ Apache Traffic Server ✘ Symfony HttpCache Utile pour : ✘ TTL différentes pour chaque partie d’une page ✘ Assembler les réponses de plusieurs “micro-services” https://www.akamai.com/us/en/support/esi.jsp C’est vous qui choisissez qui assemble la page : ✘ L’application ✘ Le serveur ✘ Le CDN
  13. $app = new Silex\Application(); $app->register(new Silex\Provider\TwigServiceProvider(), array( 'twig.path' => __DIR__.'/views',

    )); $app->register(new Silex\Provider\HttpCacheServiceProvider(), array( 'http_cache.cache_dir' => __DIR__.'/cache/', )); $app->register(new Silex\Provider\HttpFragmentServiceProvider()); $app->get('/', function() use ($app) { return (new Response($app['twig']->render('index.twig'), 200))->setTtl (0); }); $app->get('/esi', function () { return (new Response('<h2>In the ESI, it\'s '.date('U').'</h2>', 200))- >setTtl(10); })->bind('esi_route'); $app['http_cache']->run();
  14. Effet Google / Effet Capital stratégie stale-content / prefetching Googlebot

    crawl votre site et interroge des pages profondes qui ne sont pas en cache.
  15. Résolution séquentielle des ESI 10 ESI x 100 ms chacun

    = 1 sec © https://www.mjay.me/varnish-esi-and-sequential-execution/