本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。
AWS SDK for PHP 版本 3 中的命令对象
AWS SDK for PHP 使用命令模式
隐式使用命令
如果你检查任何客户端类,你会发现与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垃圾收集器SDKCommandPool
,该池将在发送每个命令之前使用一个回调来调用收集算法。请注意,调用垃圾回收器会降低性能,最佳用法将取决于您的使用案例和环境。
$pool = new CommandPool($client, $commands, [
'concurrency' => 25,
'before' => function (CommandInterface $cmd, $iterKey) {
gc_collect_cycles();
}
]);