AWS SDK for PHP 版本 3 中的命令对象 - AWS SDK for PHP

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

AWS SDK for PHP 版本 3 中的命令对象

AWS SDK for PHP 使用命令模式封装稍后用于传输HTTP请求的参数和处理程序。

隐式使用命令

如果你检查任何客户端类,你会发现与API操作对应的方法实际上并不存在。它们是使用 __call() 魔术方法实施。这些伪方法实际上是封装命令对象使用的快捷方式。SDK

您通常不需要直接与命令对象交互。当你调用类似的方法时Aws\S3\S3Client::putObject(),SDK实际上会根据提供的参数创建一个Aws\CommandInterface对象,执行命令并返回一个已填充的Aws\ResultInterface对象(或者在出错时抛出异常)。在调用客户端的任何Async方法(例如Aws\S3\S3Client::putObjectAsync())时,也会出现类似的流程:客户端根据提供的参数创建命令,序列化HTTP请求,启动请求并返回承诺。

以下示例在功能上等效。

$s3Client = new Aws\S3\S3Client([ 'version' => '2006-03-01', 'region' => 'us-standard' ]); $params = [ 'Bucket' => 'foo', 'Key' => 'baz', 'Body' => 'bar' ]; // Using operation methods creates a command implicitly $result = $s3Client->putObject($params); // Using commands explicitly $command = $s3Client->getCommand('PutObject', $params); $result = $s3Client->execute($command);

命令参数

所有命令都支持一些特殊参数,这些参数不是服务的一部分,API而是控制服务的行为。SDK

@http

使用此参数,可以微调底层HTTP处理程序执行请求的方式。可包含在 @http 参数中的选项与您在使用“http”客户端选项对客户端进行实例化时可设置的选项相同。

// Configures the command to be delayed by 500 milliseconds $command['@http'] = [ 'delay' => 500, ];

@retries

“retries”客户端选项类似,@retries 控制一个命令在被视为已失败之前可以重试的次数。将其设置为 0 可禁用重试。

// Disable retries $command['@retries'] = 0;
注意

如果您已对客户端禁用重试,则无法选择性地对传递给该客户端的各个命令启用重试。

创建命令对象

您可以使用客户端的 getCommand() 方法创建命令。它不会立即执行或传输HTTP请求,而是只有在请求传递给客户端的execute()方法时才会执行。这样一来,您有机会在执行命令之前修改命令对象。

$command = $s3Client->getCommand('ListObjects'); $command['MaxKeys'] = 50; $command['Prefix'] = 'foo/baz/'; $result = $s3Client->execute($command); // You can also modify parameters $command = $s3Client->getCommand('ListObjects', [ 'MaxKeys' => 50, 'Prefix' => 'foo/baz/', ]); $command['MaxKeys'] = 100; $result = $s3Client->execute($command);

命令 HandlerList

从客户端创建命令后,该命令将得到客户端 Aws\HandlerList 对象的一个克隆。该命令将得到客户端处理程序列表的一个克隆,以允许命令使用不影响客户端执行的其他命令的自定义中间件和处理程序。

这意味着你可以为每个命令使用不同的HTTP客户端(例如Aws\MockHandler),并通过中间件为每个命令添加自定义行为。以下示例使用创建MockHandler模拟结果,而不是发送实际HTTP请求。

use Aws\Result; use Aws\MockHandler; // Create a mock handler $mock = new MockHandler(); // Enqueue a mock result to the handler $mock->append(new Result(['foo' => 'bar'])); // Create a "ListObjects" command $command = $s3Client->getCommand('ListObjects'); // Associate the mock handler with the command $command->getHandlerList()->setHandler($mock); // Executing the command will use the mock handler, which returns the // mocked result object $result = $client->execute($command); echo $result['foo']; // Outputs 'bar'

除了更改命令所使用的处理程序外,您还可以将自定义中间件注入该命令。以下示例使用 tap 中间件,其作用是充当处理程序列表中的观察者。

use Aws\CommandInterface; use Aws\Middleware; use Psr\Http\Message\RequestInterface; $command = $s3Client->getCommand('ListObjects'); $list = $command->getHandlerList(); // Create a middleware that just dumps the command and request that is // about to be sent $middleware = Middleware::tap( function (CommandInterface $command, RequestInterface $request) { var_dump($command->toArray()); var_dump($request); } ); // Append the middleware to the "sign" step of the handler list. The sign // step is the last step before transferring an HTTP request. $list->append('sign', $middleware); // Now transfer the command and see the var_dump data $s3Client->execute($command);

CommandPool

Aws\CommandPool 使您能够使用生成 Aws\CommandInterface 对象的迭代器并发执行命令。CommandPool 确保并发执行的命令数量保持恒定,同时迭代池中的命令(当命令完成时,会执行更多命令,以确保恒定的池大小)。

下面是使用 CommandPool 发送几条命令的简单示例。

use Aws\S3\S3Client; use Aws\CommandPool; // Create the client $client = new S3Client([ 'region' => 'us-standard', 'version' => '2006-03-01' ]); $bucket = 'example'; $commands = [ $client->getCommand('HeadObject', ['Bucket' => $bucket, 'Key' => 'a']), $client->getCommand('HeadObject', ['Bucket' => $bucket, 'Key' => 'b']), $client->getCommand('HeadObject', ['Bucket' => $bucket, 'Key' => 'c']) ]; $pool = new CommandPool($client, $commands); // Initiate the pool transfers $promise = $pool->promise(); // Force the pool to complete synchronously $promise->wait();

该示例对于 CommandPool 显得非常苍白无力。我们来试试更复杂的示例。假设您要将磁盘上的文件上传到 Amazon S3 存储桶。要从磁盘中获取文件列表,我们可以使用 PHP's DirectoryIterator。此迭代器生成 SplFileInfo 对象。CommandPool 接受生成 Aws\CommandInterface 对象的迭代器,因此我们将映射 SplFileInfo 对象以返回 Aws\CommandInterface 对象。

<?php require 'vendor/autoload.php'; use Aws\Exception\AwsException; use Aws\S3\S3Client; use Aws\CommandPool; use Aws\CommandInterface; use Aws\ResultInterface; use GuzzleHttp\Promise\PromiseInterface; // Create the client $client = new S3Client([ 'region' => 'us-standard', 'version' => '2006-03-01' ]); $fromDir = '/path/to/dir'; $toBucket = 'amzn-s3-demo-bucket'; // Create an iterator that yields files from a directory $files = new DirectoryIterator($fromDir); // Create a generator that converts the SplFileInfo objects into // Aws\CommandInterface objects. This generator accepts the iterator that // yields files and the name of the bucket to upload the files to. $commandGenerator = function (\Iterator $files, $bucket) use ($client) { foreach ($files as $file) { // Skip "." and ".." files if ($file->isDot()) { continue; } $filename = $file->getPath() . '/' . $file->getFilename(); // Yield a command that is executed by the pool yield $client->getCommand('PutObject', [ 'Bucket' => $bucket, 'Key' => $file->getBaseName(), 'Body' => fopen($filename, 'r') ]); } }; // Now create the generator using the files iterator $commands = $commandGenerator($files, $toBucket); // Create a pool and provide an optional array of configuration $pool = new CommandPool($client, $commands, [ // Only send 5 files at a time (this is set to 25 by default) 'concurrency' => 5, // Invoke this function before executing each command 'before' => function (CommandInterface $cmd, $iterKey) { echo "About to send {$iterKey}: " . print_r($cmd->toArray(), true) . "\n"; }, // Invoke this function for each successful transfer 'fulfilled' => function ( ResultInterface $result, $iterKey, PromiseInterface $aggregatePromise ) { echo "Completed {$iterKey}: {$result}\n"; }, // Invoke this function for each failed transfer 'rejected' => function ( AwsException $reason, $iterKey, PromiseInterface $aggregatePromise ) { echo "Failed {$iterKey}: {$reason}\n"; }, ]); // Initiate the pool transfers $promise = $pool->promise(); // Force the pool to complete synchronously $promise->wait(); // Or you can chain the calls off of the pool $promise->then(function() { echo "Done\n"; });

CommandPool 配置

Aws\CommandPool 构造函数接受各种配置选项。

concurrency(可调用|整数)

并发执行的命令的最大数量。提供一个函数来动态调整池大小。该函数将获得当前待处理请求数,并且预计会返回一个表示新池大小限制的整数。

before(可调用)

发送每个命令之前要调用的函数。before 函数接受命令和该命令的迭代器密钥。您可以在发送命令之前根据需要更改 before 函数中的命令。

fulfilled(可调用)

执行 Promise 时要调用的函数。此函数将获得结果对象、生成该结果的迭代器的 ID,以及可解析或拒绝的聚合 Promise(如果您需要让池短路)。

rejected(可调用)

拒绝 Promise 时要调用的函数。此函数将获得 Aws\Exception 对象、生成该异常的迭代器的 ID,以及可解析或拒绝的聚合 Promise(如果您需要让池短路)。

命令之间的手动垃圾回收

如果您在大型命令池中达到了内存限制,则可能是由于在达到内存限制时,PHP垃圾收集器SDK尚未收集到循环引用。在命令之间手动调用收集算法可允许在达到该限制之前收集循环。以下示例将创建一个 CommandPool,该池将在发送每个命令之前使用一个回调来调用收集算法。请注意,调用垃圾回收器会降低性能,最佳用法将取决于您的使用案例和环境。

$pool = new CommandPool($client, $commands, [ 'concurrency' => 25, 'before' => function (CommandInterface $cmd, $iterKey) { gc_collect_cycles(); } ]);