У меня есть приложение, где мне нужно сделать alot httpWebRequest за один раз и получить ответ так быстро, как это возможно для каждого из них. Actualy Мне не нужно быстро получать все ответы, мне нужно получить каждый ответ так быстро, как я могу. Я имею в виду, что должно быть минимальное время между отправкой каждого запроса ang get response.async HttpWebRequest vs sync в многопоточном приложении
Я читал много статей о асинхронном/жду технологий и выбрал это решение:
private static void Main()
{
ServicePointManager.DefaultConnectionLimit = 100;
ServicePointManager.Expect100Continue = false;
ServicePointManager.UseNagleAlgorithm = false;
ProcessAsync("https://www.google.ru");
Console.ReadKey();
}
private static void ProcessAsync(string url)
{
for (var i = 1; i <= 100; i++)
Task.Factory.StartNew(async() => await ProcessInternalAsync(url));
}
private static async Task ProcessInternalAsync(string url)
{
var request = GetRequest(url);
var sw = Stopwatch.StartNew();
await GetStringContentAsync(request);
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
}
public static async Task<string> GetStringContentAsync(HttpWebRequest webRequest)
{
using (var response = (HttpWebResponse) await webRequest.GetResponseAsync()
.ConfigureAwait(false))
{
var content = await GetStringContentFromResponseAsync(response)
.ConfigureAwait(false);
return content;
}
}
private static async Task<string> GetStringContentFromResponseAsync(HttpWebResponse response)
{
using (var responseStream = GetStreamForResponse(response))
{
if (responseStream == null)
return null;
using (var streamReader = new StreamReader(responseStream))
{
var content = await streamReader.ReadToEndAsync()
.ConfigureAwait(false);
return content;
}
}
}
И это работает, но Wery медленно .. (500-1600 мса каждый запрос)
Так я пытался " простое "решение без async/wait:
private static void Main()
{
ServicePointManager.DefaultConnectionLimit = 100;
ServicePointManager.Expect100Continue = false;
ServicePointManager.UseNagleAlgorithm = false;
Process("https://www.google.ru");
Console.ReadKey();
}
private static void Process(string url)
{
for (var i = 1; i <= 100; i++)
Task.Factory.StartNew(() => ProcessInternal(url));
}
private static void ProcessInternal(string url)
{
var request = GetRequest(url);
var sw = Stopwatch.StartNew();
GetStringContent(request);
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
}
public static string GetStringContent(HttpWebRequest webRequest)
{
using (var response = (HttpWebResponse) webRequest.GetResponse())
{
var content = GetStringContentFromResponse(response);
return content;
}
}
private static string GetStringContentFromResponse(HttpWebResponse response)
{
using (var responseStream = GetStreamForResponse(response))
{
if (responseStream == null)
return null;
using (var streamReader = new StreamReader(responseStream))
{
var content = streamReader.ReadToEnd();
return content;
}
}
}
И он работает намного быстрее! (55-180 мс каждый запрос).
Вот некоторые другие методы:
private static HttpWebRequest GetRequest(string url)
{
var request = (HttpWebRequest) WebRequest.Create(url);
request.Timeout = 15000;
request.Proxy = null;
request.Headers.Add("Accept-Encoding", "gzip,deflate");
return request;
}
private static Stream GetStreamForResponse(HttpWebResponse webResponse)
{
var responseStream = webResponse.GetResponseStream();
if (responseStream == null)
return null;
Stream stream;
switch (webResponse.ContentEncoding.ToUpperInvariant())
{
case "GZIP":
stream = new GZipStream(responseStream, CompressionMode.Decompress);
break;
case "DEFLATE":
stream = new DeflateStream(responseStream, CompressionMode.Decompress);
break;
default:
stream = responseStream;
break;
}
return stream;
}
Так что мой вопрос: почему так большая разница? Я думал, что когда я использую ожидание с операциями ввода-вывода - это хороший способ, и это лучший способ делать HTTP-запросы. Но на практике это не совсем верно.
В решении, где нет асинхронных/ожидающих, у нас есть блок потока при ожидании ответа, и если он не будет Google и будет сервером с длительными операциями, эти запросы заблокируют все мое приложение.
Может быть, есть лучший способ решить мою проблему?
PS: Это "магия": ServicePointManager.Expect100Continue = false;
ServicePointManager.UseNagleAlgorithm = false;
это лишь некоторые советы из интернета и я понятия не имею, как она работает или нет ..
Update Спасибо к Servy, я Finaly понимал мою ошибку в измерении. Теперь метод процесса выглядит следующим образом:
private static void Process(string url)
{
var taskCollection = new List<Task>();
for (var i = 1; i <= 100; i++)
{
var sw = Stopwatch.StartNew();
taskCollection.Add(Task.Factory.StartNew(() =>
{
ProcessInternal(url);
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
}));
}
Task.WaitAll(taskCollection.ToArray());
}
Теперь второе решение показывает миллисекунды (300-1000 мс для запроса). Но в любом случае это быстрее, чем async/await. Метод async/await должен быть медленнее?
Благодарим вас за ответ. Вы правы, я теряю немного времени там, когда начинаю задачи, но в любом случае разница настолько велика, что проблема не в 5-10 мс. Я переместил секундомер внутри GetStringContent и GetStringContentAsync (это может помочь измерить более справедливое), но нет никакой разницы. –
Как вы думаете, где мне нужно разместить секундомер для измерения времени запроса в async-решении? –
@ ЮрийБржозовский Я заявил, что вы должны делать с самого начала; измерять время всей программы, а не отдельные операции. – Servy