0

複数の同一サーバーに保存されている大量の大きなファイルをダウンロードする必要があります。サーバー 3 に保存されている「5.doc」などのファイルは、サーバー 55 にも保存されます。

これを高速化するために、1 つのサーバーだけを使用してすべてのファイルを 1 つずつダウンロードするのではなく、すべてのサーバーを同時に使用しています。問題は、サーバーの 1 つが他のサーバーよりもはるかに遅いか、ダウンしている可能性があることです。Guzzle を使用してファイルをバッチ ダウンロードする場合、別のバッチを開始する前に、そのバッチ内のすべてのファイルをダウンロードする必要があります。

すべてのサーバーが常にファイルをダウンロードするように、他のファイルと一緒に別のファイルのダウンロードをすぐに開始する方法はありますか?

サーバーがダウンしている場合、タイムアウトを 300 秒に設定しました。これに達すると、Guzzle は ConnectionException をキャッチします。

どの約束 (ダウンロード) が失敗したかを特定してキャンセルできるようにするにはどうすればよいですか? どのファイル/サーバーが失敗したかについての情報を取得できますか?

以下は、ポイントを説明するために使用しているコードの簡単な例です。助けてくれてありがとう!

$filesToDownload = [['5.doc', '8.doc', '10.doc'], ['1.doc', '9.doc']]; //The file names that we need to download
$availableServers = [3, 55, 88]; //Server id's that are available

foreach ($filesToDownload as $index => $fileBatchToDownload) {
    $promises = [];

    foreach ($availableServers as $key => $availableServer) {
        array_push(
            $promises, $client->requestAsync('GET', 'http://domain.com/' . $fileBatchToDownload[$index][$key],  [
                'timeout' => 300,
                'sink' => '/assets/' . $fileBatchToDownload[$index][$key]
            ])
        );

        $database->updateRecord($fileBatchToDownload[$index][$key], ['is_cached' => 1]);
    }

    try {
        $results = Promise\unwrap($promises);
        $results = Promise\settle($promises)->wait();
    } catch (\GuzzleHttp\Exception\ConnectException $e) {
        //When can't connect to the server or didn't download within timeout
        foreach ($e->failed() as $failedPromise) {
            //Re-set record in database to is_cached = 0
            //Delete file from server
            //Remove this server from the $availableServers list as it may be down or too slow
            //Re-add this file to the next batch to download $filesToDownload
        }
    }
}
4

1 に答える 1

1

Guzzle を使用して複数のサーバーから 1 つのファイルを非同期でダウンロードする方法はわかりませんが、失敗したリクエストの配列インデックスを取得するには、promise のthen()方法を使用できます。

array_push(
    $promises,
    $client->requestAsync('GET', "http://localhost/file/{$id}", [
            'timeout' => 10,
            'sink' => "/assets/{$id}"
        ])->then(function() {
            echo 'Success';
        },
        function() use ($id) {
            echo "Failed: $id";
        }
    )
);

then()2 つのコールバックを受け入れます。1 つ目は成功時にトリガーされ、2 つ目は失敗時にトリガーされます。ソースはそれら$onFullfilledを および と呼びます$onRejected。その他の使用方法は、guzzle のドキュメントに記載されています。このようにして、失敗した直後にファイルのダウンロードを開始できます。

どのファイル/サーバーが失敗したかについての情報を取得できますか?

promise が失敗した場合、それは要求が満たされていないことを意味します。この場合、RequestExceptionクラスのインスタンスを secondthen()のコールバックに渡すことで、ホストと要求されたパスを取得できます。

use GuzzleHttp\Exception\RequestException;
.
.
.
array_push(
    $promises,
    $client->requestAsync('GET', "http://localhost/file/{$id}", [
            'timeout' => 10,
            'sink' => "/assets/{$id}"
        ])->then(function() {
            echo 'Success';
        },
        function(RequestException $e)  {
            echo "Host: ".$e->getRequest()->getUri()->getHost(), "\n";
            echo "Path: ".$e->getRequest()->getRequestTarget(), "\n";
        }
    )
);

したがって、失敗したホストとファイルの名前に関する完全な情報が得られます。詳細情報にアクセスする必要がある場合は、 がクラス$e->getRequest()のインスタンスを返し、このクラスGuzzleHttp\Psr7\Requestのすべてのメソッドをここで使用できることを知っておく必要があります。(ガズルとPSR-7 )

アイテムが正常にダウンロードされたら、他のファイルがまだダウンロードされている間に、この無料サーバーで新しいファイルのダウンロードをすぐに開始できますか?

最初にプロミスを作成するときにのみ新しいファイルをダウンロードし、失敗したリクエストを2回目のコールバック内で繰り返し/更新することを決定する必要があると思います。新しい約束をしてから約束を成功させようとすると、重複したファイルをダウンロードする無限のプロセスが発生する可能性があり、これは簡単に処理できません。

于 2016-07-10T13:25:27.297 に答える