实现返回字符串内容的异步Http请求的最佳方法

时间:2015-03-26 09:13:43

标签: c# asynchronous httpwebrequest async-await

在我的应用程序中,我需要执行大量并行的http请求,并且我已经读过使用async / await执行此操作是正确的。在每个请求中,我需要从中获取字符串内容(通常是某些网站的HTML),我的问题是:我怎样才能以最佳方式完成?

我目前的实施:

public static async Task<string> GetStringContentAsync(HttpWebRequest webRequest) 
     { 
         try 
         { 
            using (var response = (HttpWebResponse) await webRequest.GetResponseAsync() 
                                                                    .ConfigureAwait(false)) 
           { 
                 var content = await GetStringContentFromResponseAsync(response) 
                                    .ConfigureAwait(false); 
              return content; 
           } 
         } 
         catch (Exception exception) 
         { 
            return null; 
         } 
     } 


private static async Task<string> GetStringContentFromResponseAsync(HttpWebResponse response) 
    { 
       using (var responseStream = GetResponseStream(response)) 
        { 
           if (responseStream == null) 
               return null; 
           using (var streamReader = new StreamReader(responseStream)) 
           { 
               var content = await streamReader.ReadToEndAsync() 
                                               .ConfigureAwait(false); 
               return content; 
           } 
        } 
    } 

private static Stream GetResponseStream(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; 
    } 

使用的例子:

var httpWebRequest = (HttpWebRequest) WebRequest.Create("http://stackoverflow.com/");
var content = await HttpHelper.GetStringContentAsync(httpWebRequest) 
                                                              .ConfigureAwait(false); 

这是正确的实施,还是我们可以在这里改进一下?也许我在读取流时使用async / await时会有一些开销?

我的问题的原因是,当我使用我的代码时:

for(var i=0;i<1000;i++)
{
  Task.Run(()=>{
    var httpWebRequest = (HttpWebRequest) WebRequest.Create("http://google.com/");
    var content = await HttpHelper.GetStringContentAsync(httpWebRequest) 
                                  .ConfigureAwait(false); 
               });
}

此任务需要很长时间才能执行,但谷歌的一个请求非常快。我认为此示例中的异步请求几乎必须在同一时间准备就绪,而这一次必须非常接近一个google请求&#34;时间。

修改 我忘了说我知道ServicePointManager.DefaultConnectionLimit并在我的应用程序中设置它5000。所以这不是问题。 我无法使用HttpClient,因为我的最终目标是一次从不同的代理执行100-300个请求。如果我理解正确,HttpClient一次只能使用一个代理,并且无法单独设置每个请求。

2 个答案:

答案 0 :(得分:0)

这是一个棘手的问题。既然你知道DefaultConnectionLimit,那它已经很好了,但还有一个有趣且相当令人惊讶的事情:

  httpRequest.ServicePoint.ConnectionLeaseTimeout

  httpRequest.ServicePoint.MaxIdleTime

信息为here,您的延迟可能是由于其默认行为以及在尝试提出下一个请求时保持与ServicePoint的关联

答案 1 :(得分:0)

以下是您的问题的答案:https://msdn.microsoft.com/en-us/library/86wf6409(v=vs.90).aspx

  

在异步回调方法中使用同步调用可能会导致严重的性能损失。使用WebRequest及其后代发出的Internet请求必须使用Stream.BeginRead来读取WebResponse.GetResponseStream方法返回的流。

这意味着在读取响应流时绝对没有同步代码(包括await s)。但即使这还不够,如DNS lookups and TCP connection are still blocking。如果您可以使用.NET 4.0,那么有一个更容易使用的System.Net.Http.HttpClient类。否则,您可以使用System.Threading.ThreadPool,这是我最终在3.5上使用的解决方法:

ThreadPool.QueueUserWorkItem((o) => {
    // make a synchronous request via HttpWebRequest
});