AWS SDK for PHPでasyncを使ってみる

SDKの提供するメソッドには、非同期実行用にサフィックスにAsyncがついたものが必ず用意されています。


jQueryのDeferredのように使えそうな感じ。

S3ClientのPutObjectメソッドを使って動作を確認してみます。

まずは同期実行のパターン。
<?php
require 'vendor/autoload.php';
date_default_timezone_set('Asia/Tokyo');

use Aws\S3\S3Client;

// 設定値
$credentials = [
    'key'       => '*****************',
    'secret'    => '**************************************',
];

$bucketName = 'my-bucket';

// 100MBのダミーファイルを作成
$filesize = 104857600;
$dummyFile = 'dummy.dat';
$fp = fopen($dummyFile, 'wb');
ftruncate($fp, $filesize);
fclose($fp);

// --- S3にファイルアップロード(同期)---
$s3client = S3Client::factory([
    'credentials' => $credentials,
    'region' => 'ap-northeast-1',
    'version' => 'latest',
]);

// タイマーセット
$timer = microtime(true);

echo 'アップロード開始:' . date('Y/m/d H:i:s') . PHP_EOL;

for($i = 0; $i < 10; $i++){ 
    $fileName = uniqid() . '.dat';
    $result = $s3client->putObject([
        'Bucket'        => $bucketName,
        'Key'           => 'dummy/' . $fileName,
        'SourceFile'    => $dummyFile,
    ]);
}

echo 'アップロード終了:' . (microtime(true) - $timer) . '秒' . PHP_EOL;
>php sync.php
アップロード開始:2020/05/02 16:13:22
アップロード終了:85.937815189362秒
>aws s3 ls s3://my-bucket/dummy/
2020-05-02 16:13:24  104857600 5ead1d920e20d.dat
2020-05-02 16:13:31  104857600 5ead1d98daad5.dat
2020-05-02 16:13:40  104857600 5ead1da2be3cc.dat
2020-05-02 16:13:53  104857600 5ead1daf57494.dat
2020-05-02 16:14:03  104857600 5ead1db8ce204.dat
2020-05-02 16:14:10  104857600 5ead1dc092234.dat
2020-05-02 16:14:22  104857600 5ead1dcc32340.dat
2020-05-02 16:14:30  104857600 5ead1dd3ee445.dat
2020-05-02 16:14:37  104857600 5ead1ddb0cc76.dat
2020-05-02 16:14:43  104857600 5ead1de166785.dat
同期なので順々にアップロードされています。

次は非同期実行のパターン。
<?php
require 'vendor/autoload.php';
date_default_timezone_set('Asia/Tokyo');

use Aws\S3\S3Client;

use GuzzleHttp\Promise;

// 設定値
$credentials = [
    'key'       => '*****************',
    'secret'    => '**************************************',
];

$bucketName = 'my-bucket';

// 100MBのダミーファイルを作成
$filesize = 104857600;
$dummyFile = 'dummy.dat';
$fp = fopen($dummyFile, 'wb');
ftruncate($fp, $filesize);
fclose($fp);

// --- S3にファイルアップロード(非同期)---
$s3client = S3Client::factory([
    'credentials' => $credentials,
    'region' => 'ap-northeast-1',
    'version' => 'latest',
]);

// タイマーセット
$timer = microtime(true);

echo 'アップロード開始:' . date('Y/m/d H:i:s') . PHP_EOL;

for($i = 0; $i < 10; $i++){ 
    $fileName = uniqid() . '.dat';
    $promise = $s3client->putObjectAsync([
        'Bucket'        => $bucketName,
        'Key'           => 'dummy/' . $fileName,
        'SourceFile'    => $dummyFile,
    ]);
    $promises[] = $promise;
}

$aggregate = Promise\all($promises);
$aggregate->wait(); // ここでS3へのリクエストが実行される

echo 'アップロード終了:' . (microtime(true) - $timer) . '秒' . PHP_EOL;
>php async.php
アップロード開始:2020/05/02 16:17:06
アップロード終了:42.00698018074秒
>aws s3 ls s3://my-bucket/dummy/
2020-05-02 16:17:15  104857600 5ead1e729d531.dat
2020-05-02 16:17:15  104857600 5ead1e72a2b36.dat
2020-05-02 16:17:15  104857600 5ead1e72a3657.dat
2020-05-02 16:17:15  104857600 5ead1e72a3ced.dat
2020-05-02 16:17:15  104857600 5ead1e72a4316.dat
2020-05-02 16:17:15  104857600 5ead1e72a4938.dat
2020-05-02 16:17:15  104857600 5ead1e72a4f69.dat
2020-05-02 16:17:15  104857600 5ead1e72a58dd.dat
2020-05-02 16:17:15  104857600 5ead1e72a60d1.dat
2020-05-02 16:17:15  104857600 5ead1e72a6733.dat
並列実行している分、少し早くなりました。
同時にアップロード処理がコミットされたのか、S3側のタイムスタンプが全く同じ時刻になっています。


なるほど、うまく使えばSDKを使ったコードの実行速度を向上させられそうですが、非同期処理はエラートラップや実行順序の制御が難しくなるので注意が必要ですね。