mnapoli / bref-bootstrap-benchmarks

Benchmark of possible solutions for Bref and the new AWS Lambda runtime API
https://github.com/mnapoli/bref/issues/100
22 stars 3 forks source link

Solution E #6

Closed bubba-h57 closed 5 years ago

bubba-h57 commented 5 years ago

Solution E currently states that it would be forwarding Lambda events to PHP-FPM via FastCGI. I wrote a nodejs shim last summer that is similar. However, it simply forwards directly to php-cgi are you looking for something similar to this nodejs code or is Solution E really about trying to leverage PHP-FPM?

/*jslint node: true */

const spawn = require("child_process").spawnSync;
const parser = require("http-string-parser");
var path = require("path");

exports.handler = function(event, context) {
    // Sets some sane defaults here so that this function doesn't fail
    // when it's not handling a HTTP request from API Gateway.
    var requestMethod = event.httpMethod || 'GET';
    var requestBody = event.body || '';
    var serverName = event.headers ? event.headers.Host : 'lambda_test.dev';
    var requestUri = event.path || '';
    var headers = {};
    var queryParams = '';

    // Convert all headers passed by API Gateway into the correct format for PHP CGI.
    // This means converting a header such as "X-Test" into "HTTP_X-TEST".
    if (event.headers) {
        Object.keys(event.headers).map(function (key) {
            headers['HTTP_' + key.toUpperCase().replace(/-/g, '_')] = event.headers[key];
            headers[key.toUpperCase().replace(/-/g, '_')] = event.headers[key];
        });
    }

    // Convert query parameters passed by API Gateway into the correct format for PHP CGI.
    if (event.queryStringParameters) {
        var parameters = Object.keys(event.queryStringParameters).map(function(key) {
            var obj = key + "=" + event.queryStringParameters[key];
            return obj;
        });
        queryParams = parameters.join("&");
    }

    // Spawn the PHP CGI process with a bunch of environment variables that describe the request.
    var scriptPath = path.resolve(__dirname + '/../../public/index.php')

    var php = spawn('php-cgi', ['-f', scriptPath], {
        env: Object.assign({
            REDIRECT_STATUS: 200,
            REQUEST_METHOD: requestMethod,
            SCRIPT_FILENAME: scriptPath,
            SCRIPT_NAME: '/index.php',
            PATH_INFO: '/',
            SERVER_NAME: serverName,
            SERVER_PROTOCOL: 'HTTP/1.1',
            REQUEST_URI: requestUri,
            QUERY_STRING: queryParams,
            AWS_LAMBDA: true,
            CONTENT_LENGTH: Buffer.byteLength(requestBody, 'utf-8')
        }, headers, process.env),
        input: requestBody
    });

    // When the process exists, we should have a compvare HTTP response to send back to API Gateway.
    var parsedResponse = parser.parseResponse(php.stdout.toString('utf-8'));

    // Signals the end of the Lambda function, and passes the provided object back to API Gateway.
    context.succeed({
        statusCode: parsedResponse.statusCode || 200,
        headers: parsedResponse.headers,
        body: parsedResponse.body
    });
};
mnapoli commented 5 years ago

Hi, this is interesting! I'm not sure about the performances of this (this is spawning a PHP process) but it would be definitely worth it to add it in the benchmark! Maybe this could be very useful to run PHP-style apps (Wordpress, etc.) which do not use PSR-7 or any kind of abstraction for HTTP requests/responses.

If you are able to send a pull request for a scenario H (or whatever the next letter is) that would be awesome!

bubba-h57 commented 5 years ago

I will make it happen.

bubba-h57 commented 5 years ago

We need php-cgi (and also php-fpm) compiled into the /opt/bin directory of the PHP Layer for me to move forward.

That is currently built over in master/bin/php/build.sh does it make sense to move the build script into this benchmark in order to mess around with it? I am very comfortable with making changes to it and providing a pull request, but it as we attempt a variety of things (like if/when we attempt to create a Lambda SAPI) there is a potential for a fairly large quantity of changes to the build script.

What would you prefer me do?

barryvdh commented 5 years ago

I'll have a layer for you that adds it: arn:aws:lambda:us-east-2:416360873395:layer:php-72:3

I also have a PHP version that starts php-cgi in listening mode, not sure what is faster.

barryvdh commented 5 years ago

The only changes I did was settings disable-fpm and disable-cgi to enable it, and added cp /tmp/bref/php-src-php-$PHP_VERSION/sapi/cgi/php-cgi /opt/bin/ below the one that copies php itself. I guess fpm is the same?

barryvdh commented 5 years ago

Basic attempt here: https://github.com/mnapoli/bref-bootstrap-benchmarks/pull/7 But using php and as a listener. But not really happy with it (yet)

bubba-h57 commented 5 years ago

Nice! I will try your layer now with my Scenario H attempt :-)

barryvdh commented 5 years ago

This might be interesting though: https://twitter.com/symfony_en/status/1070611411762581504

Would take away some issues perhaps for the cgi implementation.

mnapoli commented 5 years ago

@barryvdh it seems this binary is running PHP-FPM though.

barryvdh commented 5 years ago

When available, otherwise falls back to php-cgi. But I don't think the source is available (yet)

mnapoli commented 5 years ago

Right so I see no benefit to use that wrapper since we know the environment in which we run PHP. Additionally we are better off running PHP-FPM with one worker only, or even directly with php-cgi in FastCGI mode as discussed.

barryvdh commented 5 years ago

Yeah was hoping the server would be open source, so we could see some implementation details, that might have some edge cases already worked out, but don't think they're open sourcing it yet.

Anyways, we have 2 implementations using fastcgi, I suppose this can be closed?

mnapoli commented 5 years ago

:+1: