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

Async PHP Requests & Reactive Responses with PHP-FPM

Async PHP Requests & Reactive Responses with PHP-FPM

There are many approaches to execute PHP sub-tasks asynchronously or to parallelise PHP execution. While some solutions require extra extensions, individual PHP builds or a lot of process control management, this talk will show you how to configure and use the built-in PHP FastCGI Process Manager (php-fpm) to execute requests asynchronously in an isolated, tunable process pool and eventually handle their responses in a reactive way.

Talk at phpCE 2017 in Poland

Holger Woltersdorf

November 05, 2017
Tweet

More Decks by Holger Woltersdorf

Other Decks in Programming

Transcript

  1. November 5th 2017 • php CE • Poland HOLGER WOLTERSDORF
    Async PHP Requests
    & Reactive Responses with PHP-FPM

    View Slide

  2. HOLGER WOLTERSDORF
    CIO • FATHER • HUSBAND • PHP DEV WITH ♥
    github.com/hollodotme
    @hollodotme

    View Slide

  3. GERMANY

    View Slide

  4. + async

    View Slide

  5. USE CASE
    A PDF CREATION SERVICE

    View Slide

  6. ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • NOVEMBER 5th 2017 • php CE • Poland HOLGER WOLTERSDORF
    USE CASE (PDF CREATION)
    6
    WEB-SERVICE

    View Slide

  7. ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • NOVEMBER 5th 2017 • php CE • Poland HOLGER WOLTERSDORF
    USE CASE (PDF CREATION)
    7
    WEB-SERVICE
    SEQUENTIAL PROCESSING IS OK(-ish)

    View Slide

  8. ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • NOVEMBER 5th 2017 • php CE • Poland HOLGER WOLTERSDORF
    USE CASE (PDF CREATION)
    8
    WEB-SERVICE
    BUT WHAT IF…

    View Slide

  9. ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • NOVEMBER 5th 2017 • php CE • Poland HOLGER WOLTERSDORF
    USE CASE (PDF CREATION)
    9
    PARALLELISM
    TO THE RESCUE

    View Slide

  10. ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • NOVEMBER 5th 2017 • php CE • Poland HOLGER WOLTERSDORF
    WE TRIED…
    10
    exec('php "/create-pdf.php" > /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

    View Slide

  11. ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • NOVEMBER 5th 2017 • php CE • Poland HOLGER WOLTERSDORF
    WORKS, BUT…
    11
    ๏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)

    View Slide

  12. ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • NOVEMBER 5th 2017 • php CE • Poland HOLGER WOLTERSDORF
    WE TRIED…
    12
    $scriptUrl = 'http:#//##www.yoursite.com/create-pdf.php';
    $ch = curl_init();
    curl_setopt( $ch, CURLOPT_URL, $scriptUrl );
    curl_setopt( $ch, CURLOPT_FRESH_CONNECT, true );
    curl_setopt( $ch, CURLOPT_TIMEOUT_MS, 1 );
    curl_exec( $ch );
    curl_close( $ch );
    PHP 5.2.3 - 2009

    View Slide

  13. ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • NOVEMBER 5th 2017 • php CE • Poland HOLGER WOLTERSDORF
    WORKS, BUT…
    13
    ๏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)

    View Slide

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

    View Slide

  15. ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • NOVEMBER 5th 2017 • php CE • Poland HOLGER WOLTERSDORF
    WORKS, BUT…
    15
    ๏ 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

    View Slide

  16. ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • NOVEMBER 5th 2017 • php CE • Poland HOLGER WOLTERSDORF
    WE TRIED TO BE CLEVER…
    16
    function runInBackground()
    {
    include '/create-pdf.php';
    }
    register_shutdown_function( 'runInBackground' );
    header( 'Location: /show/user/a/page', true, 301 );
    flush();

    View Slide

  17. ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • NOVEMBER 5th 2017 • php CE • Poland HOLGER WOLTERSDORF
    WORKS, BUT…
    17
    ๏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!

    View Slide

  18. ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • NOVEMBER 5th 2017 • php CE • Poland HOLGER WOLTERSDORF
    WE COULD USE…
    18
    pthreads

    View Slide

  19. ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • NOVEMBER 5th 2017 • php CE • Poland HOLGER WOLTERSDORF
    WE COULD USE…
    19
    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

    View Slide

  20. ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • NOVEMBER 5th 2017 • php CE • Poland HOLGER WOLTERSDORF
    WE COULD USE…
    20
    pcntl

    View Slide

  21. ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • NOVEMBER 5th 2017 • php CE • Poland HOLGER WOLTERSDORF
    WE COULD USE…
    21
    pcntl
    ๏NEEDS CUSTOM PHP BUILD
    ๏NOT WORKING ON WINDOWS
    ๏BASIC KNOWLEDGE ABOUT UNIX PROCESSES NEEDED
    ๏PROCESS MANAGEMENT IS UP TO YOU

    View Slide

  22. ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • NOVEMBER 5th 2017 • php CE • Poland HOLGER WOLTERSDORF
    WE COULD USE…
    22
    ๏NEEDS ANOTHER PIECE OF INFRASTRUCTURE + PHP EXTENSION
    ๏FEATURE RICH, BUT OVERLOADED FOR SIMPLE ASYNC TASKS
    ๏A LOT OF SETUP FOR LOCAL DEVELOPMENT

    View Slide

  23. ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • NOVEMBER 5th 2017 • php CE • Poland HOLGER WOLTERSDORF
    WHAT DO WE WANT?
    23
    ๏ 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

    View Slide

  24. ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • NOVEMBER 5th 2017 • php CE • Poland HOLGER WOLTERSDORF
    WHAT IF I TOLD YOU…
    24
    THERE IS A BULLET-PROOF PROCESS MANAGER
    SHIPPED WITH PHP
    … AND YOU’RE PROBABLY USING IT ALREADY.

    View Slide

  25. ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • NOVEMBER 5th 2017 • php CE • Poland HOLGER WOLTERSDORF
    25
    PHP-FPM
    (PHP FastCGI Process Manager)

    View Slide

  26. ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • NOVEMBER 5th 2017 • php CE • Poland HOLGER WOLTERSDORF
    26
    WEBSERVER

    (NGINX, APACHE)
    PHP-FPM
    HOW IT USUALLY WORKS
    POOL (WWW)
    1..N PROCESSES
    PHP WEB CONTEXT

    View Slide

  27. ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • NOVEMBER 5th 2017 • php CE • Poland HOLGER WOLTERSDORF
    HOW IT USUALLY WORKS
    27
    PID TIME COMMAND
    1365 00:00:01 php-fpm: master process (/etc/php/7.1/fpm/php-fpm.conf)
    1719 00:00:00 php-fpm: pool ##www
    1720 00:00:00 php-fpm: pool ##www
    4529 00:00:01 php-fpm: pool ##www
    LOOKS FAMILIAR?

    View Slide

  28. hollodotme/fast-cgi-client

    View Slide

  29. ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • NOVEMBER 5th 2017 • php CE • Poland HOLGER WOLTERSDORF
    29
    WEBSERVER

    (NGINX, APACHE)
    PHP-FPM
    HOW IT WORKS
    POOL (WWW)
    1..N PROCESSES
    POOL (BACKGROUND)

    0..N PROCESSES
    PHP WEB CONTEXT

    View Slide

  30. ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • NOVEMBER 5th 2017 • php CE • Poland HOLGER WOLTERSDORF
    30
    WEBSERVER

    (NGINX, APACHE)
    PHP-FPM
    HOW IT WORKS
    POOL (WWW)
    1..N PROCESSES
    POOL (BACKGROUND)

    0..N PROCESSES
    QUEUE
    PHP WEB CONTEXT

    View Slide

  31. ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • NOVEMBER 5th 2017 • php CE • Poland HOLGER WOLTERSDORF
    PHP-FPM POOL CONFIG UNIX DOMAIN SOCKET
    31
    ; 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

    View Slide

  32. ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • NOVEMBER 5th 2017 • php CE • Poland HOLGER WOLTERSDORF
    CONNECT TO UNIX DOMAIN SOCKET
    32
    namespace YourVendor\YourProject;
    use hollodotme\FastCGI\Client;
    use hollodotme\FastCGI\SocketConnections\UnixDomainSocket;
    $connection = new UnixDomainSocket(
    # Socket path to php-fpm
    '/var/run/php/php7.1-fpm-background.sock',
    # Connect timeout in milliseconds (default: 5000)
    5000,
    # Read/write timeout in milliseconds (default: 5000)
    5000
    );
    $client = new Client( $connection );

    View Slide

  33. ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • NOVEMBER 5th 2017 • php CE • Poland HOLGER WOLTERSDORF
    CONNECT TO NETWORK SOCKET
    33
    namespace YourVendor\YourProject;
    use hollodotme\FastCGI\Client;
    use hollodotme\FastCGI\SocketConnections\NetworkSocket;
    $connection = new NetworkSocket(
    # Hostname or IP
    '127.0.0.1',
    # Port
    9001,
    # Connect timeout in milliseconds (default: 5000)
    5000,
    # Read/write timeout in milliseconds (default: 5000)
    5000
    );
    $client = new Client( $connection );

    View Slide

  34. ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • NOVEMBER 5th 2017 • php CE • Poland HOLGER WOLTERSDORF
    PHP-FPM POOL CONFIG NETWORK SOCKET
    34
    ; 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

    View Slide

  35. ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • NOVEMBER 5th 2017 • php CE • Poland HOLGER WOLTERSDORF
    SEND A SYNCHRONOUS REQUEST
    35
    # Content-type: ’application/x-##www-form-urlencoded’ (default)
    $content = http_build_query( ['key' #=> 'value'] );
    # Simulate HTTP Verbs: GET, POST, PUT, PATCH, DELETE
    $request = new PostRequest( '/create-pdf.php', $content );
    $response = $client#->sendRequest( $request );
    echo $response#->getBody();

    View Slide

  36. ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • NOVEMBER 5th 2017 • php CE • Poland HOLGER WOLTERSDORF
    RESPONSE INTERFACE
    36
    namespace hollodotme\FastCGI\Interfaces;
    interface ProvidesResponseData
    {
    public function getRequestId() : int;
    public function getHeaders() : array;
    public function getHeader( string $headerKey ) : string;
    public function getBody() : string;
    public function getRawResponse() : string;
    public function getDuration() : float;
    }

    View Slide

  37. ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • NOVEMBER 5th 2017 • php CE • Poland HOLGER WOLTERSDORF
    ASYNC REQUEST (FIRE & FORGET)
    37
    $client = new Client( new NetworkSocket( '127.0.0.1', 9000 ) );
    $content = http_build_query( ['key' #=> 'value'] );
    $request = new PostRequest( '/create-pdf.php', $content );
    $requestId = $client#->sendAsyncRequest( $request );
    echo "Request sent, got ID: {$requestId}";

    View Slide

  38. ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • NOVEMBER 5th 2017 • php CE • Poland HOLGER WOLTERSDORF
    ASYNC REQUEST + WAIT FOR RESPONSE
    38
    # ##...
    $requestId = $client#->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();

    View Slide

  39. ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • NOVEMBER 5th 2017 • php CE • Poland HOLGER WOLTERSDORF
    USING RESPONSE CALLBACKS
    39
    # Response callbacks expect a ProvidesResponseData instance
    # ##...variadic function
    $request#->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 );

    View Slide

  40. ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • NOVEMBER 5th 2017 • php CE • Poland HOLGER WOLTERSDORF
    USING PASS THROUG CALLBACKS
    40
    # Pass through callbacks expect a string (output buffer)
    # ##...variadic function
    $request#->addPassThroughCallbacks(
    function( string $buffer ) {
    echo $buffer;
    }
    );
    $requestId = $client#->sendAsyncRequest( $request );

    View Slide

  41. ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • NOVEMBER 5th 2017 • php CE • Poland HOLGER WOLTERSDORF
    USING RESPONSE CALLBACKS
    41
    # ##...
    $client#->waitForResponse( $requestId ); # Inner loop
    # ##... is the same as
    while(true) # Outer loop
    {
    if ( $client#->hasResponse( $requestId ) )
    {
    $client#->handleResponse( $requestId );
    break;
    }
    }

    View Slide

  42. ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • NOVEMBER 5th 2017 • php CE • Poland HOLGER WOLTERSDORF
    MULTIPLE REQUESTS - ORDERED RESPONSES
    42
    $requestIds = [];
    $requestIds[] = $client#->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";
    }

    View Slide

  43. ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • NOVEMBER 5th 2017 • php CE • Poland HOLGER WOLTERSDORF
    MULTIPLE REQUESTS - ORDERED RESPONSES
    43
    # PRINTS
    Response #1
    Response #2
    Response #3
    ๏ NO MATTER HOW LONG EACH
    WORKER TOOK TO RESPOND

    View Slide

  44. ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • NOVEMBER 5th 2017 • php CE • Poland HOLGER WOLTERSDORF
    MULTIPLE REQUESTS - REACTIVE RESPONSES
    44
    $requestIds = [];
    $requestIds[] = $client#->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 '.';
    }

    View Slide

  45. ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • NOVEMBER 5th 2017 • php CE • Poland HOLGER WOLTERSDORF
    MULTIPLE REQUESTS - REACTIVE RESPONSES
    45
    # ##... is the same as
    while ( $client#->hasUnhandledResponses() )
    {
    $readyRequestIds = $client#->getRequestIdsHavingResponse();
    # read all ready responses
    foreach ( $readyRequestIds as $requestId )
    {
    $response = $client#->readResponse( $requestId );
    echo $response#->getBody() . "\n";
    }

    echo '.';
    }

    View Slide

  46. ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • NOVEMBER 5th 2017 • php CE • Poland HOLGER WOLTERSDORF
    MULTIPLE REQUESTS - REACTIVE RESPONSES
    46
    # PRINTS
    .....Response #2
    .......Response #3
    ..........Response #1
    ๏ ORDERED BY RESPONSE TIME

    View Slide

  47. ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • NOVEMBER 5th 2017 • php CE • Poland HOLGER WOLTERSDORF
    MULTIPLE REQUESTS - REACTIVE CALLBACKS
    47
    $client#->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 );
    }
    }

    View Slide

  48. DEMO!

    View Slide

  49. QUESTIONS?

    View Slide

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

    View Slide

  51. ASYNC PHP REQUESTS AND REACTIVE RESPONSES WITH PHP-FPM • NOVEMBER 5th 2017 • php CE • Poland HOLGER WOLTERSDORF
    LINKS / REFERENCES
    51
    ๏ 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

    View Slide