2016-07-03 11 views
0

Мне нужно загрузить большое количество больших файлов, хранящихся на нескольких одинаковых серверах. Файл, например «5.doc», который хранится на сервере 3, также сохраняется на сервере 55.Определить, какое обещание не удалось и динамически изменить очередь обещаний в Guzzle 6

Чтобы ускорить это, вместо того, чтобы использовать только один сервер для загрузки всех файлов один за другим, м, используя все серверы одновременно. Проблема в том, что один из серверов может быть намного медленнее, чем другие, или даже может быть отключен. При использовании файлов загрузки 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 
     } 
    } 
} 
+0

В настоящее время я устанавливаю неудавшийся идентификатор сервера как заголовок, который затем можно получить через $ e-> getRequest() -> getHeaders() [

] [0]. (временное решение) –

ответ

1

Я не знаю, как вы делаете асинхронную загрузку одного файла с нескольких серверов, используя жрать, но получить индекс массива неудачных запросов может быть сделано then() методом Promise,:

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 называет их $onFullfilled и $onRejected. Другие виды использования задокументированы в documentation. Таким образом, вы можете начать загрузку файла сразу после его сбоя.

Могу ли я получить информацию о том, какой файл/сервер не удалось?

Когда обещание потерпело неудачу, значит, запрос остался невыполненным. В этом случае вы можете получить хост и требуемый путь, передавая экземпляр класса RequestException ко второму then() «s обратного вызова:

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, и все методы на this class доступны для использования здесь. (Guzzle and PSR-7)

Когда элемент успешно загружен, мы можем сразу же начать новую загрузку файлов на этом бесплатном сервере, в то время как другие файлы по-прежнему загрузки?

Я думаю, вы должны решить загрузить новые файлы только с созданием обещаний в самом начале и повторить/возобновить неудачные запросы в течение второго обратного вызова.Попытка сделать новые обещания, последовавшие за успешным обещанием, может привести к бесконечному процессу с загрузкой дублированных файлов, и это непросто в обращении.

+0

Спасибо за помощь по первой части вопроса. Когда элемент успешно загружен, можем ли мы сразу же начать загрузку нового файла на свободный сервер, в то время как другие файлы загружаются? Чтобы уточнить «Я не уверен, как вы выполняете асинхронную загрузку одного файла с нескольких серверов», каждый файл доступен на всех серверах, но файл загружается только с одного сервера, в то время как другие серверы фокусируются на отдых. Когда он запустится, сервер 3 загрузит '5.doc', 55 '8.doc' и 88 '10 .doc '. Когда вы заканчиваете загрузку, они должны начинаться с «1.doc» немедленно. –

+0

Я добавил дополнительную информацию в отношении вашего комментария. @TobyMellor – revo

+0

Спасибо, что это было очень полезно. Я наградил тебя щедростью. –