Azure Blob Storage DownloadToStreamAsync зависает при изменении сети - PullRequest
3 голосов
/ 04 ноября 2019

У меня проблема с библиотеками Microsoft.WindowsAzure.Storage v9.3.3 и Microsoft.Azure.Storage.Blob v11.1.0 NuGet. В частности, при загрузке большого файла. Если вы меняете свою сеть во время метода «DownloadToStreamAsync», вызов зависает. Я видел, как мой код, который обрабатывает много файлов, иногда зависает, и я пытался сузить его. Я думаю, что изменение сети может быть надежным способом вызвать сбой в библиотеках хранилища BLOB-объектов Azure.

Дополнительная информация о проблеме;

  • Когда я отключаю сетевой кабель, мой компьютер переключается на WiFi, но запрос никогда не возобновляется
  • Если я начинаю загрузку по WiFi, а затем подключаю сетевой кабель, возникает такая же ошибка
  • Свойство «ServerTimeout» никогда не приводит к сбою запроса или действует как ожидается в соответствии с Документацией
  • Свойство MaximumExecutionTime не выполняет запрос, но мы не хотимограничить себя определенным периодом времени, особенно потому, что мы имеем дело с большими файлами

Следующий код завершается ошибкой 100% времени, если сеть изменяется во время вызова.

static void Main(string[] args)
{
    try
    {
        CloudStorageAccount.TryParse("<Connection String>", out var storageAccount);
        var cloudBlobClient = storageAccount.CreateCloudBlobClient();
        var container = cloudBlobClient.GetContainerReference("<Container Reference>");
        var blobRef = container.GetBlockBlobReference("Large Text.txt");
        Stream memoryStream = new MemoryStream();
        BlobRequestOptions optionsWithRetryPolicy = new BlobRequestOptions() { ServerTimeout = TimeSpan.FromSeconds(5), RetryPolicy = new LinearRetry(TimeSpan.FromSeconds(20), 4) };
        blobRef.DownloadToStreamAsync(memoryStream, null, optionsWithRetryPolicy, null).GetAwaiter().GetResult();
        Console.WriteLine("Completed");
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Exception: {ex.Message}");
    }
    finally
    {
        Console.WriteLine("Finished");
    }
}

Я обнаружил эту активную проблему в GitHub хранилища Azure, но она кажется неактивной.

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

Ответы [ 2 ]

3 голосов
/ 05 ноября 2019

Согласно моим исследованиям, в настоящее время эта поддержка не поддерживается, кроме **MaximumExecutionTime**. Максимальное время выполнения - это время, выделенное для одного вызова API. Если общее время, потраченное в API - по всем запросам REST, повторным попыткам и т. Д. - превышает это значение, время ожидания клиента истечет. Это значение отслеживается только на клиенте, оно не отправляется в службу.

На данный момент вы можете отслеживать количество данных, загруженных в ваш поток, и отменить задачу, если в течение определенного периода времени вообще не загружается никаких данных. **MaximumExecutionTime** позволит вам указать верхнюю границу суммывремени, которое займет API, прежде чем произойдет сбой с таймаутом. MaximumExecutionTime - это общее время, в течение которого разрешена операция, включая повторные попытки. Это приведет к превышению времени ожидания запроса даже при полной потере входящих пакетов.

Дополнительная ссылка:

https://social.msdn.microsoft.com/Forums/SqlServer/en-US/124261bf-bf78-4410-a892-86d574e94f6b/download-hangs-when-network-cable-unplugged?forum=windowsazuredata

https://docs.microsoft.com/en-us/dotnet/api/microsoft.azure.storage.blob.blobrequestoptions.maximumexecutiontime?view=azure-dotnet

В качестве альтернативы, как показано ниже, вы можете разбить ваш файл BLOB на ожидаемые фрагменты, а затем использовать **CloudBlockBlob.DownloadRangeToStream** для загрузки каждого из ваших кусков файлов.

https://stackoverflow.com/a/44384037/6049604

Надеюсь, это поможет.

0 голосов
/ 05 ноября 2019

Спасибо Мохиту за предложение.

  • Создание задачи для проверки длины потока в фоновом режиме
  • Если поток не увеличился за заданный период времени,отмените DownloadToStreamAsync

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: я не писал тестов для этого кода или для того, чтобы заставить его работать быстродействующим образом, так как вы не могли ждать такого ожидания для каждого файла, который вы обрабатываете. Возможно, мне придется отменить начальную задачу, если загрузка завершится, я пока не знаю, я просто хотел, чтобы она сначала работала. Я не считаю, что производство готово.

// Create download cancellation token
var downloadCancellationTokenSource = new CancellationTokenSource();
var downloadCancellationToken = downloadCancellationTokenSource.Token;

var completedChecking = false;

// A background task to confirm the download is still progressing
Task.Run(() =>
{
    // Allow the download to start
    Task.Delay(TimeSpan.FromSeconds(2)).GetAwaiter().GetResult();

    long currentStreamLength = 0;
    var currentRetryCount = 0;
    var availableRetryCount = 5;

    // Keep the checking going during the duration of the Download
    while (!completedChecking)
    {
        Console.WriteLine("Checking");
        if (currentRetryCount == availableRetryCount)
        {
            Console.WriteLine($"RETRY WAS {availableRetryCount} - FAILING TASK");
            downloadCancellationTokenSource.Cancel();
            completedChecking = true;
        }

        if (currentStreamLength == memoryStream.Length)
        {
            currentRetryCount++;
            Console.WriteLine($"Length has not increased. Incremented Count: {currentRetryCount}");
            Task.Delay(TimeSpan.FromSeconds(10)).GetAwaiter().GetResult();
        }
        else
        {
            currentStreamLength = memoryStream.Length;
            Console.WriteLine($"Download in progress: {currentStreamLength}");
            currentRetryCount = 0;
            Task.Delay(TimeSpan.FromSeconds(1)).GetAwaiter().GetResult();
        }
    }
});

Console.WriteLine("Starting Download");

blobRef.DownloadToStreamAsync(memoryStream, downloadCancellationToken).GetAwaiter().GetResult();

Console.WriteLine("Completed Download");
completedChecking = true;

Console.WriteLine("Completed");
...