xp-forge/lambda

XP框架的AWS Lambda

v5.4.0 2024-07-05 07:09 UTC

README

Build status on GitHub XP Framework Module BSD Licence Requires PHP 7.0+ Supports PHP 8.0+ Latest Stable Version

无服务器基础设施。

示例

将此代码放入名为 Greet.class.php 的文件中

use com\amazon\aws\lambda\Handler;

class Greet extends Handler {

  /** @return callable|com.amazon.aws.lambda.Lambda|com.amazon.aws.lambda.Streaming */
  public function target() {
    return fn($event, $context) => sprintf(
      'Hello %s from PHP %s via %s @ %s',
      $event['name'],
      PHP_VERSION,
      $context->functionName,
      $context->region
    );
  }
}

传递的两个参数是 $event(一个值,取决于lambda从哪里被调用)和 $context(一个上下文实例,见下文)。

初始化

如果您需要运行任何初始化代码,可以在从 target() 返回lambda之前执行。这段代码仅在初始化阶段运行一次。

use com\amazon\aws\lambda\Handler;

class Greet extends Handler {

  /** @return callable|com.amazon.aws.lambda.Lambda|com.amazon.aws.lambda.Streaming */
  public function target() {
    $default= $this->environment->properties('task')->readString('greet', 'default');

    return fn($event, $context) => sprintf(
      'Hello %s from PHP %s via %s @ %s',
      $event['name'] ?? $default,
      PHP_VERSION,
      $context->functionName,
      $context->region
    );
  }
}

通过 $this->environment 访问的lambda的环境是一个环境实例,见下文。

日志记录

要向lambda的日志流写入输出,请使用 trace()

use com\amazon\aws\lambda\Handler;

class Greet extends Handler {

  /** @return callable|com.amazon.aws.lambda.Lambda|com.amazon.aws.lambda.Streaming */
  public function target() {
    return function($event, $context) {
      $this->environment->trace('Invoked with ', $event);

      return sprintf(/* Shortened for brevity */);
    };
  }
}

任何非字符串参数都会使用 util.Objects::stringOf() 转换为字符串。要集成到XP日志,将环境的writer传递给控制台追加器,例如使用 $cat= Logging::all()->toConsole($this->environment->writer)

响应流

此库支持AWS Lambda响应流,这是AWS在2023年4月宣布的功能。要使用流,请从处理器的方法 target() 返回一个 function(var, Stream, Context),而不是一个 function(var, Context)

use com\amazon\aws\lambda\{Context, Handler, Stream};

class Streamed extends Handler {

  public function target(): callable {
    return function($event, Stream $stream, Context $context) {
      $stream->use('text/plain');
      $stream->write("[".date('r')."] Hello world...\n");

      sleep(1);

      $stream->write("[".date('r')."] ...from Lambda\n");
      $stream->end();
    };
  }
}

调用此lambda将产生以下结果

Streaming in Terminal

Stream 接口定义如下

public interface com.amazon.aws.lambda.Stream extends io.streams.OutputStream, lang.Closeable {
  public function transmit(io.Channel|io.streams.InputStream $source, string $mimeType): void
  public function use(string $mimeType): void
  public function write(string $bytes): void
  public function end(): void
  public function flush(): void
  public function close(): var
}

开发

要本地运行lambda,请使用以下命令

$ xp lambda run Greet '{"name":"Timm"}'
Hello Timm from PHP 8.2.11 via Greet @ test-local-1

这不会提供完整的lambda环境,并且没有执行限制!要程序化地检测此环境,请使用 $this->environment->local(),它将返回true。

集成测试

要测试在本地容器化lambda环境中运行的lambda,请使用 test 命令。

$ xp lambda test Greet '{"name":"Timm"}'
START RequestId: 9ff45cda-df9b-1b8c-c21b-5fe27c8f2d24 Version: $LATEST
END RequestId: 9ff45cda-df9b-1b8c-c21b-5fe27c8f2d24
REPORT RequestId: 9ff45cda-df9b-1b8c-c21b-5fe27c8f2d24  Init Duration: 922.19 ms...
"Hello Timm from PHP 8.2.11 via test @ us-east-1"

此功能由AWS Lambda自定义运行时基础镜像提供。虽然它也在您的机器上运行,但 $this->environment->local() 将返回false。

设置

第一步是创建和发布运行时层

$ xp lambda runtime
$ aws lambda publish-layer-version \
  --layer-name lambda-xp-runtime \
  --zip-file fileb://./runtime-X.X.X.zip \
  --region us-east-1

...并创建一个角色

$ cat > /tmp/trust-policy.json
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": {"Service": "lambda.amazonaws.com"},
    "Action": "sts:AssumeRole"
  }]
}
$ aws iam create-role \
  --role-name InvokeLambda \
  --path "/service-role/" \
  --assume-role-policy-document file:///tmp/trust-policy.json

在确保使用composer更新了依赖项后,创建函数

$ xp lambda package Greet.class.php
$ aws lambda create-function \
  --function-name greet \
  --handler Greet \
  --zip-file fileb://./function.zip \
  --runtime provided.al2 \
  --role "arn:aws:iam::XXXXXXXXXXXX:role/service-role/InvokeLambda" \
  --region us-east-1 \
  --layers "arn:aws:lambda:us-east-1:XXXXXXXXXXXX:layer:lambda-xp-runtime:1"

调用

要调用函数

$ aws lambda invoke \
  --cli-binary-format raw-in-base64-out \
  --function-name greet \
  --payload '{"name":"Timm"}'
  response.json
$ cat response.json
"Hello Timm from PHP 8.0.10 via greet @ us-east-1"

部署更改

在最初创建lambda之后,您可以更新其代码如下

$ xp lambda package Greet.class.php
$ aws lambda update-function-code \
  --function-name greet \
  --zip-file fileb://./function.zip \
  --publish

升级运行时

要升级现有的运行时层,构建新的运行时并发布新版本,请调用以下命令以创建新版本

$ xp lambda runtime
$ aws lambda publish-layer-version \
  --layer-name lambda-xp-runtime \
  --zip-file fileb://./runtime-X.X.X.zip \
  --region us-east-1

现在,切换函数以使用此新层

$ aws lambda update-function-configuration \
  --function-name greet \
  --layers "arn:aws:lambda:us-east-1:XXXXXXXXXXXX:layer:lambda-xp-runtime:2"

使用其他AWS服务

要程序化地使用其他AWS服务,请使用 ServiceEndpoint

use com\amazon\aws\{Credentials, ServiceEndpoint};
use com\amazon\aws\lambda\Handler;

class WebSockets extends Handler {

  /** @return callable|com.amazon.aws.lambda.Lambda|com.amazon.aws.lambda.Streaming */
  public function target() {
    return function($event, $context) {

      // Send message to WebSocket connection
      $this->environment->endpoint('execute-api')
        ->in($context->region)
        ->using($event['requestContext']['apiId'])
        ->resource('/{stage}/@connections/{connectionId}', $event['requestContext'])
        ->transmit(['message' => 'Reply'])
      ;
      return ['statusCode' => 200];
    };
  }
}

要本地测试此功能,请通过命令行的 -e 传递必要的环境变量

$ xp lambda test -e AWS_ACCESS_KEY_ID=... -e AWS_SECRET_ACCESS_KEY=... WebSockets '{"requestContext":...}'
# ...

上下文

传递给目标lambda的上下文对象定义如下

public class com.amazon.aws.lambda.Context implements lang.Value {
  public string $awsRequestId
  public string $invokedFunctionArn
  public string $traceId
  public string $clientContext
  public string $cognitoIdentity
  public string $deadline
  public string $functionName
  public string $functionVersion
  public string $memoryLimitInMB
  public string $logGroupName
  public string $logStreamName
  public string $region
  public int $payloadLength

  public function __construct(array $headers, array $environment)

  public function remainingTime(?float $now): ?float
  public function toString(): string
  public function hashCode(): string
  public function compareTo(var $value): int
}

环境

运行时环境定义如下

public class com.amazon.aws.lambda.Environment {
  public string $root
  public [:string] $variables
  public io.streams.StringWriter $writer
  public util.PropertySource $properties

  public function __construct(string $root, ?io.streams.StringWriter $writer)

  public function taskroot(): io.Path
  public function path(string $path): io.Path
  public function tempDir(): io.Path
  public function local(): bool
  public function variable(string $name): ?string
  public function credentials(): com.amazon.aws.Credentials
  public function trace(var... $args): void
  public function properties(string $name): util.PropertyAccess
}

接口

除了函数外,处理器的 target() 方法还可以返回实现 LambdaStreaming 接口的实例

public interface com.amazon.aws.lambda.Lambda {
  public function process(var $event, com.amazon.aws.lambda.Context $context): var
}

public interface com.amazon.aws.lambda.Streaming {
  public function handle(
    var $event,
    com.amazon.aws.lambda.Stream $stream,
    com.amazon.aws.lambda.Context $context
  ): void
}

另请参阅