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

Concevoir des applications PHP résilientes en 2019

Concevoir des applications PHP résilientes en 2019

Vous souhaitez créer une architecture en micro services, ou une API REST et un joli front en React parce que c'est à la mode. Mais que se passe-t-il quand l'un de vos micro services tombe, ou si un de vos appels à votre API met un peu trop de temps à répondre ?

En gros, comment gérez-vous vous la résilience de vos applications ?

Je vous montrerai comment faire : notamment à l'aide d'une librairie PHP que j'ai créée et qui est intégrée dans le projet Open Source PrestaShop.

Vous comprendrez comment fonctionne un "Circuit Breaker", comment le configurer et comment le tester.

A la fin de cette session, vous aurez les clés pour ne plus craindre les problèmes de réseau. Et vous aurez acquis un nouveau réflexe: penser au pire, pour garantir le minimum vital à vos utilisateurs.

C9bef243216af3ddcf5c62bfeed46c68?s=128

Mickaël Andrieu

October 24, 2019
Tweet

Transcript

  1. Concevoir des applications résilientes en 2019

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

    Mise en application en PHP Applaudissements et/ou huées du public
  3. En attendant chez PrestaShop... 3

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

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

    • Système de news intégré • Modules tiers (Google analytics par ex) 6
  7. None
  8. Et si un service est indisponible ? 8

  9. None
  10. Plus de back office !

  11. 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 11
  12. Comment vous pouvez limiter la casse ? 12

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

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

    CDN & compagnie • Traefic & compagnie 14 “Google ça peut pas tomber façon” - un(e) dev.
  15. Améliorer son infrastructure • GCP for the H*** ! •

    CDN & compagnie • Traefic & compagnie 15 “Google ça peut pas tomber façon” - un(e) dev. On délègue cette dette technique à des tiers
  16. 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 ? 16 “En fait si, même les services de Google tombent parfois !” - un(e) autre dev, un peu chiant(e)
  17. #Protip Configurer un timeout de requête plus court que le

    timeout PHP 17
  18. Le design pattern Circuit Breaker 18

  19. Aller à un rendez vous 1. Taper à la porte

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

    2. La personne nous ouvre 21 Dans ce cas, le circuit breaker laisse passer les requêtes. On dit qu’il est fermé et que le système est ouvert.
  22. Cas 2: service indisponible 1. On tape à la porte

    2. La personne nous ouvre pas 22 Passé un certain nombre de tentatives, on va appliquer un comportement dégradé. Le Circuit Breaker s’ouvre et intercepte toutes les requêtes au service tiers et retourne une réponse que l’on aura défini. On dit qu’il est ouvert et que le système est fermé ou isolé.
  23. Cas 3: vérification de disponibilité “Après avoir attendu un certain

    temps, on retape à la porte” 23 Passé un seuil défini, le Circuit Breaker va autoriser une requête sur le service tiers. • Si elle réussit, le Circuit se referme et le système est ouvert. • Si elle échoue 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é.
  24. None
  25. Le Circuit Breaker • Simple à comprendre et utiliser •

    Implémente les patterns Retry et Timeout • Permet de retourner une réponse dégradée 25 “Je vous l’avais dit, lol !” - un(e) autre dev
  26. Implémenter un Circuit Breaker en PHP 26

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

    Ganesha 27
  28. Comment choisir ? Ganesha Circuit Breaker Resiliency PHP 5.6 5.6

    7.2 Guzzle ? v6 (optionel) v5 V6 (optionel) Tests ? 91% 95% 99% PHPStan ? 0 (avec erreurs) 7 7 28
  29. Ex d’utilisation de Resiliency 29 <?php $circuitBreaker = new MainCircuitBreaker($mainSystem,

    $storage, $dispatcher); /** * @var Service $service */ $fallbackResponse = function ($service) { return '{}'; }; $circuitBreaker->call( 'https://api.domain.com', $fallbackResponse, [ '_token' => '123456789', ] );
  30. Les ingrédients d’un bon Circuit Breaker • Un client HTTP

    • Un système de stockage persistant • Un objet qui définit la configuration • Un Event Dispatcher (“Nice to have”) 30
  31. Ex d’utilisation de Resiliency 31 <?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(); // any PSR-14 Event Dispatcher implementation. $dispatcher = new Symfony\Component\EventDispatcher\EventDispatcher;
  32. Ex dans une application Symfony 4 32 class DemoController extends

    AbstractController { /** * @Route("/", name="home") * * @param CircuitBreaker $circuitBreaker */ public function index(CircuitBreaker $circuitBreaker) { $responseFromCircuitBreaker = $circuitBreaker->call( 'https://my-json-server.typicode.com/typicode/demo/comments', function () { return json_encode([ ['id' => 1, 'body' => 'Response from fallback', 'postId' => 1] ]); } ); } https://github.com/mickaelandrieu/resiliency-demo
  33. Tester correctement son système • Un client de test HTTP

    • Mocker les réponses attendues • Vérifier l’état du Circuit Breaker • … et la réponse reçue ! 33
  34. Ex de tests avec PHPUnit 34 <?php /** * Returns

    an instance of Client able to emulate * available and not available services. * * @return GuzzleClient */ protected function getTestClient(): GuzzleClient { $mock = new MockHandler([ new RequestException('Service unavailable', new Request('GET', 'test')), new RequestException('Service unavailable', new Request('GET', 'test')), new Response(200, [], '{"hello": "world"}'), ]); $handler = HandlerStack::create($mock); return new GuzzleClient(['handler' => $handler]); }
  35. Ex de tests avec PHPUnit 35 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); //After 1 failed call switch to OPENED state $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() ) ); }
  36. A vous de jouer ! 36