Your SlideShare is downloading. ×
0
LA PROGRAMMATION ASYNCHRONE

ET LES PÂTES

François Zaninotto - @francoisz - marmelab.com
PARLONS

CUISINE
Spaghetti à la tomate de Giuseppina
Plat principal - Facile - Bon marché

Pour 4 personnes
Préparation et cuisson: 20 minu...
Spaghetti à la tomate de Giuseppina
Plat principal - Facile - Bon marché

1. Faire bouillir une grande quantité d'eau (non...
MAINTENANT LE CHEF C’EST

PHP
//	
  time	
  is	
  0	
  
$pastaPan	
  =	
  new	
  Pan();	
  
$water	
  =	
  new	
  Water();	
  
$pastaPan-­‐>fill($water)...
LES 3 GRANDS TABOUS
DU DEVELOPPEUR
UTILISER eval()
LES 3 GRANDS TABOUS
DU DEVELOPPEUR
INJECTER UN CONTENEUR
D’INJECTION DE DÉPENDANCE
LES 3 GRANDS TABOUS
DU DEVELOPPEUR

SERVIR DES
PÂTES FROIDES
2/10

NOTE DES INVITÉS
EVENTLOOP
À LA RESCOUSSE
class	
  EventLoop	
  
{	
  
	
  	
  protected	
  $tick	
  =	
  0;	
  
	
  	
  protected	
  $callbacksForTick	
  =	
  arra...
LA CASSEROLE

ASYNCHRONE
class	
  AsynchronousPan	
  extends	
  Pan	
  
{	
  
	
  	
  protected	
  $eventLoop;	
  
	
  	
  	
  
	
  	
  public	
  f...
$eventLoop	
  =	
  new	
  EventLoop();	
  
!

$pan	
  =	
  new	
  AsynchronousPan($eventLoop);	
  
$pan-­‐>warm(10,	
  fun...
DÉMONSTRATION
Ca	
  chauffe	
  !	
  
tic-­‐tac	
  :	
  1	
  
tic-­‐tac	
  :	
  2	
  
tic-­‐tac	
  :	
  3	
  
tic-­‐tac	
  :	
  4	
  
tic...
LES SPAGHETTI

ASYNCHRONES
$eventLoop	
  =	
  new	
  EventLoop();	
  
!

$plate	
  =	
  new	
  Plate();	
  
!

//	
  pasta	
  
$pastaPan	
  =	
  new	...
../..	
  
//	
  sauce	
  
$eventLoop-­‐>executeLater($delay	
  =	
  7,	
  function()	
  use	
  ($plate,	
  $eventLoop)	
  ...
Synchrone

SÉQUENTIALITÉ
Asynchrone
pastaPan:	
  Allumage	
  
tic-­‐tac	
  :	
  1	
  
...	
  
tic-­‐tac	
  :	
  7	
  
saucePan:	
  L'huile	
  chauffe	
  
tic-...
SCRIPT SYNCHRONE

29 MINUTES
SCRIPT ASYNCHRONE

18 MINUTES
7/10

NOTE DES INVITÉS
class	
  EventLoop	
  
{	
  
	
  	
  protected	
  $tick	
  =	
  0;	
  
	
  	
  protected	
  $callbacksForTick	
  =	
  arra...
//	
  ...	
  
$eventLoop-­‐>start();	
  
!

//	
  Never	
  executed	
  
$plate-­‐>serve('Régalez-­‐vous');
RESYNCHRONISER

L’ASYNCHRONE
class	
  PlateOfSpaghettiWithSauce	
  extends	
  Plate	
  
{	
  
	
  	
  protected	
  $hasSpaghetti	
  =	
  false;	
  
	
 ...
../..	
  
//	
  sauce	
  
$eventLoop-­‐>executeLater($delay	
  =	
  7,	
  function()	
  use	
  ($plate,	
  $eventLoop)	
  ...
DÉMÊLER
LE CODE
SPAGHETTI
$saucePan	
  =	
  new	
  AsynchronousPan($eventLoop);	
  
$eventLoop-­‐>executeLater($delay	
  =	
  7,	
  function()	
  {	...
$saucePan	
  =	
  new	
  AsynchronousPan($eventLoop);	
  
$serveSauce	
  =	
  function()	
  use	
  ($saucePan,	
  $plate)	...
$saucePan	
  =	
  new	
  AsynchronousPan($eventLoop);	
  
$warmSaucePan	
  =	
  function($callback)	
  use	
  ($saucePan)	...
class	
  Async	
  
{	
  
	
  	
  public	
  static	
  function	
  waterfall($tasks,	
  $callback	
  =	
  null)	
  
	
  	
  ...
EN ASYNCHRONE

PAS DE RETURN
//	
  prototype	
  synchronous	
  function	
  
$cook	
  =	
  function($ingredient)	
  use	
  ($saucePan)	
  {	
  
	
  	
  ...
//	
  prototype	
  asynchronous	
  function	
  
$cook	
  =	
  function($ingredient,	
  $callback)	
  use	
  ($saucePan)	
 ...
EN ASYNCHRONE

PAS DE TRY/CATCH
$cook	
  =	
  function($ingredient,	
  $callback)	
  use	
  ($saucePan)	
  {	
  
	
  	
  $saucePan-­‐>fill($ingredient);	
...
$cook	
  =	
  function($ingredient,	
  $callback)	
  use	
  ($saucePan)	
  {	
  
	
  	
  $saucePan-­‐>fill($ingredient);	
...
DU CODE

RÉUTILISABLE
ROBUSTE
UN PLAT

CHAUD
VICTOIRE !
10/10
NOTE DES INVITÉS
IL N’Y A TOUJOURS

QU’UN SEUL CHEF
PROGRAMMATION NON

PARALLELE
PAS DE TEMPS PERDU

ARÉPONSE D’UN AUTRE
ATTENDRE
LA
LATENCE I/O
Operation	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  CPU	
  cycles	
  
L1	
  ..................	
  3	
...
L1	
  cache	
  reference	
  .........................	
  0.5	
  ns	
  
L2	
  cache	
  reference	
  ..........................
I/O = ATTENTE

ETABLISSEMENT CONNEXION SÉCURISÉE
RÉCEPTION REQUÊTE HTTP
REQUÊTE BASE DE DONNÉES
LECTURE VALEUR DANS MEMCAC...
90%

DU TEMPS DE RÉPONSE
D’UNE REQUÊTE HTTP
EST PASSÉ À ATTENDRE UNE I/O
GET	
  /favicon.ico
Process serveur
Routing
Lancement de l’ordre de
chargement du fichier
Déplacement de la tête de lectur...
UN SERVEUR WEB
PASSE SON TEMPS

A SE TOURNER

LES POUCES
POUR MIEUX UTILISER LE CPU

ON MULTIPLIE

LES PROCESS
LESLA CONSO MÉMOIRE
THREADS
ET DONC
MaxClients	
  50
UN AUTRE MONDE

EST POSSIBLE
GET	
  /favicon.ico
Process serveur
Transfert des données
du disque en mémoire

I/O disque
asynchrone
Envoi de la réponse ...
LIBEL
EVENT LOOP

LIBUV

LIBEIO

ASYNCHRONOUS I/O

MULTI-PLATFORM ABSTRACTION LAYER
GET	
  /favicon.ico
GET	
  /js/jquery.js
GET	
  /css/main.css
COMMENT FAIRE
DES I/O ASYNCHRONES

EN PHP ?
Event-­‐driven,	
  non-­‐blocking	
  I/O	
  with	
  PHP.	
  
PHP & PROCESS PERSISTENTS

PAS BON MÉNAGE
PHP N’A PAS DE FONCTIONS
D’I/O DISQUE ASYNCHRONES
PECL/LIBIO
TANT QU’À INSTALLER
UN BINAIRE
AUTANT EN PRENDRE UN

STABLE
POPULAIRE
TANT QU’À REPOSER
SUR UNE EVENT LOOP
AUTANT UTILISER
UN LANGAGE

ÉVÈNEMENTIEL
TANT QU’À ABUSER
DES FONCTIONS ANONYMES
AUTANT UTILISER UN LANGAGE

FONCTIONNEL
var	
  fs	
  =	
  require('fs');	
  
fs.unlink('/tmp/hello',	
  function	
  (err)	
  {	
  
	
  	
  if	
  (err)	
  throw	
 ...
plate	
  =	
  new	
  Plate();	
  
pastaPan	
  =	
  new	
  AsynchronousPan(eventLoop);	
  
water	
  =	
  new	
  Water();	
 ...
$plate	
  =	
  new	
  Plate();	
  
$pastaPan	
  =	
  new	
  AsynchronousPan($eventLoop);	
  
$water	
  =	
  new	
  Water()...
I/O ASYNCHRONES

UN SEUL CHEF
DES CALLBACKS
PLUS DE CONCURRENCE
MOINS DE CONSOL MÉMOIRE
PAS POSSIBLE EN PHP, NATIF EN NODE...
DES PÂTES

CHAUDES
MERCI
François Zaninotto - @francoisz - marmelab.com
github.com/fzaninotto - github.com/marmelab
La programmation asynchrone... et les pates
Upcoming SlideShare
Loading in...5
×

La programmation asynchrone... et les pates

1,089

Published on

La vidéo de la conférence au Forum PHP 2013 / AFUP : https://www.youtube.com/watch?v=WygFJHEloq0

Published in: Technology
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
1,089
On Slideshare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
7
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

Transcript of "La programmation asynchrone... et les pates"

  1. 1. LA PROGRAMMATION ASYNCHRONE ET LES PÂTES François Zaninotto - @francoisz - marmelab.com
  2. 2. PARLONS CUISINE
  3. 3. Spaghetti à la tomate de Giuseppina Plat principal - Facile - Bon marché Pour 4 personnes Préparation et cuisson: 20 minutes Ingrédients: - 500g de spaghetti - 6 tomates bien mûres - 1 oignon - 1 carotte - 2 gousses d'ail - 1 branche de céleri - huile d'olive - sauge, romarin, basilic frais - sel et poivre - amour
  4. 4. Spaghetti à la tomate de Giuseppina Plat principal - Facile - Bon marché 1. Faire bouillir une grande quantité d'eau (non salée) dans une casserole 2. Pendant ce temps, pelez les tomates et coupez-les grossièrement 3. Epluchez et émincez l'oignon, la carotte, l'ail et le céleri 4. Lorsque l'eau bout, salez-la, puis déposez les spaghettis en couronne 5. Faire chauffer l'huile d'olive dans une sauteuse. Mettez-y à brunir la deuxième gousse d'ail préalablement épluchée. 6. Retirez la gousse d'ail, puis versez le mirepoix (mélange de légumes) et faites revenir à feu vif 7. Ajoutez les tomates, les herbes, salez et poivrez copieusement. Faites chauffer à feu moyen pendant 5 minutes 8. Goûtez régulièrement les spaghettis. Lorsqu'ils sont cuits al dente, égouttez-les puis déposez-les dans un plat chaud. 9. Versez la sauce tomate immédiatement sur les pâtes fumantes. Ajoutez le basilic grossièrement découpé. 10.Servez avec du parmesan rapé et un bon Chianti Classico, et régalez-vous
  5. 5. MAINTENANT LE CHEF C’EST PHP
  6. 6. //  time  is  0   $pastaPan  =  new  Pan();   $water  =  new  Water();   $pastaPan-­‐>fill($water);   $pastaPan-­‐>warm($duration  =  10);   //  now  time  is  10   $pastaPan-­‐>fill(new  Spaghetti());   $pastaPan-­‐>warm($duration  =  8);   //  now  time  is  18   $pastaPan-­‐>remove($water);   ! $saucePan  =  new  Pan();   $saucePan-­‐>fill(new  OliveOil());   $saucePan-­‐>warm($duration  =  2);   //  now  time  is  20   $saucePan-­‐>fill(MirepoixFactory::create($withGarlic  =  true));   $saucePan-­‐>warm($duration  =  5);   //  now  time  is  25   $saucePan-­‐>fill(TomatoFactory::create());   $saucepan-­‐>warm($duration  =  4);   //  now  time  is  29   ! $plate  =  new  Plate();   $plate-­‐>addContentsOf($pastaPan);   $plate-­‐>addContentsOf($saucePan);   $plate-­‐>serve('Régalez-­‐vous');
  7. 7. LES 3 GRANDS TABOUS DU DEVELOPPEUR UTILISER eval()
  8. 8. LES 3 GRANDS TABOUS DU DEVELOPPEUR INJECTER UN CONTENEUR D’INJECTION DE DÉPENDANCE
  9. 9. LES 3 GRANDS TABOUS DU DEVELOPPEUR SERVIR DES PÂTES FROIDES
  10. 10. 2/10 NOTE DES INVITÉS
  11. 11. EVENTLOOP À LA RESCOUSSE
  12. 12. class  EventLoop   {      protected  $tick  =  0;      protected  $callbacksForTick  =  array();   !    public  function  executeLater($delay,  $callback)  {          $this-­‐>callbacksForTick[$this-­‐>tick  +  $delay]  []=  $callback;      }          public  function  start()  {          while  ($this-­‐>callbacksForTick)  {              $this-­‐>tick++;              $this-­‐>executeCallbacks();          }      }            public  function  executeCallbacks()  {          echo  "tic-­‐tac  :  "  .  $this-­‐>tick  .  "n";          if  (!isset($this-­‐>callbacksForTick[$this-­‐>tick]))  {              return;  //  no  callback  to  execute          }          foreach  ($this-­‐>callbacksForTick[$this-­‐>tick]  as  $callback)  {              call_user_func($callback);          }          //  clean  up          unset($this-­‐>callbacksForTick[$this-­‐>tick]);      }   }
  13. 13. LA CASSEROLE ASYNCHRONE
  14. 14. class  AsynchronousPan  extends  Pan   {      protected  $eventLoop;            public  function  __construct(EventLoop  $eventLoop)      {          $this-­‐>eventLoop  =  $eventLoop;      }            public  function  warm($duration,  $callback)      {          $this-­‐>eventLoop-­‐>executeLater($duration,  $callback);      }   }
  15. 15. $eventLoop  =  new  EventLoop();   ! $pan  =  new  AsynchronousPan($eventLoop);   $pan-­‐>warm(10,  function()  {      echo  "Régalez-­‐vousn";   });   ! echo  "Ca  chauffe  !n";   ! $eventLoop-­‐>start();
  16. 16. DÉMONSTRATION
  17. 17. Ca  chauffe  !   tic-­‐tac  :  1   tic-­‐tac  :  2   tic-­‐tac  :  3   tic-­‐tac  :  4   tic-­‐tac  :  5   tic-­‐tac  :  6   tic-­‐tac  :  7   tic-­‐tac  :  8   tic-­‐tac  :  9   tic-­‐tac  :  10   Régalez-­‐vous
  18. 18. LES SPAGHETTI ASYNCHRONES
  19. 19. $eventLoop  =  new  EventLoop();   ! $plate  =  new  Plate();   ! //  pasta   $pastaPan  =  new  AsynchronousPan($eventLoop);   $water  =  new  Water();   $pastaPan-­‐>fill($water);   echo  "pastaPan:  Allumagen";   $pastaPan-­‐>warm($duration  =  10,  function()  use  ($pastaPan,  $plate,  $water)  {      echo  "pastaPan:  L'eau  boutn";      $pastaPan-­‐>fill(new  Spaghetti());      echo  "pastaPan:  Lancement  de  la  cuisson  des  spaghettisn";      $pastaPan-­‐>warm($duration  =  8,  function()  use  ($pastaPan,  $plate,  $water){          echo  "pastaPan:  Les  spaghettis  sont  prêtsn";          $pastaPan-­‐>remove($water);          $plate-­‐>addContentsOf($pastaPan);      });   });   ../..
  20. 20. ../..   //  sauce   $eventLoop-­‐>executeLater($delay  =  7,  function()  use  ($plate,  $eventLoop)  {      $saucePan  =  new  AsynchronousPan($eventLoop);      $saucePan-­‐>fill(new  OliveOil());      echo  "saucePan:  L'huile  chauffen";      $saucePan-­‐>warm($duration  =  2,  function()  use($saucePan,  $plate)  {          echo  "saucePan:  L'huile  est  chauden";          $saucePan-­‐>fill(MirepoixFactory::create($withGarlic  =  true));          echo  "saucePan:  Lancement  de  la  cuisson  du  mirepoixn";          $saucePan-­‐>warm($duration  =  5,  function()  use($saucePan,  $plate)  {              echo  "saucePan:  Le  mirepoix  est  prêt  pour  la  tomaten";              $saucePan-­‐>fill(TomatoFactory::create());              echo  "saucePan:  Lancement  de  la  cuisson  de  la  tomaten";              $saucePan-­‐>warm($duration  =  4,  function()  use($saucePan,  $plate)  {                  echo  "saucePan:  La  sauce  est  prêten";                  $plate-­‐>addContentsOf($saucePan);              });          });      });   });   ! $eventLoop-­‐>start();   $plate-­‐>serve('Régalez-­‐vous');
  21. 21. Synchrone SÉQUENTIALITÉ Asynchrone
  22. 22. pastaPan:  Allumage   tic-­‐tac  :  1   ...   tic-­‐tac  :  7   saucePan:  L'huile  chauffe   tic-­‐tac  :  8   tic-­‐tac  :  9   saucePan:  L'huile  est  chaude   saucePan:  Lancement  de  la  cuisson  du  mirepoix   tic-­‐tac  :  10   pastaPan:  L'eau  bout   pastaPan:  Lancement  de  la  cuisson  des  spaghettis   tic-­‐tac  :  11   ....   tic-­‐tac  :  14   saucePan:  Le  mirepoix  est  prêt  pour  la  tomate   saucePan:  Lancement  de  la  cuisson  de  la  tomate   tic-­‐tac  :  15   ...   tic-­‐tac  :  18   pastaPan:  Les  spaghettis  sont  prêts   saucePan:  La  sauce  est  prête   Régalez-­‐vous
  23. 23. SCRIPT SYNCHRONE 29 MINUTES
  24. 24. SCRIPT ASYNCHRONE 18 MINUTES
  25. 25. 7/10 NOTE DES INVITÉS
  26. 26. class  EventLoop   {      protected  $tick  =  0;      protected  $callbacksForTick  =  array();   !    public  function  executeLater($delay,  $callback)  {          $this-­‐>callbacksForTick[$this-­‐>tick  +  $delay]  []=  $callback;      }          public  function  start()  {          while  ($this-­‐>callbacksForTick)  {              $this-­‐>tick++;              $this-­‐>executeCallbacks();          }      }            public  function  executeCallbacks()  {          echo  "tic-­‐tac  :  "  .  $this-­‐>tick  .  "n";          if  (!isset($this-­‐>callbacksForTick[$this-­‐>tick]))  {              return;  //  no  callback  to  execute          }          foreach  ($this-­‐>callbacksForTick[$this-­‐>tick]  as  $callback)  {              call_user_func($callback);          }          //  clean  up          unset($this-­‐>callbacksForTick[$this-­‐>tick]);      }   }
  27. 27. //  ...   $eventLoop-­‐>start();   ! //  Never  executed   $plate-­‐>serve('Régalez-­‐vous');
  28. 28. RESYNCHRONISER L’ASYNCHRONE
  29. 29. class  PlateOfSpaghettiWithSauce  extends  Plate   {      protected  $hasSpaghetti  =  false;      protected  $hasSauce  =  false;            public  function  addContentsOf(Pan  $pan)      {          parent::addContentsOf($pan);          if  ($pan-­‐>contains('Spaghetti'))  {              $this-­‐>hasSpaghetti  =  true;          }          if  ($pan-­‐>contains('Tomato'))  {              $this-­‐>hasSauce  =  true;          }          if  ($this-­‐>hasSpaghetti  &&  $this-­‐>hasSauce)  {              $this-­‐>serve('Régalez-­‐vous');          }      }   }
  30. 30. ../..   //  sauce   $eventLoop-­‐>executeLater($delay  =  7,  function()  use  ($plate,  $eventLoop)  {      $saucePan  =  new  AsynchronousPan($eventLoop);      $saucePan-­‐>fill(new  OliveOil());      echo  "saucePan:  L'huile  chauffen";      $saucePan-­‐>warm($duration  =  2,  function()  use($saucePan,  $plate)  {          echo  "saucePan:  L'huile  est  chauden";          $saucePan-­‐>fill(MirepoixFactory::create($withGarlic  =  true));          echo  "saucePan:  Lancement  de  la  cuisson  du  mirepoixn";          $saucePan-­‐>warm($duration  =  5,  function()  use($saucePan,  $plate)  {              echo  "saucePan:  Le  mirepoix  est  prêt  pour  la  tomaten";              $saucePan-­‐>fill(TomatoFactory::create());              echo  "saucePan:  Lancement  de  la  cuisson  de  la  tomaten";              $saucePan-­‐>warm($duration  =  4,  function()  use($saucePan,  $plate)  {                  echo  "saucePan:  La  sauce  est  prêten";                  $plate-­‐>addContentsOf($saucePan);              });          });      });   });   ! $eventLoop-­‐>start();   $plate-­‐>serve('Régalez-­‐vous');
  31. 31. DÉMÊLER LE CODE SPAGHETTI
  32. 32. $saucePan  =  new  AsynchronousPan($eventLoop);   $eventLoop-­‐>executeLater($delay  =  7,  function()  {      call_user_func($warmSaucePan);   });   $warmSaucePan  =  function()  use  ($saucePan)  {      $saucePan-­‐>fill(new  OliveOil());      echo  "saucePan:  L'huile  chauffen";      $saucePan-­‐>warm($duration  =  2,  $cookMirepoix);   };   $cookMirepoix  =  function()  use  ($saucePan)  {      echo  "saucePan:  L'huile  est  chauden";      $saucePan-­‐>fill(MirepoixFactory::create($withGarlic  =  true));      echo  "saucePan:  Lancement  de  la  cuisson  du  mirepoixn";      $saucePan-­‐>warm($duration  =  5,  $cookTomato);   };   $cookTomato  =  function()  use  ($saucePan)  {      echo  "saucePan:  Le  mirepoix  est  prêt  pour  la  tomaten";      $saucePan-­‐>fill(TomatoFactory::create());      echo  "saucePan:  Lancement  de  la  cuisson  de  la  tomaten";      $saucePan-­‐>warm($duration  =  4,  $serveSauce);   };   $serveSauce  =  function()  use  ($saucePan,  $plate)  {      echo  "saucePan:  La  sauce  est  prêten";      $plate-­‐>addContentsOf($saucePan);   };
  33. 33. $saucePan  =  new  AsynchronousPan($eventLoop);   $serveSauce  =  function()  use  ($saucePan,  $plate)  {      echo  "saucePan:  La  sauce  est  prêten";      $plate-­‐>addContentsOf($saucePan);   };   $cookTomato  =  function()  use  ($saucePan,  $serveSauce)  {      echo  "saucePan:  Le  mirepoix  est  prêt  pour  la  tomaten";      $saucePan-­‐>fill(TomatoFactory::create());      echo  "saucePan:  Lancement  de  la  cuisson  de  la  tomaten";      $saucePan-­‐>warm($duration  =  4,  $serveSauce);   };   $cookMirepoix  =  function()  use  ($saucePan,  $cookTomato)  {      echo  "saucePan:  L'huile  est  chauden";      $saucePan-­‐>fill(MirepoixFactory::create($withGarlic  =  true));      echo  "saucePan:  Lancement  de  la  cuisson  du  mirepoixn";      $saucePan-­‐>warm($duration  =  5,  $cookTomato);   };   $warmSaucePan  =  function()  use  ($saucePan,  $cookMirepoix)  {      $saucePan-­‐>fill(new  OliveOil());      echo  "saucePan:  L'huile  chauffen";      $saucePan-­‐>warm($duration  =  2,  $cookMirepoix);   };   $eventLoop-­‐>executeLater($delay  =  7,  function()  use  ($warmSaucePan)  {          call_user_func($warmSaucePan);   });
  34. 34. $saucePan  =  new  AsynchronousPan($eventLoop);   $warmSaucePan  =  function($callback)  use  ($saucePan)  {      $saucePan-­‐>fill(new  OliveOil());      echo  "saucePan:  L'huile  chauffen";      $saucePan-­‐>warm($duration  =  2,  $callback);   };   $cookMirepoix  =  function($callback)  use  ($saucePan)  {      echo  "saucePan:  L'huile  est  chauden";      $saucePan-­‐>fill(MirepoixFactory::create($withGarlic  =  true));      echo  "saucePan:  Lancement  de  la  cuisson  du  mirepoixn";      $saucePan-­‐>warm($duration  =  5,  $callback);   };   $cookTomato  =  function($callback)  use  ($saucePan)  {      echo  "saucePan:  Le  mirepoix  est  prêt  pour  la  tomaten";      $saucePan-­‐>fill(TomatoFactory::create());      echo  "saucePan:  Lancement  de  la  cuisson  de  la  tomaten";      $saucePan-­‐>warm($duration  =  4,  $callback);   };   $serveSauce  =  function()  use  ($saucePan,  $plate)  {      echo  "saucePan:  La  sauce  est  prêten";      $plate-­‐>addContentsOf($saucePan);   };   $eventLoop-­‐>executeLater($delay  =  7,  function()  use  ($plate,  $eventLoop)  {      Async::waterfall(          array($warmSaucePan,  $cookMirepoix,  $cookTomato),          $serveSauce      );   });
  35. 35. class  Async   {      public  static  function  waterfall($tasks,  $callback  =  null)      {              $taskCallback  =  function  ()  use  (&$next)  {                      call_user_func_array($next,  func_get_args());              };              $done  =  function  ()  use  ($callback)  {                      if  ($callback)  {                              call_user_func_array($callback,  func_get_args());                      }              };              $next  =  function  ()  use  (&$tasks,  $taskCallback,  $done)  {                      if  (0  ===  count($tasks))  {                              call_user_func_array($done,  func_get_args());                              return;                      }   !                    $task  =  array_shift($tasks);                      $args  =  array_merge(func_get_args(),  array($taskCallback));                      call_user_func_array($task,  $args);              };              $next();      }   } Source: https://github.com/reactphp/async/blob/master/src/React/Async/Util.php#L81
  36. 36. EN ASYNCHRONE PAS DE RETURN
  37. 37. //  prototype  synchronous  function   $cook  =  function($ingredient)  use  ($saucePan)  {      $saucePan-­‐>fill($ingredient);      $saucePan-­‐>warm(5);      return  'chaud  devant';   };   ! ! $message  =  $cook(MirepoixFactory::create());   echo  $message,  "n";  
  38. 38. //  prototype  asynchronous  function   $cook  =  function($ingredient,  $callback)  use  ($saucePan)  {      $saucePan-­‐>fill($ingredient);      $saucePan-­‐>warm(5,  function()  {          $callback('chaud  devant');      });   };   ! $cook(MirepoixFactory::create(),  function($message)  {      echo  $message,  "n";   });
  39. 39. EN ASYNCHRONE PAS DE TRY/CATCH
  40. 40. $cook  =  function($ingredient,  $callback)  use  ($saucePan)  {      $saucePan-­‐>fill($ingredient);      $saucePan-­‐>warm(5,  function()  {          $isSummer  =  in_array(date('m'),    array(6,  7,  8)):          if  ($saucePan-­‐>contains('Tomato')  &&  !$isSummer)  {              throw  new  OutOfBoundsException('On  ne  fait                    de  la  bonne  sauce  tomate  qu'en  été');          }          $callback('chaud  devant  !');      });   };   ! try  {      $cook(MirepoixFactory::create(),  function($message)  {          echo  $message,  "n";      });     }  catch  (OutOfBoundsException  $e)  {      echo  "Echec  de  la  recetten";   }
  41. 41. $cook  =  function($ingredient,  $callback)  use  ($saucePan)  {      $saucePan-­‐>fill($ingredient);      $saucePan-­‐>warm(5,  function()  {          $isSummer  =  in_array(date('m'),    array(6,  7,  8)):          if  ($saucePan-­‐>contains('Tomato')  &&  !$isSummer)  {              return  callback(new  OutOfBoundsException('On  ne  fait                    de  la  bonne  sauce  tomate  qu'en  ete'));          }          $callback(null,  'chaud  devant  !');      });   };   ! $cook(MirepoixFactory::create(),  function($error,  $message)  {      if  ($error  instanceOf  OutOfBoundsException)  {          echo  "Echec  de  la  recetten";          return;      }      echo  $message,  "n";   });
  42. 42. DU CODE RÉUTILISABLE ROBUSTE UN PLAT CHAUD
  43. 43. VICTOIRE !
  44. 44. 10/10 NOTE DES INVITÉS
  45. 45. IL N’Y A TOUJOURS QU’UN SEUL CHEF
  46. 46. PROGRAMMATION NON PARALLELE
  47. 47. PAS DE TEMPS PERDU ARÉPONSE D’UN AUTRE ATTENDRE LA
  48. 48. LATENCE I/O Operation                          CPU  cycles   L1  ..................  3   L2  ..................  14   RAM  .................  250   Disk  ................  41,000,000   Network  .............  240,000,000
  49. 49. L1  cache  reference  .........................  0.5  ns   L2  cache  reference  ...........................  7  ns   Main  memory  reference  ......................  100  ns                           Compress  1K  bytes  with  Zippy  .............  3,000  ns    =      3  µs   Send  2K  bytes  over  1  Gbps  network  .......  20,000  ns    =    20  µs   SSD  random  read  ........................  150,000  ns    =  150  µs   Read  1  MB  sequentially  from  memory  .....  250,000  ns    =  250  µs   Round  trip  within  same  datacenter  ......  500,000  ns    =  0.5  ms   Read  1  MB  sequentially  from  SSD*  .....  1,000,000  ns    =      1  ms   Disk  seek  ...........................  10,000,000  ns    =    10  ms   Read  1  MB  sequentially  from  disk  ....  20,000,000  ns    =    20  ms   Send  packet  CA-­‐>Netherlands-­‐>CA  ....  150,000,000  ns    =  150  ms Source: http://www.cs.cornell.edu/projects/ladis2009/talks/dean-keynote-ladis2009.pdf
  50. 50. I/O = ATTENTE ETABLISSEMENT CONNEXION SÉCURISÉE RÉCEPTION REQUÊTE HTTP REQUÊTE BASE DE DONNÉES LECTURE VALEUR DANS MEMCACHE LECTURE DE FICHIER SUR DISQUE APPEL À UNE API REST ENVOI MESSAGE À UN AMQP ENVOI RÉPONSE HTTP
  51. 51. 90% DU TEMPS DE RÉPONSE D’UNE REQUÊTE HTTP EST PASSÉ À ATTENDRE UNE I/O
  52. 52. GET  /favicon.ico Process serveur Routing Lancement de l’ordre de chargement du fichier Déplacement de la tête de lecture Transfert des données du disque en mémoire Construction de la réponse HTTP Traitement Attente Envoi de la réponse HTTP Fin de la requête
  53. 53. UN SERVEUR WEB PASSE SON TEMPS A SE TOURNER LES POUCES
  54. 54. POUR MIEUX UTILISER LE CPU ON MULTIPLIE LES PROCESS LESLA CONSO MÉMOIRE THREADS ET DONC
  55. 55. MaxClients  50
  56. 56. UN AUTRE MONDE EST POSSIBLE
  57. 57. GET  /favicon.ico Process serveur Transfert des données du disque en mémoire I/O disque asynchrone Envoi de la réponse HTTP I/O réseau asynchrone Traitement Attente
  58. 58. LIBEL EVENT LOOP LIBUV LIBEIO ASYNCHRONOUS I/O MULTI-PLATFORM ABSTRACTION LAYER
  59. 59. GET  /favicon.ico GET  /js/jquery.js GET  /css/main.css
  60. 60. COMMENT FAIRE DES I/O ASYNCHRONES EN PHP ?
  61. 61. Event-­‐driven,  non-­‐blocking  I/O  with  PHP.  
  62. 62. PHP & PROCESS PERSISTENTS PAS BON MÉNAGE
  63. 63. PHP N’A PAS DE FONCTIONS D’I/O DISQUE ASYNCHRONES PECL/LIBIO
  64. 64. TANT QU’À INSTALLER UN BINAIRE AUTANT EN PRENDRE UN STABLE POPULAIRE
  65. 65. TANT QU’À REPOSER SUR UNE EVENT LOOP AUTANT UTILISER UN LANGAGE ÉVÈNEMENTIEL
  66. 66. TANT QU’À ABUSER DES FONCTIONS ANONYMES AUTANT UTILISER UN LANGAGE FONCTIONNEL
  67. 67. var  fs  =  require('fs');   fs.unlink('/tmp/hello',  function  (err)  {      if  (err)  throw  err;      console.log('successfully  deleted  /tmp/hello');   });   //  more  code   console.log('deletion  script');
  68. 68. plate  =  new  Plate();   pastaPan  =  new  AsynchronousPan(eventLoop);   water  =  new  Water();   pastaPan.fill(water);   console.log('pastaPan:  Starting  to  boil  water');   pastaPan.warm(duration  =  10,  function()  {      console.log('pastaPan:  Water  is  boiling');      pastaPan.fill(new  Spaghetti());      console.log('pastaPan:  Starting  to  boil  spaghetti');      pastaPan.warm(duration  =  8,  function()  {          console.log('pastaPan:  Spaghetti  is  ready');          pastaPan.remove(water);          plate.addContentsOf(pastaPan);      });   });
  69. 69. $plate  =  new  Plate();   $pastaPan  =  new  AsynchronousPan($eventLoop);   $water  =  new  Water();   $pastaPan-­‐>fill($water);   echo  "pastaPan:  Starting  to  boil  watern";   $pastaPan-­‐>warm($duration  =  10,  function()  use  ($pastaPan,  $plate,  $water)  {      echo  "pastaPan:  Water  is  boilingn";      $pastaPan-­‐>fill(new  Spaghetti());      echo  "pastaPan:  Starting  to  boil  spaghettin";      $pastaPan-­‐>warm($duration  =  8,  function()  use  ($pastaPan,  $plate,  $water)  {          echo  "pastaPan:  Spaghetti  is  readyn";          $pastaPan-­‐>remove($water);          $plate-­‐>addContentsOf($pastaPan);      });   });
  70. 70. I/O ASYNCHRONES UN SEUL CHEF DES CALLBACKS PLUS DE CONCURRENCE MOINS DE CONSOL MÉMOIRE PAS POSSIBLE EN PHP, NATIF EN NODE.JS PAS DE RETURN, PAS DE TRY/CATCH
  71. 71. DES PÂTES CHAUDES
  72. 72. MERCI François Zaninotto - @francoisz - marmelab.com github.com/fzaninotto - github.com/marmelab
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×