Slide 1

Slide 1 text

October 28th 2017 • unKonf • Mannheim HOLGER WOLTERSDORF Async PHP Requests & Reactive Responses with PHP-FPM // unKonf

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

+ async

Slide 4

Slide 4 text

USE CASE PDF CREATION SERVICE

Slide 5

Slide 5 text

ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • October 28th 2017 • unKonf • Mannheim HOLGER WOLTERSDORF // unKonf USE CASE (PDF CREATION) 5 WEB-SERVICE

Slide 6

Slide 6 text

ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • October 28th 2017 • unKonf • Mannheim HOLGER WOLTERSDORF // unKonf USE CASE (PDF CREATION) 6 WEB-SERVICE SEQUENTIAL PROCESSING IS OK(-ish)

Slide 7

Slide 7 text

ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • October 28th 2017 • unKonf • Mannheim HOLGER WOLTERSDORF // unKonf USE CASE (PDF CREATION) 7 WEB-SERVICE BUT: WHAT IF…

Slide 8

Slide 8 text

ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • October 28th 2017 • unKonf • Mannheim HOLGER WOLTERSDORF // unKonf USE CASE (PDF CREATION) 8 PARALLELISM TO THE RESCUE

Slide 9

Slide 9 text

ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • October 28th 2017 • unKonf • Mannheim HOLGER WOLTERSDORF // unKonf WE TRIED… 9 /dev/null 2>&1 &'); # OR shell_exec('php "/create-pdf.php" > /dev/null 2>&1 &'); # OR proc_open('php "/create-pdf.php"', $descriptorSpec ); PHP 4 - 2001

Slide 10

Slide 10 text

ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • October 28th 2017 • unKonf • Mannheim HOLGER WOLTERSDORF // unKonf WORKS, BUT… 10 ๏ PHP SCRIPT IS CALLED IN CLI MODE (DIFFERENT ENVIRONMENT) ๏ COMMAND GETS QUIET MESSY WHEN A LOT OF DATA SHOULD BE PASSED ๏ DATA HANDLING IN CALLED SCRIPT BASED ON $ARGV ARRAY ๏ RESPONSE ENDS UP IN NIRVANA ๏ DEBUGGING IS A NIGHTMARE (ESPECIALLY IN PRODUCTION)

Slide 11

Slide 11 text

ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • October 28th 2017 • unKonf • Mannheim HOLGER WOLTERSDORF // unKonf WE TRIED… 11

Slide 12

Slide 12 text

ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • October 28th 2017 • unKonf • Mannheim HOLGER WOLTERSDORF // unKonf WORKS, BUT… 12 ๏ WEBSERVER ALWAYS INVOLVED (= OVERHEAD + ERROR SOURCE) ๏ MAYBE A LOAD BALANCER INVOLVED, TOO ๏ AT LEAST 2 ENVIRONMENTS TO MAINTAIN ๏ CURL EXTENSION NEEDED ๏ RESPONSE ENDS UP IN NIRVANA ๏ CALLED SCRIPT MUST BE EXPOSED (UNDER DOMAIN’S DOCUMENT ROOT)

Slide 13

Slide 13 text

ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • October 28th 2017 • unKonf • Mannheim HOLGER WOLTERSDORF // unKonf WE TRIED VERY HARD… 13 query( "INSERT INTO queue (id, script, data) VALUES ('123', '/create-pdf.php', '{json}')" ); #/>$ crontab -e */1 * * * * php "/path/to/queue-processor.php" +

Slide 14

Slide 14 text

ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • October 28th 2017 • unKonf • Mannheim HOLGER WOLTERSDORF // unKonf WORKS, BUT… 14 ๏ NO ON-DEMAND EXECUTION ๏ NEEDS A LOT OF LOCKING AND LOGGING ๏ RAISE CONDITIONS FOR THE WIN! ๏ ERRORS CAN PILE UP UNTIL SERVER IS DEAD ๏ HEAVY DATABASE LOAD FOR TECHNICALLY ELUSIVE DATA (HINT: BAD IDEA) ๏ MAINTENANCE OUTSIDE PHP PROJECT NEEDED (CRONTAB) ๏ HARD TO TEST

Slide 15

Slide 15 text

ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • October 28th 2017 • unKonf • Mannheim HOLGER WOLTERSDORF // unKonf WE TRIED TO BE CLEVER… 15

Slide 16

Slide 16 text

ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • October 28th 2017 • unKonf • Mannheim HOLGER WOLTERSDORF // unKonf WORKS, BUT… 16 ๏ WEBSERVER INVOLVED ๏ RESPONSE ENDS UP IN NIRVANA ๏ MEMORY LEAKS FOR THE WIN! ๏ PRETTY HARD ERROR HANDLING ๏ NO EXECUTION TIME LIMIT ๏ WTF? - DON’T TRY TO BE CLEVER!

Slide 17

Slide 17 text

ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • October 28th 2017 • unKonf • Mannheim HOLGER WOLTERSDORF // unKonf WE COULD USE… 17 pthreads

Slide 18

Slide 18 text

ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • October 28th 2017 • unKonf • Mannheim HOLGER WOLTERSDORF // unKonf WE COULD USE… 18 pthreads ๏ NEEDS CUSTOM PHP BUILD ๏ NOT ALL EXTENSIONS ARE THREAD-SAFE ๏ NOT WORKING IN WEB ENVIRONMENT ๏ BASIC KNOWLEDGE ABOUT MULTI THREADING NEEDED ๏ FEATURE AND CONFIG OVERHEAD FOR SIMPLE ASYNC TASKS ๏ PROCESS / THREAD MANAGEMENT IS UP TO YOU

Slide 19

Slide 19 text

ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • October 28th 2017 • unKonf • Mannheim HOLGER WOLTERSDORF // unKonf WE COULD USE… 19 pcntl

Slide 20

Slide 20 text

ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • October 28th 2017 • unKonf • Mannheim HOLGER WOLTERSDORF // unKonf WE COULD USE… 20 pcntl ๏ NEEDS CUSTOM PHP BUILD ๏ NOT WORKING ON WINDOWS ๏ BASIC KNOWLEDGE ABOUT UNIX PROCESSES NEEDED ๏ PROCESS MANAGEMENT IS UP TO YOU

Slide 21

Slide 21 text

ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • October 28th 2017 • unKonf • Mannheim HOLGER WOLTERSDORF // unKonf WE COULD USE… 21 ๏ NEEDS ANOTHER PIECE OF INFRASTRUCTURE + PHP EXTENSION ๏ FEATURE RICH, BUT OVERLOADED FOR SIMPLE ASYNC TASKS ๏ A LOT OF SETUP FOR LOCAL DEVELOPMENT

Slide 22

Slide 22 text

ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • October 28th 2017 • unKonf • Mannheim HOLGER WOLTERSDORF // unKonf WHAT DO WE WANT? 22 ๏ MAKE ASYNC CALLS TO PHP ๏ EVENTUALLY GET THE RESPONSES ๏ NO ADDITIONAL INFRASTRUCTURE ๏ NO ADDITIONAL EXTENSIONS ๏ WEB-REQUEST LIKE DATA HANDLING (BECAUSE WE’RE USED TO IT) ๏ TAKE ADVANTAGE OF OPCACHE ๏ BACKGROUND WORKERS NOT EXPOSED TO "PUBLIC" ๏ TUNEABLE PROCESS MANAGEMENT

Slide 23

Slide 23 text

ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • October 28th 2017 • unKonf • Mannheim HOLGER WOLTERSDORF // unKonf WHAT IF I TOLD YOU… 23 ๏THERE IS A BULLET-PROOF PROCESS MANAGER SHIPPED WITH PHP ๏…AND YOU’RE PROBABLY USING IT ALREADY.

Slide 24

Slide 24 text

ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • October 28th 2017 • unKonf • Mannheim HOLGER WOLTERSDORF // unKonf 24 PHP-FPM (PHP FastCGI Process Manager)

Slide 25

Slide 25 text

ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • October 28th 2017 • unKonf • Mannheim HOLGER WOLTERSDORF // unKonf 25 WEBSERVER
 (NGINX, APACHE) PHP-FPM HOW IT USUALLY WORKS POOL (WWW) 1..N PROCESSES PHP WEB CONTEXT

Slide 26

Slide 26 text

ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • October 28th 2017 • unKonf • Mannheim HOLGER WOLTERSDORF // unKonf 26 WEBSERVER
 (NGINX, APACHE) PHP-FPM HOW IT USUALLY WORKS POOL (WWW) 1..N PROCESSES POOL (BACKGROUND)
 1..N PROCESSES QUEUE PHP WEB CONTEXT

Slide 27

Slide 27 text

hollodotme/fast-cgi-client

Slide 28

Slide 28 text

ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • October 28th 2017 • unKonf • Mannheim HOLGER WOLTERSDORF // unKonf CONNECT TO UNIX DOMAIN SOCKET 28

Slide 29

Slide 29 text

ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • October 28th 2017 • unKonf • Mannheim HOLGER WOLTERSDORF // unKonf PHP-FPM POOL CONFIG UNIX DOMAIN SOCKET 29 ; Pool name [background] ; Process ownership user = www-data group = www-data ; Socket path listen = /var/run/php/php7.1-fpm-background.sock ; Socket ownership listen.owner = www-data listen.group = www-data ; Process management pm = ondemand ; Maximum of children that can be alive at the same time pm.max_children = 100 ; Number of seconds after which an idle children will be killed pm.process_idle_timeout = 10s

Slide 30

Slide 30 text

ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • October 28th 2017 • unKonf • Mannheim HOLGER WOLTERSDORF // unKonf CONNECT TO NETWORK SOCKET 30

Slide 31

Slide 31 text

ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • October 28th 2017 • unKonf • Mannheim HOLGER WOLTERSDORF // unKonf PHP-FPM POOL CONFIG NETWORK SOCKET 31 ; Pool name [background] ; Process ownership user = www-data group = www-data ; Socket IP and Port listen = 127.0.0.1:9001 ; Socket ownership listen.owner = www-data listen.group = www-data ; Process management pm = ondemand ; Maximum of children that can be alive at the same time pm.max_children = 100 ; Number of seconds after which an idle children will be killed pm.process_idle_timeout = 10s

Slide 32

Slide 32 text

ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • October 28th 2017 • unKonf • Mannheim HOLGER WOLTERSDORF // unKonf SEND A SYNCHRONOUS REQUEST 32 'value'] ); # Simulate HTTP Verbs: GET, POST, PUT, PATCH, DELETE $request = new PostRequest( '/create-pdf.php', $content ); $response = $client->sendRequest( $request ); echo $response->getBody();

Slide 33

Slide 33 text

ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • October 28th 2017 • unKonf • Mannheim HOLGER WOLTERSDORF // unKonf RESPONSE INTERFACE 33

Slide 34

Slide 34 text

ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • October 28th 2017 • unKonf • Mannheim HOLGER WOLTERSDORF // unKonf ASYNC FIRE & FORGET 34 'value'] ); $request = new PostRequest( '/create-pdf.php', $content ); $requestId = $client->sendAsyncRequest( $request ); echo "Request sent, got ID: {$requestId}";

Slide 35

Slide 35 text

ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • October 28th 2017 • unKonf • Mannheim HOLGER WOLTERSDORF // unKonf ASYNC REQUEST + WAIT FOR RESPONSE 35 sendAsyncRequest( $request ); echo "Request sent, got ID: {$requestId}"; # Do something in the meanwhile here ... $response = $client->readResponse( $requestId ); # Blocking call until response is received or read timed out echo $response->getBody();

Slide 36

Slide 36 text

ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • October 28th 2017 • unKonf • Mannheim HOLGER WOLTERSDORF // unKonf USING RESPONSE CALLBACKS 36 addResponseCallbacks( function( ProvidesResponseData $response ) { echo $response->getBody(); } ); # Failure callbacks expect a \Throwable instance # ...variadic function $request->addFailureCallbacks( function ( \Throwable $throwable ) { echo $throwable->getMessage(); } ); $requestId = $client->sendAsyncRequest( $request );

Slide 37

Slide 37 text

ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • October 28th 2017 • unKonf • Mannheim HOLGER WOLTERSDORF // unKonf USING PASS THROUG CALLBACKS 37 addPassThroughCallbacks( function( string $buffer ) { echo $buffer; } ); $requestId = $client->sendAsyncRequest( $request );

Slide 38

Slide 38 text

ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • October 28th 2017 • unKonf • Mannheim HOLGER WOLTERSDORF // unKonf USING RESPONSE CALLBACKS 38 waitForResponse( $requestId ); # Inner loop # ... is the same as while(true) # Outer loop { if ( $client->hasResponse( $requestId ) ) { $client->handleResponse( $requestId ); break; } }

Slide 39

Slide 39 text

ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • October 28th 2017 • unKonf • Mannheim HOLGER WOLTERSDORF // unKonf MULTIPLE REQUESTS - ORDERED RESPONSES 39 sendAsyncRequest( $request1 ); $requestIds[] = $client->sendAsyncRequest( $request2 ); $requestIds[] = $client->sendAsyncRequest( $request3 ); echo 'Sent requests: ' . implode( ', ', $requestIds ) . "\n"; # Do something else here in the meanwhile ... # Blocking call until all responses are received or read timed out foreach ($client->readResponses(5000, ...$requestIds) as $response) { echo $response->getBody() . "\n"; }

Slide 40

Slide 40 text

ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • October 28th 2017 • unKonf • Mannheim HOLGER WOLTERSDORF // unKonf MULTIPLE REQUESTS - ORDERED RESPONSES 40 # PRINTS Response #1 Response #2 Response #3 ๏ NO MATTER HOW LONG EACH WORKER TOOK TO RESPOND

Slide 41

Slide 41 text

ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • October 28th 2017 • unKonf • Mannheim HOLGER WOLTERSDORF // unKonf MULTIPLE REQUESTS - REACTIVE RESPONSES 41 sendAsyncRequest( $request1 ); $requestIds[] = $client->sendAsyncRequest( $request2 ); $requestIds[] = $client->sendAsyncRequest( $request3 ); echo 'Sent requests: ' . implode( ', ', $requestIds ) . "\n"; # Do something else here in the meanwhile ... while ( $client->hasUnhandledResponses() ) { # read all ready responses foreach ( $client->readReadyResponses() as $response ) { echo $response->getBody() . "\n"; }
 echo '.'; }

Slide 42

Slide 42 text

ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • October 28th 2017 • unKonf • Mannheim HOLGER WOLTERSDORF // unKonf MULTIPLE REQUESTS - REACTIVE RESPONSES 42 hasUnhandledResponses() ) { $readyRequestIds = $client->getRequestIdsHavingResponse(); # read all ready responses foreach ( $readyRequestIds as $requestId ) { $response = $client->readResponse( $requestId ); echo $response->getBody() . "\n"; }
 echo '.'; }

Slide 43

Slide 43 text

ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • October 28th 2017 • unKonf • Mannheim HOLGER WOLTERSDORF // unKonf MULTIPLE REQUESTS - REACTIVE RESPONSES 43 # PRINTS .....Response #2 .......Response #3 ..........Response #1 ๏ ORDERED BY RESPONSE TIME

Slide 44

Slide 44 text

ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • October 28th 2017 • unKonf • Mannheim HOLGER WOLTERSDORF // unKonf MULTIPLE REQUESTS - REACTIVE CALLBACKS 44 waitForResponses(); # Inner loop # ... is the same as while ( $client->hasUnhandledResponses() ) # Outer loop { $client->handleReadyResponses(); } # ... is the same as while ( $client->hasUnhandledResponses() ) # Outer loop { $readyRequestIds = $client->getRequestIdsHavingResponse(); foreach ( $readyRequestIds as $requestId ) { $client->handleResponse( $requestId ); } }

Slide 45

Slide 45 text

QUESTIONS?

Slide 46

Slide 46 text

THANK YOU! github.com/hollodotme @hollodotme / @F9T3ch fortuneglobe.com phpug-dresden.org @phpugdd HOLGER WOLTERSDORF icehawk.github.io speakerdeck.com/hollodotme Slides available at:

Slide 47

Slide 47 text

ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • October 28th 2017 • unKonf • Mannheim HOLGER WOLTERSDORF // unKonf LINKS / REFERENCES 47 ๏ PHP USERGROUP DRESDEN e.V. http://phpug-dresden.org ๏ Asynchronous cURL Requests: http://www.paul-norman.co.uk/2009/06/asynchronous-curl-requests/ ๏ Arne Blankerts’ talk about nodejs + PHP: https://thephp.cc/dates/2016/02/confoo/just-married-node-js-and-php ๏ Original PHP FastCGI Client by Pierrick Charron: https://github.com/adoy/PHP-FastCGI-Client/ ๏ PHP FastCGI Client: https://github.com/hollodotme/fast-cgi-client ๏ PHP FastCGI Client Demo: https://github.com/hollodotme/fast-cgi-client-demo ๏ FastCGI Specification: http://www.mit.edu/~yandros/doc/specs/fcgi-spec.html ๏ Experimental use-case with rabbitMQ: https://hollo.me/php/experimental-async-php-volume-2.html