DEV Community

Leo Cavalcante
Leo Cavalcante

Posted on

Using Bref's LambdaRuntime to Asynchronously Run Swoole Coroutines as Functions on AWS

Swoole will be shipping something really-really cool that is it's own CLI. You can start playing with it using the pre-compiled binary distributed under Swoole's releases at https://github.com/swoole/swoole-src/releases/tag/v4.8.7.

The trick here, for this project, is: we will be shipping Swoole CLI binary along side with Bref's LambdaRuntime to provide a custom AWS lambda runtime that is Swoole powered.

Let's get started.

Create a directory to hold our files:

mkdir swoole-lambda
cd swoole-lambda
Enter fullscreen mode Exit fullscreen mode

Bring Swoole's IDE helper so we can have code completions in our editor:

composer require --dev swoole/ide-helper
Enter fullscreen mode Exit fullscreen mode

Then we can already bring Bref to the playground:

composer require bref/bref
Enter fullscreen mode Exit fullscreen mode

Bref will provide us the abstraction to communicate with AWS Lambda runtime. We can just call it at our bootstrap file. The bootstrap file is where the lambda runtime will start the invocation:

#!/opt/bin/swoole-cli
<?php

use Bref\Context\Context;
use Bref\Runtime\LambdaRuntime;
use Swoole\Coroutine;

require_once __DIR__ . '/vendor/autoload.php';

$runtime = LambdaRuntime::fromEnvironmentVariable('swoole-cli');
$handler = require $_ENV['LAMBDA_TASK_ROOT'] . '/handler.php';

Coroutine\run(static function () use ($runtime, $handler): void {
    while (true) {
        $runtime->processNextEvent($handler);
    }
});
Enter fullscreen mode Exit fullscreen mode

AWS Lambda will move what is in our bin/ directory to /opt/bin, so our Swoole CLI will be there. That is why we can create our Bootstrap PHP application as a self-executing script that will be using an interpreter located at that path.

Let's grab it:

wget https://github.com/swoole/swoole-src/releases/download/v4.8.7/swoole-cli-v4.8.7-linux-x64.tar.xz
tar -xf swoole-cli-v4.8.7-linux-x64.tar.xz
mkdir bin
mv swoole-cli bin/
rm swoole-cli-v4.8.7-linux-x64.tar.xz
Enter fullscreen mode Exit fullscreen mode

UPX to the rescue! 148MB can be too large for a function. Let's use UPX to make it smaller:

upx -9 bin/swoole-cli
Enter fullscreen mode Exit fullscreen mode

The -9 tells UPX to make it as small as possible. This can take a while, but the final result is a 44MB binary, it is about 30% of the original file size!

Now we can safely create our runtime ZIP file:

zip -r runtime.zip bootstrap bin
Enter fullscreen mode Exit fullscreen mode

And upload it to AWS Lambda as a layer:

aws lambda publish-layer-version \
--layer-name swoole-runtime \
--zip-file fileb://runtime.zip \
--region us-east-1
Enter fullscreen mode Exit fullscreen mode

Now lets zip our vendor files:

zip -r vendor.zip vendor
Enter fullscreen mode Exit fullscreen mode

And upload it as a layer as well:

aws lambda publish-layer-version \
--layer-name swoole-lambda-vendor \
--zip-file fileb://vendor.zip \
--region us-east-1
Enter fullscreen mode Exit fullscreen mode

With the layers uploaded, we are ready to create our function.

The handler.php file which bootstrap requires, holds our function code:

<?php

declare(strict_types=1);

use Bref\Context\Context;

return static fn ($event, Context $context): string =>
    'Hello ' . ($event['name'] ?? 'world');

Enter fullscreen mode Exit fullscreen mode

Zip it:

zip -r function.zip handler.php
Enter fullscreen mode Exit fullscreen mode

And create:

aws lambda create-function \
--function-name swoole-lambda \
--handler handler.handler \
--zip-file fileb://function.zip \
--runtime provided \
--role arn:aws:iam::884320951759:role/swoole-lambda \
--region us-east-1 \
--layers arn:aws:lambda:us-east-1:884320951759:layer:swoole-runtime:1 \
arn:aws:lambda:us-east-1:884320951759:layer:swoole-lambda-vendor:1
Enter fullscreen mode Exit fullscreen mode

Ok, roll the drums.

Let's test it:

aws lambda invoke \
--function-name swoole-lambda \
--region us-east-1 \
--log-type Tail \
--query 'LogResult' \
--output text \
--payload $(echo '{"name": "Swoole"}' | base64) output.txt | base64 --decode
Enter fullscreen mode Exit fullscreen mode

The output should be something like:

START RequestId: eaa39e02-b833-4f06-b18d-7e9a5b603a97 Version: $LATEST
END RequestId: eaa39e02-b833-4f06-b18d-7e9a5b603a97
REPORT RequestId: eaa39e02-b833-4f06-b18d-7e9a5b603a97  Duration: 3.67 ms       Billed Duration: 4 ms   Memory Size: 128 MB     Max Memory Used: 115 MB
Enter fullscreen mode Exit fullscreen mode

Let's see the results:

cat output.txt
"Hello Swoole"
Enter fullscreen mode Exit fullscreen mode

That is all folks!

Bref abstracts way all the hassle about interfacing with AWS Lambda runtime. Internally it uses curl_* which Swoole can hook up and make asynchronously!

And of course, don't need to mention, the Swoole CLI project also rocks a lot bringing us a PHP interpreter with Swoole built-in (statically compiled).

Discussion (0)