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

Grâce aux tags Varnish, j'ai switché ma prod sur Raspberry Pi

Grâce aux tags Varnish, j'ai switché ma prod sur Raspberry Pi

Le moyen le plus rapide d'obtenir une réponse d'un Backend est de ne pas l'appeler ;-) Une solution fournie par les "reverse-proxy" me direz-vous, mais pas si simple d'invalider le cache... Ce talk aborde une fonctionnalité méconnue de Varnish: les tags. Nous verrons comment en tirer partie via les "event listeners" d'une application Symfony standard. Au menu, un cluster de Rasberry Pi, une API, et des données toujours fraîches sous la milliseconde

A03f8f68e696cb514cb1e5cb7bd9b0ea?s=128

Jérémy Derussé

May 18, 2017
Tweet

More Decks by Jérémy Derussé

Other Decks in Programming

Transcript

  1. Going to production on a Raspberry Pi with varnish tags

  2. Who am I? Jérémy DERUSSÉ Web Technical Leader author of

    Symfony Lock Component 3.3 @jderusse
  3. How to get fast responses from a PHP application? you

    can't
  4. How to get fast responses from a PHP application? your

    backend you can't
  5. Solution using http shared cache describe in RFC-2616 RFC-7234

  6. Integration in Symfony use Sensio\Bundle\FrameworkExtraBundle\Configuration\Cache; /** * @Cache(smaxage="3600") */ public

    function indexAction() { // ... } Advanced usage with FriendsOfSymfony/FOSHttpCache
  7. Issue #1 Unsharable resources private resources (ie. invoice, shopping cart,

    ...) per role representations Solution vary cache on user/role/other Repousser les limites : HTTP cache et utilisateurs connectés
  8. Issue #2 Cache invalidation “ There are only two hard

    things in Computer Science: cache invalidation and naming things. Phil Karlton
  9. Cache models Validation model etag last-modified drawbacks application is booted

    hard to implement Expiration model expires cache-control drawback no control on invalidation
  10. cache model In a real world Backend can't validate every

    cache HIT Life time is not predictable BUT Varnish can be requested to partially invalidate responses Backend knows when resources change Responses are build on top of resources
  11. curl -I "http://varnish.myapp.com/c" curl -I "http://varnish.myapp.com/b" curl -I "http://varnish.myapp.com/a" HTTP/1.1

    200 OK Cache-Control: public, s-maxage=3600 X-Cache-Tags: Foo,Bar HTTP/1.1 200 OK Cache-Control: public, s-maxage=3600 X-Cache-Tags: Foo Varnish tags curl \ -X "BAN" \ -H "X-Cache-Tags: Foo" \ "http://varnish.myapp.com" HTTP/1.1 200 OK Cache-Control: public, s-maxage=3600 X-Cache-Tags: Bar,Qux
  12. Varnish tags curl "http://varnish.myapp.com/posts/42" HTTP/1.1 200 OK Cache-Control: public, s-maxage=86400

    X-Cache-Tags: Post:42,Author:12,Comment:314,Comment:1337 { "id": 42, "title": "My blog post.", "body": "Lorem Ipsum.", "author": { "id": 12, "username": "jderusse" }, "comments": [ { "id": 314, "message": "Wow such post" }, { "id": 1337, "message": "much performance" } ] }
  13. Automate Tagging Tagging Response 1. Collect displayed resources 2. Generate

    resource identifier 3. Tag response
  14. Automate Tagging Tagging Response - 1. Collect displayed resources namespace

    App\EventListener; use JMS\Serializer\EventDispatcher\Events; use JMS\Serializer\EventDispatcher\EventSubscriberInterface; use JMS\Serializer\EventDispatcher\ObjectEvent; class SerializationTagListener implements EventSubscriberInterface { public function onPostSerialize(ObjectEvent $event) { $resource = $event->getObject(); // TODO } public static function getSubscribedEvents() { return [ [ 'event' => Events::POST_SERIALIZE, 'format' => 'json', 'method' => 'onPostSerialize', ], ]; } }
  15. Automate Tagging Tagging Response - 2. Generate resource identifier namespace

    App\EventListener; use App\Tag\TagExtractorInterface; use JMS\Serializer\EventDispatcher\EventSubscriberInterface; use JMS\Serializer\EventDispatcher\ObjectEvent; class SerializationTagListener implements EventSubscriberInterface { private $tagExtractor; public function __construct(TagExtractorInterface $tagExtractor) { $this->tagExtractor = $tagExtractor; } public function onPostSerialize(ObjectEvent $event): void { //... $tags = $this->tagExtractor->extract($event->getObject()); } //... }
  16. Automate Tagging Tagging Response - 3. Tag response namespace App\EventListener;

    use App\Tag\TagExtractorInterface; use FOS\HttpCache\Handler\TagHandler; use JMS\Serializer\EventDispatcher\EventSubscriberInterface; use JMS\Serializer\EventDispatcher\ObjectEvent; class SerializationTagListener implements EventSubscriberInterface { private $tagExtractor; private $tagHandler; public function __construct(TagExtractorInterface $tagExtractor, TagHandler $tagHandler) { $this->tagExtractor = $tagExtractor; $this->tagHandler = $tagHandler; } public function onPostSerialize(ObjectEvent $event): void { $tags = $this->tagExtractor->extract($event->getObject()); $this->tagHandler->addTags($tags); } //... }
  17. Automate Tagging Tagging Response 1. Collect displayed resources 2. Generate

    resource identifier 3. Tag response Invalidate cache 1. Listen changes 2. Generate resource identifier 3. Call varnish
  18. Automate Tagging Invalidate Cache - 1. Listen changes namespace App\EventListener;

    use Doctrine\Common\EventSubscriber; use Doctrine\ORM\Event\OnFlushEventArgs; use Doctrine\ORM\Events; class DoctrineInvalidationTagListener implements EventSubscriber { public function getSubscribedEvents() { return [Events::onFlush]; } public function onFlush(OnFlushEventArgs $eventArgs) { $uow = $eventArgs->getEntityManager()->getUnitOfWork(); foreach ($uow->getScheduledEntityUpdates() as $resource) { // TODO } foreach ($uow->getScheduledEntityDeletions() as $resource) { // TODO } } }
  19. Automate Tagging Invalidate Cache - 2. Generate resource identifier namespace

    App\EventListener; use App\Tag\TagExtractorInterface; use Doctrine\Common\EventSubscriber; class DoctrineInvalidationTagListener implements EventSubscriber { private $tagExtractor; public function __construct(TagExtractorInterface $tagExtractor) { $this->tagExtractor = $tagExtractor; } public function onFlush(OnFlushEventArgs $eventArgs) { $uow = $eventArgs->getEntityManager()->getUnitOfWork(); $tags = []; foreach ($uow->getScheduledEntityUpdates() as $resource) { $tags = array_merge($tags, $this->tagExtractor->extract($resource)); } foreach ($uow->getScheduledEntityDeletions() as $resource) { $tags = array_merge($tags, $this->tagExtractor->extract($resource)); } // TODO } }
  20. Automate Tagging Invalidate Cache - 3. Call varnish namespace App\EventListener;

    use App\Tag\TagExtractorInterface; use Doctrine\Common\EventSubscriber; use FOS\HttpCache\Handler\TagHandler; class DoctrineInvalidationTagListener implements EventSubscriber { private $tagExtractor; public function __construct(TagExtractorInterface $tagExtractor, TagHandler $tagHandler) { $this->tagExtractor = $tagExtractor; $this->tagHandler = $tagHandler; } public function onFlush(OnFlushEventArgs $eventArgs) { // ... $this->tagHandler->invalidateTags($tags); } }
  21. Automate Tagging Tagging Response 1. Collect displayed resources 2. Generate

    resource identifier 3. Tag response Invalidate cache 1. Listen changes 2. Generate resource identifier 3. Call varnish Enjoy
  22. Silver bullet? Works well when HIT >> MISS Read >>

    Write Application knows resources used to build response Drawback Operations are not Atomic B ackend handles writes Backend knows infrastructure It slows writes
  23. Demo - env=dev - fetch=lazy symfony/symfony doctrine/orm marmelab/admin-on-rest

  24. Demo Raspberry Pi Orange Pi docker NGINX PHP7-FPM Varnish 9.99$

  25. Demo

  26. ab -n 8000 -c 26 192.168.1.17:81/comments/1 This is ApacheBench, Version

    2.3 <$Revision: 1757674 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net Licensed to The Apache Software Foundation, http://www.apache.org/ Server Software: nginx/1.10.3 Server Hostname: 192.168.1.16 Server Port: 80 Document Path: /comments/1 Document Length: 576 bytes Concurrency Level: 26 Time taken for tests: 26.912 seconds Complete requests: 200 Failed requests: 0 Total transferred: 193400 bytes HTML transferred: 115200 bytes Requests per second: 7.43 [#/sec] (mean) Time per request: 3498.553 [ms] (mean) Time per request: 134.560 [ms] (mean, across all concurrent Transfer rate: 7.02 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 1 2 1.4 1 6 Processing: 546 3342 1156.7 3020 7204 Waiting: 546 3342 1156.7 3020 7204 Total: 550 3344 1156.5 3021 7205 in numbers ab -n 8000 -c 26 192.168.1.17/comments/1 This is ApacheBench, Version 2.3 <$Revision: 1757674 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net Licensed to The Apache Software Foundation, http://www.apache.org/ Server Software: nginx/1.10.3 Server Hostname: 192.168.1.17 Server Port: 80 Document Path: /comments/1 Document Length: 576 bytes Concurrency Level: 26 Time taken for tests: 2.340 seconds Complete requests: 8000 Failed requests: 0 Total transferred: 8948504 bytes HTML transferred: 4608000 bytes Requests per second: 3418.52 [#/sec] (mean) Time per request: 7.606 [ms] (mean) Time per request: 0.293 [ms] (mean, across all concurrent Transfer rate: 3734.21 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 0 2 0.8 2 7 Processing: 2 5 3.3 5 55 Waiting: 2 5 3.3 4 55 Total: 2 8 3.3 7 56 app varnish
  27. Thank You

  28. Questions? @jderusse https://joind.in/talk/0a509

  29. Credits http://linuxgizmos.com/10-dollar-orange-pi-one-pits-quad-core-cortex-a7-against-pi-zero/ http://toutsurlesbisounours.centerblog.net/rub-bisounours-3eme-generation-.html https://rcgo.com.br/recursos.html