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

Concevoir des applications résilientes en 2019

Concevoir des applications résilientes en 2019

Que se passe-t-il dans vos applications si l'un de vos services tiers est indisponible ?

Chez PrestaShop, ça nous est arrivé fin 2017 et les conséquences ont été dramatiques !
Nous avons conçu une librarie PHP capable de vous protéger de l'indisponibilités des API dont vous dépendez mais il vous reste le plus dur à faire: sensibiliser vos équipes business et produit au "pire", pour prévoir la meilleure expérience possible vos utilisateurs

C9bef243216af3ddcf5c62bfeed46c68?s=128

Mickaël Andrieu

November 20, 2019
Tweet

Transcript

  1. Concevoir des applications résilientes en 2019

  2. Qui suis-je ? Mickaël Andrieu Formateur / Architecte logiciel Certifié

    Opquast
  3. Au programme Pourquoi c’est important Le design pattern Circuit Breaker

    Mise en application en PHP Applaudissements et/ou huées du public
  4. 4

  5. PrestaShop en 2019 Le parc de boutiques d’un leader mondial

    de l’e-commerce en bref ! 5 300K Boutiques 30% 65 Part de marché (FR) Pays
  6. None
  7. None
  8. Des services tiers partout • Pour les mises à jour

    • Système de news intégré • Modules tiers (Google analytics par ex) 8
  9. 9

  10. None
  11. Plus de back office !

  12. Les conséquences de l’indisponibilité • Perte d’image, de confiance •

    Perte d’argent (car marketplace intégrée) • Difficultés à résoudre le problème de façon pérenne 12
  13. 13

  14. La résilience d’une application est sa capacité à résister à

    une altération de son environnement.
  15. Améliorer son infrastructure • GCP for the H*** ! •

    CDN & compagnie • Traefic & compagnie 15 “Google ça peut pas tomber façon” - un(e) dev.
  16. Disponibilité d’un service (SLA) 16 Disponibilité en % Indisponibilité par

    année 99 3.65 jours 99.9 8.76 heures 99.99 52.56 minutes 99.999 5.26 minutes 99.9999 31.5 secondes
  17. Durabilité d’un service 17 La durabilité d’un service est la

    garantie de conservation des données sur la durée. => On en parle ici <=
  18. Améliorer son infrastructure • GCP for the H*** ! •

    CDN & compagnie • Traefic & compagnie 18 “Google ça peut pas tomber façon” - un(e) dev. On délègue cette dette technique à des tiers
  19. Améliorer son code • Requête avec client HTTP qui gère

    un timeout • Faire du code non bloquant • Prévoir un fallback en cas de problème ? 19 “En fait si, même les services de Google tombent parfois !” - un(e) autre dev, un peu chiant(e)
  20. plus court 20

  21. Gestion des timeouts 21 <?php $httpClientTimeout = max( $configuredTimeout, ini_get('max_execution_time')

    );
  22. utile 22

  23. Le design pattern Circuit Breaker 23

  24. None
  25. La porte est fermée, que faire ? 1. Taper à

    la porte 2. Attendre une réponse 3. Insister ? 4. Repartir, s’assoir ? (réponse dégradée) 25
  26. Cas idéal: service disponible 1. On tape à la porte

    2. La personne nous ouvre 26 Le Circuit Breaker laisse passer les requêtes. On dit qu’il est fermé et que le système est ouvert.
  27. Cas 2: service indisponible 1. On tape à la porte

    2. Personne ne répond 27 Passé un certain nombre de tentatives, on va appliquer un comportement dégradé.
  28. Cas 2: service indisponible 1. On tape à la porte

    2. La personne ne nous ouvre pas 28 Le Circuit Breaker s’ouvre et intercepte toutes les requêtes. Il retourne une réponse dégradée. On dit qu’il est ouvert et que le système est fermé ou isolé.
  29. Cas 3: vérification de disponibilité “Après avoir attendu un certain

    temps, on retape à la porte” 29 Passé un certain délai, le Circuit Breaker va autoriser une requête sur le service tiers.
  30. Cas 3: vérification de disponibilité “Après avoir attendu un certain

    temps, on retape à la porte” 30 ➔ En cas de succès: le Circuit se referme et le système est ouvert. ➔ En cas d’échec: le Circuit se ré ouvre et le système est isolé de l’extérieur. Cet état de transition est dit semi-ouvert ou semi-fermé.
  31. None
  32. Le Circuit Breaker • Difficile à comprendre • Simple à

    utiliser • Implémente les patterns Retry et Timeout • Permet de retourner une réponse dégradée 32 “Lourd !” - un(e) autre dev
  33. Implémenter un Circuit Breaker en PHP 33

  34. Les librairies disponibles • PrestaShop Circuit Breaker • Resiliency •

    Ganesha 34
  35. Comment choisir ? Ganesha Circuit Breaker Resiliency 5.6 5.6 7.2

    v6 (optionnel) v5 V6 (optionnel) 91% 95% 99% 0 (avec erreurs) 7 7 35
  36. Introduction à Resiliency 36 composer require love-oss/resiliency

  37. Les ingrédients d’un bon Circuit Breaker 1. Un client HTTP

    2. Un système de stockage persistant 3. Un objet qui définit la configuration 4. Un dispatcher d’événements 37
  38. Client(s) HTTP ❖ Capable de faire un appel HTTP(S) ❖

    Capable de gérer un timeout ❖ Compatible avec Guzzle 6 ✔ ❖ Compatible avec Symfony HttpClient ✔ 38
  39. meurt Circuit Breaker 39 Persistance du Circuit Breaker

  40. Persistance du Circuit - adapters 40 ➔ Tableau PHP ➔

    Fichiers ➔ Doctrine ORM ➔ Memcache(d) ➔ Redis ... https://symfony.com/doc/current/components/cache.html#available-cache-adapters
  41. Configuration du Circuit Breaker 41 • Nombre d’échecs (failures) •

    Temps d’attente (timeout) du Client HTTP • Seuil d’attente avant test de disponibilité (threshold) • Temps d’attente secondaire (stripped timeout)
  42. 42 Event Dispatcher

  43. Les événements disponibles 43 ★ Initiated ★ Tried ★ Opened

    ★ ReOpened ★ Closed ★ AvailabilityChecked / “HalfOpened” ★ Reseted ★ Isolated
  44. Préparation du Circuit Breaker 44 <?php $client = new GuzzleClient([

    'proxy' => '192.168.16.1:10', 'method' => 'POST', ]); $mainSystem = MainSystem::createFromArray([ 'failures' => 2, 'timeout' => 0.1, 'stripped_timeout' => 0.2, 'threshold' => 10.0, ], $client); $storage = new SimpleArray(); // une implémentation PSR-14 de l’Event Dispatcher. $dispatcher = new Symfony\Component\EventDispatcher\EventDispatcher();
  45. Utilisation de Resiliency 45 <?php $circuitBreaker = new MainCircuitBreaker($mainSystem, $storage,

    $dispatcher); $fallbackResponse = function ($request) { $uri = $request->getUri(); return "{'error': 'le service API ('. $uri .') est actuellement indisponible'}"; }; $circuitBreaker->call( 'https://api.domain.com', $fallbackResponse, [ '_token' => '123456789', ] );
  46. Utilisation de Resiliency - Isolation 46 <?php $circuitBreaker = new

    MainCircuitBreaker(...); /** * Isoler le système d’un service tiers */ $circuitBreaker->isolate('https://api.domain.com'); /** * Une fois la maintenance terminée, on ré-ouvre le système à l’extérieur ! */ $circuitBreaker->reset('https://api.domain.com');
  47. Utilisation de Resiliency - Monitoring 47 <?php $monitor = new

    SimpleMonitor(); /** * Collecte d’informations lors du cycle de vie de l’application */ function listener(Event $event) { $monitor->add($event); }; /** * Récupération d’un rapport complet pour analyse/stockage */ $report = $monitor->getReport();
  48. Ex dans une application Symfony 48 class DemoController extends AbstractController

    { /** * @Route("/", name="home") */ public function index(CircuitBreaker $circuitBreaker) { $responseFromCircuitBreaker = $circuitBreaker->call( 'https://my-json-server.typicode.com/typicode/demo/comments', function () { return json_encode([ ['id' => 1, 'body' => 'Réponse dégradée', 'postId' => 1] ]); } ); } https://github.com/mickaelandrieu/resiliency-demo
  49. Monitoring avec Symfony 49

  50. Tester 50 Tester la résilience

  51. Tester correctement son système 1. Un client de test HTTP

    2. Mocker les réponses attendues 3. Vérifier l’état du Circuit Breaker 4. … et le contenu de la réponse reçue ! 51
  52. Ex de tests avec PHPUnit 52 <?php /** * Retourne

    une instance de Client HTTP capable de simuler * la disponibilité ou l’indisponibilité de services tiers. * * @return GuzzleClient */ protected function getTestClient(): GuzzleClient { $mock = new MockHandler([ new RequestException('Service indisponible', new Request('GET', 'test')), new RequestException('Service indisponible', new Request('GET', 'test')), new Response(200, [], '{"bonjour": "Codeurs et Codeuses en Seine"}'), ]); $handler = HandlerStack::create($mock); return new GuzzleClient(['handler' => $handler]); }
  53. Ex de tests avec PHPUnit 53 public function testCircuitBreakerWillBeOpenInCaseOfFailures(): void

    { $this->assertInstanceOf(Closed::class, $this->circuitBreaker->getState()); $response = $this->circuitBreaker->call( 'https://httpbin.org/get/foo', $this->createFallbackResponse() ); $this->assertSame('{"uri": https://httpbin.org/get/foo"}', $response); //Après 1 échec le Circuit Breaker s’ouvre ! $this->assertInstanceOf(Opened::class, $this->circuitBreaker->getState()); $this->assertSame( '{"uri": https://httpbin.org/get/foo"}', $this->circuitBreaker->call( 'https://httpbin.org/get/foo', $this->createFallbackResponse() ) ); }
  54. None