Async PHP with React

Async PHP with React

Presented September 12, 2015 at Pacific Northwest PHP: https://joind.in/talk/view/14936

Presented June 16, 2015 at New York PHP: http://www.meetup.com/new-york-php/events/222792380/

Presented April 18, 2015 at Lone Star PHP: https://joind.in/talk/view/13569

Presented January 26, 2015 at Symfony User Group Cologne: http://www.meetup.com/sfugcgn/events/219210293/

Presented January 23, 2015 at PHPBenelux: https://joind.in/talk/view/13408

Presented October 30, 2014 at ZendCon: https://joind.in/talk/view/12404

Presented October 10, 2014 at Symfony Live NYC: https://joind.in/talk/view/12196

Presented August 21, 2014 Nomad PHP: https://joind.in/talk/view/11574

Presented May 22, 2014 at php[tek]: http://joind.in/talk/view/10626

Presented May 15, 2014 at Laracon: https://joind.in/talk/view/11331

Presented December 13, 2013 at SymfonyCon: https://joind.in/talk/view/10377

Reveal.js presentation published at: http://jmikola.github.io/slides/async_php/

F23700b51dc0c196c1dc02f84aeeecdf?s=128

Jeremy Mikola

September 12, 2015
Tweet

Transcript

  1. Jeremy Mikola jmikola

  2. Let’s make a web server

  3. Node.js v a r h t t p = r

    e q u i r e ( ' h t t p ' ) ; v a r s e r v e r = n e w h t t p . S e r v e r ( ) ; s e r v e r . o n ( ' r e q u e s t ' , f u n c t i o n ( r e q , r e s ) { r e s . w r i t e H e a d ( 2 0 0 , { ' C o n t e n t - T y p e ' : ' t e x t / p l a i n ' } ) ; r e s . e n d ( ' H e l l o W o r l d ' ) ; } ) ; s e r v e r . l i s t e n ( 8 0 0 0 , ' 1 2 7 . 0 . 0 . 1 ' ) ;
  4. Python f r o m t w i s t

    e d . i n t e r n e t i m p o r t r e a c t o r f r o m t w i s t e d . w e b . s e r v e r i m p o r t S i t e f r o m t w i s t e d . w e b . r e s o u r c e i m p o r t R e s o u r c e c l a s s H e l l o W o r l d P a g e ( R e s o u r c e ) : i s L e a f = T r u e d e f r e n d e r _ G E T ( s e l f , r e q u e s t ) : r e t u r n " H e l l o W o r l d " r e s o u r c e = H e l l o W o r l d P a g e ( ) f a c t o r y = S i t e ( r e s o u r c e ) r e a c t o r . l i s t e n T C P ( 8 0 0 0 , f a c t o r y ) r e a c t o r . r u n ( )
  5. PHP $ s e r v e r = s

    t r e a m _ s o c k e t _ s e r v e r ( ' t c p : / / 1 2 7 . 0 . 0 . 1 : 8 0 0 0 ' ) ; w h i l e ( $ c o n n = s t r e a m _ s o c k e t _ a c c e p t ( $ s e r v e r , - 1 ) ) { f w r i t e ( $ c o n n , " H T T P / 1 . 1 2 0 0 O K \ r \ n " ) ; f w r i t e ( $ c o n n , " C o n t e n t - L e n g t h : 1 1 \ r \ n \ r \ n " ) ; f w r i t e ( $ c o n n , " H e l l o W o r l d " ) ; f c l o s e ( $ c o n n ) ; } What’s the difference?
  6. Blocking IO - 1 Why is this a problem?

  7. Blocking IO I O C P U I O C

    P U I O C P U
  8. Common Latencies L 1 c a c h e r

    e f e r e n c e 0 . 5 n s B r a n c h m i s p r e d i c t 5 n s L 2 c a c h e r e f e r e n c e 7 n s M u t e x l o c k / u n l o c k 2 5 n s M a i n m e m o r y r e f e r e n c e 1 0 0 n s S e n d 1 K b y t e s o v e r 1 G b p s n e t w o r k 1 0 , 0 0 0 n s R e a d 4 K r a n d o m l y f r o m S S D 1 5 0 , 0 0 0 n s R e a d 1 M B s e q u e n t i a l l y f r o m m e m o r y 2 5 0 , 0 0 0 n s R o u n d t r i p w i t h i n s a m e d a t a c e n t e r 5 0 0 , 0 0 0 n s R e a d 1 M B s e q u e n t i a l l y f r o m S S D 1 , 0 0 0 , 0 0 0 n s D i s k s e e k 1 0 , 0 0 0 , 0 0 0 n s R e a d 1 M B s e q u e n t i a l l y f r o m d i s k 2 0 , 0 0 0 , 0 0 0 n s R o u n d t r i p f r o m U S t o E u r o p e 1 5 0 , 0 0 0 , 0 0 0 n s https://gist.github.com/jboner/2841832
  9. Common Latencies S e n d 1 K b y

    t e s o v e r 1 G b p s n e t w o r k 1 0 , 0 0 0 n s R e a d 4 K r a n d o m l y f r o m S S D 1 5 0 , 0 0 0 n s R e a d 1 M B s e q u e n t i a l l y f r o m m e m o r y 2 5 0 , 0 0 0 n s R o u n d t r i p w i t h i n s a m e d a t a c e n t e r 5 0 0 , 0 0 0 n s R e a d 1 M B s e q u e n t i a l l y f r o m S S D 1 , 0 0 0 , 0 0 0 n s D i s k s e e k 1 0 , 0 0 0 , 0 0 0 n s R e a d 1 M B s e q u e n t i a l l y f r o m d i s k 2 0 , 0 0 0 , 0 0 0 n s R o u n d t r i p f r o m U S t o E u r o p e 1 5 0 , 0 0 0 , 0 0 0 n s https://gist.github.com/jboner/2841832
  10. C10k Problem You can buy a 1000MHz machine with 2

    gigabytes of RAM and an 1000Mbit/sec Ethernet card for $1200 or so. At 20000 clients, that’s 50KHz, 100Kbytes, and 50Kbits/sec per client. It shouldn’t take any more horsepower than that to take four kilobytes from the disk and send them to the network once a second for each of twenty thousand clients. So hardware is no longer the bottleneck. “ http://www.kegel.com/c10k.html
  11. How can we solve this?

  12. Threading I O C P U I O C P

    U I O C P U
  13. Non-blocking IO I O C P U I O C

    P U I O C P U
  14. Tools within PHP p t h r e a d

    s p o p e n ( ) p r o c _ o p e n ( ) p c n t l _ f o r k ( ) s o c k e t _ s e l e c t ( ) s t r e a m _ s e l e c t ( ) s t r e a m _ s e t _ b l o c k i n g ( ) c u r l _ m u l t i _ s e l e c t ( )
  15. $ man 2 select S E L E C T

    ( 2 ) L i n u x P r o g r a m m e r ' s M a n u a l N A M E s e l e c t , p s e l e c t - s y n c h r o n o u s I / O m u l t i p l e x i n g S Y N O P S I S # i n c l u d e < s y s / s e l e c t . h > i n t s e l e c t ( i n t n f d s , f d _ s e t * r e a d f d s , f d _ s e t * w r i t e f d s , f d _ s e t * e x c e p t f d s , s t r u c t t i m e v a l * t i m e o u t ) ; i n t p s e l e c t ( i n t n f d s , f d _ s e t * r e a d f d s , f d _ s e t * w r i t e f d s , f d _ s e t * e x c e p t f d s , c o n s t s t r u c t t i m e s p e c * t i m e o u t , c o n s t s i g s e t _ t * s i g m a s k ) ; D E S C R I P T I O N s e l e c t ( ) a n d p s e l e c t ( ) a l l o w a p r o g r a m t o m o n i t o r m u l t i p l e f i l e d e s c r i p t o r s , w a i t i n g u n t i l o n e o r m o r e o f t h e f i l e d e s c r i p t o r s b e c o m e " r e a d y " f o r s o m e c l a s s o f I / O o p e r a t i o n ( e . g . , i n p u t p o s s i b l e ) . A f i l e d e s c r i p t o r i s c o n s i d e r e d r e a d y i f i t i s p o s s i b l e t o p e r f o r m t h e c o r r e s p o n d i n g I / O o p e r a t i o n ( e . g . , r e a d ( 2 ) ) w i t h o u t b l o c k i n g .
  16. Polling IO $ r e a d a b l

    e = $ r e a d S t r e a m s ; $ w r i t a b l e = $ w r i t e S t r e a m s ; $ e x c e p t = n u l l ; i f ( s t r e a m _ s e l e c t ( $ r e a d a b l e , $ w r i t a b l e , $ e x c e p t , 1 ) ) { f o r e a c h ( $ r e a d a b l e a s $ s t r e a m ) { / / . . . } f o r e a c h ( $ w r i t a b l e a s $ s t r e a m ) { / / . . . } }
  17. Event Loop Tracks timers and streams Each loop iteration is

    a "tick" Ticks execute timers and poll IO Polling timeout can be calculated No timers/streams ends loop
  18. Event-driven, non-blocking I/O with PHP.

  19. Hello World $ l o o p = R e

    a c t \ E v e n t L o o p \ F a c t o r y : : c r e a t e ( ) ; $ s o c k e t = n e w R e a c t \ S o c k e t \ S e r v e r ( $ l o o p ) ; $ h t t p = n e w R e a c t \ H t t p \ S e r v e r ( $ s o c k e t ) ; $ h t t p - > o n ( ' r e q u e s t ' , f u n c t i o n ( $ r e q , $ r e s ) { $ r e s - > w r i t e H e a d ( 2 0 0 , [ ' C o n t e n t - T y p e ' = > ' t e x t / p l a i n ' ] ) ; $ r e s - > e n d ( ' H e l l o W o r l d ' ) ; } ) ; $ s o c k e t - > l i s t e n ( 8 0 0 0 ) ; $ l o o p - > r u n ( ) ;
  20. Reactor Pattern Resources Demultiplexer Dispatcher Request Handlers

  21. React’s Stack D N o d e A R .

    D r o n e W h o i s S O C K S R e d i s W e b S o c k e t s S t o m p Ø M Q I R C D N S H t t p H t t p C l i e n t S o c k e t C h i l d P r o c e s s S t r e a m E v e n t L o o p E v e n t E m i t t e r
  22. Event Loop Add/remove readable/writable streams Schedule run-once or periodic timers

    Multiple implementations StreamSelectLoop uses LibEventLoop uses extension LibEvLoop uses extension stream_select() libevent libev
  23. Event Loop $ l o o p = R e

    a c t \ E v e n t L o o p \ F a c t o r y : : c r e a t e ( ) ; $ s e r v e r = s t r e a m _ s o c k e t _ s e r v e r ( ' t c p : / / 1 2 7 . 0 . 0 . 1 : 8 0 8 0 ' ) ; s t r e a m _ s e t _ b l o c k i n g ( $ s e r v e r , 0 ) ; $ l o o p - > a d d R e a d S t r e a m ( $ s e r v e r , f u n c t i o n ( $ s e r v e r ) u s e ( $ l o o p ) { $ c o n n = s t r e a m _ s o c k e t _ a c c e p t ( $ s e r v e r ) ; $ d a t a = " H T T P / 1 . 1 2 0 0 O K \ r \ n " ; $ d a t a . = " C o n t e n t - L e n g t h : 1 1 \ r \ n \ r \ n " $ d a t a . = " H e l l o W o r l d " ; $ l o o p - > a d d W r i t e S t r e a m ( $ c o n n , f u n c t i o n ( $ c o n n ) u s e ( & $ d a t a , $ l o o p ) { $ b y t e s W r i t t e n = f w r i t e ( $ c o n n , $ d a t a ) ; i f ( $ b y t e s W r i t t e n = = = s t r l e n ( $ d a t a ) ) { f c l o s e ( $ c o n n ) ; $ l o o p - > r e m o v e S t r e a m ( $ c o n n ) ; } e l s e { $ d a t a = s u b s t r ( $ d a t a , 0 , $ b y t e s W r i t t e n ) ; } } ) ; } ) ; $ l o o p - > r u n ( ) ;
  24. Stream Wraps a PHP stream resource Registered with the event

    loop Readable and writable Buffered reads and writes
  25. ReadableStream i s R e a d a b l

    e ( ) p a u s e ( ) r e s u m e ( ) p i p e ( W r i t a b l e S t r e a m $ d e s t ) c l o s e ( ) Events d a t a , e n d , e r r o r , c l o s e
  26. WritableStream i s W r i t a b l

    e ( ) w r i t e ( $ d a t a ) e n d ( $ d a t a = n u l l ) c l o s e ( ) Events d r a i n , e r r o r , c l o s e , p i p e
  27. Streams Readable → Writable

  28. CompositeStream Joins writable and readable streams

  29. ThroughStream Allows data to be filtered

  30. BufferedSink Converts writable stream to a promise

  31. None
  32. Promises Wouldn’t it be nice if asynchronous functions could return

    immediately?
  33. Deferred p r o m i s e ( )

    r e s o l v e ( $ v a l u e = n u l l ) r e j e c t ( $ r e a s o n = n u l l ) p r o g r e s s ( $ u p d a t e = n u l l ) Computation or unit of work that may not have completed yet
  34. Promise Placeholder for the result of a deferred computation t

    h e n ( $ o n F u l f i l l e d , $ o n R e j e c t e d , $ o n P r o g r e s s ) t h e n ( ) returns a new Promise composing the first
  35. Promises in Action $ l o o p = R

    e a c t \ E v e n t L o o p \ F a c t o r y : : c r e a t e ( ) ; $ f a c t o r y = n e w R e a c t \ D n s \ R e s o l v e r \ F a c t o r y ( ) ; $ d n s = $ f a c t o r y - > c r e a t e ( ' 8 . 8 . 8 . 8 ' , $ l o o p ) ; $ d n s - > r e s o l v e ( $ a r g v [ 1 ] ) - > t h e n ( f u n c t i o n ( $ i p ) { e c h o " H o s t : $ i p \ n " ; } , f u n c t i o n ( $ e ) { e c h o " E r r o r : { $ e - > g e t M e s s a g e ( ) } \ n " ; } ) ; $ l o o p - > r u n ( ) ; $ p h p d n s . p h p j m i k o l a . n e t H o s t : 9 7 . 1 0 7 . 1 3 1 . 5 4
  36. Suggested Reading — Domenic Denicola “You’re missing the point of

    promises” Now back to the stack…
  37. Socket Abstracts s t r e a m _ s

    o c k e t _ s e r v e r ( ) Connections are readable/writable Server and client contexts TCP connections
  38. Server l i s t e n ( $ p

    o r t , $ h o s t ) g e t P o r t ( ) s h u t d o w n ( ) Events c o n n e c t i o n , e r r o r
  39. Connection g e t R e m o t e

    A d d r e s s ( ) i s R e a d a b l e ( ) p a u s e ( ) r e s u m e ( ) p i p e ( W r i t a b l e S t r e a m $ d e s t ) i s W r i t a b l e ( ) w r i t e ( $ d a t a ) e n d ( $ d a t a = n u l l ) c l o s e ( ) Events d a t a , d r a i n , e n d , e r r o r , c l o s e , p i p e
  40. TCP Chat $ l o o p = R e

    a c t \ E v e n t L o o p \ F a c t o r y : : c r e a t e ( ) ; $ s o c k e t = n e w R e a c t \ S o c k e t \ S e r v e r ( $ l o o p ) ; $ c l i e n t s = n e w S p l O b j e c t S t o r a g e ( ) ; $ s o c k e t - > o n ( ' c o n n e c t i o n ' , f u n c t i o n ( $ c o n n ) u s e ( $ c l i e n t s ) { $ c l i e n t s - > a t t a c h ( $ c o n n ) ; $ c o n n - > o n ( ' d a t a ' , f u n c t i o n ( $ d a t a ) u s e ( $ c l i e n t s , $ c o n n ) { f o r e a c h ( $ c l i e n t s a s $ c l i e n t ) { i f ( $ c o n n = = = $ c l i e n t ) { c o n t i n u e ; } $ c l i e n t - > w r i t e ( $ c o n n - > g e t R e m o t e A d d r e s s ( ) . ' : ' . $ d a t a ) ; } } ) ; $ c o n n - > o n ( ' e n d ' , f u n c t i o n ( ) u s e ( $ c l i e n t s , $ c o n n ) { $ c l i e n t s - > d e t a c h ( $ c o n n ) ; } ) ; } ) ; $ s o c k e t - > l i s t e n ( 8 8 8 8 , ' 0 . 0 . 0 . 0 ' ) ; $ l o o p - > r u n ( ) ;
  41. TCP Chat $ t e l n e t j

    m i k o l a . n e t 8 8 8 8 T r y i n g 9 7 . 1 0 7 . 1 3 1 . 5 4 . . . C o n n e c t e d t o j m i k o l a . n e t . E s c a p e c h a r a c t e r i s ' ^ ] ' . I s t h i s t h i n g o n ? 1 0 . 1 0 . 2 1 7 . 2 0 2 : H e l l o !
  42. MongoDB Shell $ l o o p = R e

    a c t \ E v e n t L o o p \ F a c t o r y : : c r e a t e ( ) ; $ p r o c e s s = n e w R e a c t \ C h i l d P r o c e s s \ P r o c e s s ( ' m o n g o ' ) ; $ s t d o u t = n e w R e a c t \ S t r e a m \ S t r e a m ( f o p e n ( ' p h p : / / s t d o u t ' , ' w ' ) , $ l o o p ) ; $ p r o c e s s - > o n ( ' e x i t ' , f u n c t i o n ( $ e x i t C o d e , $ t e r m S i g n a l ) u s e ( $ s t d o u t ) { $ s t d o u t - > c l o s e ( ) ; } ) ; $ l o o p - > a d d T i m e r ( 0 . 0 0 1 , f u n c t i o n ( $ t i m e r ) u s e ( $ p r o c e s s , $ s t d o u t ) { $ p r o c e s s - > s t a r t ( $ t i m e r - > g e t L o o p ( ) ) ; $ p r o c e s s - > s t d o u t - > p i p e ( $ s t d o u t ) ; } ) ; $ l o o p - > a d d T i m e r ( 1 , f u n c t i o n ( $ t i m e r ) u s e ( $ p r o c e s s ) { $ p r o c e s s - > s t d i n - > w r i t e ( " d b . f o o . d r o p ( ) ; \ n " ) ; $ p r o c e s s - > s t d i n - > w r i t e ( " d b . f o o . i n s e r t ( { x : 1 } ) ; \ n " ) ; $ p r o c e s s - > s t d i n - > w r i t e ( " d b . f o o . i n s e r t ( { x : 2 } ) ; \ n " ) ; $ p r o c e s s - > s t d i n - > w r i t e ( " d b . f o o . i n s e r t ( { x : 3 } ) ; \ n " ) ; $ p r o c e s s - > s t d i n - > w r i t e ( " d b . f o o . f i n d ( ) . s o r t ( { x : - 1 } ) ; \ n " ) ; $ p r o c e s s - > s t d i n - > w r i t e ( " e x i t \ n " ) ; } ) ; $ l o o p - > r u n ( ) ;
  43. Avoid Blocking Database queries Disk and file IO CPU-intensive tasks

  44. Helpful Tools Inter-process communication Message queues Break up computations with

    $ l o o p - > n e x t T i c k ( c a l l a b l e )
  45. Async MySQL m y s q l i _ q

    u e r y ( $ l i n k , $ q u e r y , M Y S Q L I _ A S Y N C ) m y s q l i _ p o l l ( & $ r e a d , & $ e r r o r , & $ r e j e c t , $ s e c ) m y s q l i _ r e a p _ a s y n c _ q u e r y ( $ l i n k )
  46. Async MySQL m y s q l i _ p

    o l l ( & $ r e a d , & $ e r r o r , & $ r e j e c t , $ s e c ) This looks a lot like s t r e a m _ s e l e c t ( )
  47. None
  48. $ c o m p o s e r r

    e q u i r e " r e a c t / r e a c t = ~ 0 . 4 "
  49. WebSockets https://github.com/cboden/Ratchet

  50. GifSockets https://github.com/videlalvaro/gifsockets https://github.com/reactphp/gifsocket

  51. Thanks! r e a c t p h p .

    o r g Questions?
  52. Image Credits http://www.mariouniverse.com/sprites/snes/smw http://larsurus.deviantart.com/art/Lyra-on-Segway-PNG-269707453