0

我需要下载大量存储在多个相同服务器上的大文件。存储在服务器 3 上的文件(如“5.doc”)也存储在服务器 55 上。

为了加快速度,我不是只使用一台服务器一个接一个地下载所有文件,而是同时使用所有服务器。问题是其中一台服务器可能比其他服务器慢得多,甚至可能停机。使用 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 从多个服务器异步下载一个文件,但是可以通过 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()接受两个回调。第一个在成功时触发,第二个在失败时触发。调用它们$onFullfilled$onRejected。其他用法记录在 guzzle文档中。这样,您可以在文件失败后立即开始下载文件。

我可以获得有关哪个文件/服务器失败的信息吗?

当一个承诺失败时,这意味着请求仍未完成。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的所有方法都可以在此处使用。( Guzzle 和 PSR-7 )

当一个项目成功下载后,我们是否可以立即在这个免费服务器上开始一个新的文件下载,而其他文件仍在下载?

我认为您应该决定仅在开始时创建承诺并在第二次回调中重复/更新失败的请求时才下载新文件。尝试做出新的承诺,然后再成功承诺可能会导致下载重复文件的无休止的过程,这并不容易处理。

于 2016-07-10T13:25:27.297 回答