Как использовать асинхронный HttpClient.PostAsync, когда URL-адрес отвечает после искусственного тайм-аута?

Как можно HttpClient.PostAsync использоваться для отправки HTTP-запросов на URL-адреса с искусственным временем отправки ответа.

Обратите внимание на URL-адрес и параметр sleep=30, который используется для введения искусственной задержки в 30 секунд перед отправкой ответа HTTP.

        Console.WriteLine("Start time : " + DateTime.Now);

        for (int i = 1; i <= 10; i++)
        { 
            using (var client = new HttpClient())
            {
                client.BaseAddress = new Uri(@"http://fake-response.appspot.com/api/?data={Hello World}&sleep=30");
                //client.BaseAddress = new Uri(@"http://fake-response.appspot.com/api/?data={Hello World}&status=200");

                client.Timeout = new TimeSpan(0, 0, 60);

                var parameters = new Dictionary<string, string>();
                parameters["ContentType"] = "text/plain;charset=UTF-8";

                //Create Task to POST to the URL
                client.DefaultRequestHeaders.ExpectContinue = true;
                var response = await client.PostAsync(client.BaseAddress, new FormUrlEncodedContent(parameters));

                Task<bool> b1 = ProcessURLAsync(response, 1, 5, 2);
            }
        }

        Console.WriteLine("End time : " + DateTime.Now);

Что нужно сделать, так это то, что асинхронные HTTP-сообщения должны выполняться в цикле и не должны зависеть от тайм-аута, указанного в URL-адресе. Однако время ожидания PostAsync истекает до получения ответа.

Пожалуйста, проверьте время, необходимое для отправки 2 разных URL-адресов в цикле из 10 асинхронных сообщений POST.

Я проверил HttpClient.DefaultRequestHeaders.ExpectContinue , но я так не думаю может помочь в этом случае использования.


person Sanjay Karia    schedule 08.10.2016    source источник
comment
Я не уверен, что понимаю, но я думаю, вы хотите бесконечный тайм-аут?   -  person Kevin Gosse    schedule 08.10.2016
comment
@KooKiz - это альтернатива, но это не сделает запросы асинхронными   -  person Sanjay Karia    schedule 08.10.2016
comment
Таким образом, вы ожидаете, что запрос займет 30 секунд, но установите тайм-аут на 10 секунд, и время ожидания истечет. Что здесь не так?   -  person Evk    schedule 08.10.2016
comment
@Evk Тайм-аут в 10 секунд предназначен для тайм-аута сети, тогда как искусственная задержка на стороне сервера заранее неизвестна. В основном клиент должен отправлять запросы и двигаться вперед и обрабатывать ответ асинхронно, когда он доступен.   -  person Sanjay Karia    schedule 08.10.2016


Ответы (2)


Эта искусственная задержка ничем не отличается от тайм-аута сети с точки зрения клиента. Таким образом, вы должны установить client.Timeout на максимальную ожидаемую искусственную задержку + реальное время ожидания сети. Если не хотите блокировать ожидание ответа - просто не await Task вернули из PostAsync. Вы можете хранить все такие задачи в некотором списке и ждать их завершения с помощью await Task.WhenAll(yourTaskList). Или вы можете использовать ContinueWith для выполнения определенных действий, когда данная задача будет завершена. Однако, если вы вообще заботитесь об ответе, вам все равно нужно установить достаточно большой тайм-аут, иначе запрос будет прерван преждевременно.

Вот пример кода, который поможет вам

    static async void MakeRequests()
    {
        var requests = new List<Task<bool>>();
        for (int i = 1; i <= 10; i++)
        {
            // no await here, so, not await MakeRequest(i);
            requests.Add(MakeRequest(i));
        }
        // now all 10 requests are running in parallel
        try {
            await Task.WhenAll(requests);
        }
        catch {
           // no need to handle it here - we handle all errors below 
        }

        // if we are here, all requests are either completed or failed, inspect their results
        foreach (var request in requests) {
            if (request.IsCanceled) {
                // failed by timeout
            }
            else if (request.IsFaulted) {
                // failed
                Log(request.Exception);
            }
            else {
                // success
                bool result = request.Result;
                // handle your result here if needed
            }
        }
    }

    static async Task<bool> MakeRequest(int i) {
        using (var client = new HttpClient()) {
            client.BaseAddress = new Uri(@"http://fake-response.appspot.com/api/?data={Hello World}&sleep=30");
            //client.BaseAddress = new Uri(@"http://fake-response.appspot.com/api/?data={Hello World}&status=200");
            // no timeout here, or set to max expected delay
            //client.Timeout = new TimeSpan(0, 0, 60);

            var parameters = new Dictionary<string, string>();
            parameters["ContentType"] = "text/plain;charset=UTF-8";

            //Create Task to POST to the URL
            client.DefaultRequestHeaders.ExpectContinue = true;
            var response = await client.PostAsync(client.BaseAddress, new FormUrlEncodedContent(parameters));

            Task<bool> b1 = ProcessURLAsync(response, 1, 5, 2);
            return b1;
        }
    }
person Evk    schedule 08.10.2016
comment
Ответы нужно обрабатывать. Таким образом, в основном мы либо ждем одного ответа (или суммы времени всех ответов), оба из которых не решают проблему (асинхронные запросы POST - с искусственным временем ответа). Хотя это не решает проблему, но я думаю, что это было бы близко к ответу, однако подождал бы дальнейших входных данных. - person Sanjay Karia; 08.10.2016
comment
Ну, я не очень понимаю, что вам нужно. Вы делаете несколько запросов параллельно (не ожидая Задачи), затем вы ждете не сумму раз всех ответов, а ждете max i> раз их ответов (потому что они выполняются параллельно). Если вы хотите обрабатывать каждый ответ немедленно, когда он доступен, используйте ContinueWith (без ожидания). - person Evk; 08.10.2016
comment
Я отредактировал исходный код в вопросе. Лучший способ понять этот вариант использования — проверить время, затраченное на выполнение 10 POST-запросов на 2 разных URL-адреса в приведенном выше примере кода. Существует огромная разница во времени, необходимом для POST, когда URL-адрес содержит искусственную временную задержку, хотя используется асинхронность. - person Sanjay Karia; 09.10.2016
comment
@SanjayKaria Я добавил пример кода, чтобы помочь вам понять проблему. - person Evk; 09.10.2016
comment
Собрав все задания, не дожидаясь их и потом дождавшись, пока все они сработают. - person Sanjay Karia; 10.10.2016

Однако время ожидания PostAsync истекает до получения ответа.

Этот метод истекает, потому что вы установили для свойства HttpClient.Timeout значение 10 секунд. Установка этого свойства указывает клиенту на тайм-аут по истечении указанного времени, если ответ не получен.

person oldbam    schedule 08.10.2016
comment
Если мы не установим тайм-аут, то по умолчанию он будет равен 100 секундам и будет тайм-аут для искусственных задержек > 100сек. Суть в том, что независимо от тайм-аута обработка останавливается на линии - var response = await client.PostAsync(client. BaseAddress, новый FormUrlEncodedContent (параметры)); ожидание ответа от запроса, вместо того, чтобы двигаться дальше. Это ожидание не происходит, когда мы удаляем искусственную задержку (sleep=30) из URL. - person Sanjay Karia; 09.10.2016
comment
Другой пользователь уже ответил на ваш вопрос: вам нужно 1) увеличить время ожидания, которое вы установили на HttpClient; 2) не вызывать await после PostAsync, а собирать все задачи, возвращаемые при вызове PostAsync, в список, а затем вызывать await Task.WhenAll(tasks) - person oldbam; 09.10.2016