识别哪个承诺失败并动态更改 Guzzle 6 中的承诺队列

Identify which promise failed and dynamically change promise queue in Guzzle 6

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

为了加快速度,我不是只使用一台服务器一个接一个地下载所有文件,而是同时使用所有服务器。问题是其中一台服务器可能比其他服务器慢得多,甚至可能已关闭。使用 Guzzle 批量下载文件时,该批次中的所有文件必须在另一批次开始之前下载。

有没有办法立即开始同时下载另一个文件,以便所有服务器都不断地下载一个文件?

如果服务器出现故障,我将超时设置为 300 秒,当超时时,Guzzle 将捕获它的 ConnectionException。

如何确定哪些承诺(下载)失败,以便取消它们?我可以获取有关 file/server 失败的信息吗?

下面是我用来说明这一点的代码的简化示例。感谢您的帮助!

$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
        }
    }
}

我不确定您是如何使用 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() 接受两个回调。第一个在成功时触发,第二个在失败时触发。 Source calls them $onFullfilled and $onRejected. Other usages are documented in guzzle documentation。这样您就可以在文件失败后立即开始下载文件。

Can I get information about which file/server failed?

当承诺失败时,意味着请求仍未实现。在这种情况下,您可以通过将 RequestException class 的实例传递给第二个 then() 的回调来获取主机和请求的路径:

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() returns GuzzleHttp\Psr7\Request class 的实例和 this class are available to be used here. (Guzzle and PSR-7)

上的所有方法

When an item is successfully downloaded, can we then immediately start a new file download on this free server, whilst the other files are still downloading?

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