使用多个http请求 - 响应

时间:2014-08-30 10:40:57

标签: c# xml multithreading http .net-4.0

我有一个循环浏览应用列表的程序。

Apps
--------
App1
App2
App3

现在,对于每个人,我都会做一个http请求,以获取每个应用程序的构建列表作为Xml。

这样的请求,

http://example.com/getapplist.do?appid=App1

给了我一个回复,

<appid name="App1">
  <buildid BldName="Bld3" Status="Not Ready"></buildid> 
  <buildid BldName="Bld2" Status="Ready"></buildid>
  <buildid BldName="Bld1" Status="Ready"></buildid>
</appid>

现在,我使用状态&#34;准备好&#34; 获得最高版本号,然后执行另一个网络API呼叫,例如,

http://example.com/getapplist.do?appid=App1&bldid=Bld2

这给了我一个回复,

 <buildinfo appid="App1" buildid="Bld2" value="someinfo"></build>

我将它们提供给内部数据表。但是现在,这个程序需要花费很长时间才能完成(3个小时),因为我有接近2000个appids,每个id有2个Web请求。我尝试使用指定here BackgroundWorker 对此问题进行排序。我想到将来自http响应的所有信息整理成单个XML,然后使用该XML进行进一步处理。这会引发错误,

  

另一个进程正在使用的文件

所以我的代码看起来像,

if (!backgroundWorker1.IsBusy) 
{
    for(int i = 0; i < appList.Count; i++)
    { 
        BackgroundWorker bgw = new BackgroundWorker();
        bgw.WorkerReportsProgress = true;  
        bgw.WorkerSupportsCancellation = true;                     
        bgw.DoWork += new DoWorkEventHandler(bgw_DoWork);                   
        bgw.ProgressChanged += new ProgressChangedEventHandler(bgw_ProgressChanged);
        bgw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgw_RunWorkerCompleted);
        //Start The Worker 
        bgw.RunWorkerAsync();
    }
}

DoWork函数选择标记值并将其放入XML中。

我可以从所有后台工作人员的所有http响应中将app-buildinfo详细信息放入公共文件中的最佳方法是什么?

2 个答案:

答案 0 :(得分:4)

HTTP请求本质上是IO绑定和异步的,没有理由使用后台工作程序来完成您的需要。

您可以通过Microsoft.Bcl.AsyncHttpClient利用与.NET 4兼容的async-await

private async Task ProcessAppsAsync(List<string> appList)
{
    var httpClient = new HttpClient();

    // This will execute your IO requests concurrently,
    // no need for extra threads.
    var appListTasks = appList.Select(app => httpClient.GetAsync(app.Url)).ToList();

    // Wait asynchronously for all of them to finish
    await Task.WhenAll(appListTasks);

   // process each Task.Result and aggregate them to an xml
    using (var streamWriter = new StreamWriter(@"PathToFile")
    {
        foreach (var appList in appListTasks)
        {
           await streamWriter.WriteAsync(appList.Result);
        }
    }
}

这样,您可以同时处理所有请求,并在完成后处理所有请求。

答案 1 :(得分:0)

此解决方案适用于.Net 2.0及更高版本,使用WebClient类中的异步方法,并使用递减Interlocked类和普通lock的计数器来序列化将结果写入文件。

var writer = XmlWriter.Create(
    new FileStream("api.xml",
                    FileMode.Create));
writer.WriteStartElement("apps"); // root element in the xml
// lock for one write
object writeLock = new object(); 
// this many calls            
int counter = appList.Count;

foreach (var app in appList)
{
    var wc = new WebClient();

    var url = String.Format(
        "http://example.com/getapplist.do?appid={0}&bldid=Bld2", 
        app);
    wc.DownloadDataCompleted += (o, args) =>
        {
            try
            {
                var xd = new XmlDocument();
                xd.LoadXml(Encoding.UTF8.GetString(args.Result));
                lock (writeLock)
                {
                    xd.WriteContentTo(writer);
                }
            }
            finally
            {
                // count down our counter in a thread safe manner
                if (Interlocked.Decrement(ref counter) == 0)
                {
                    // this was the last one, close nicely
                    writer.WriteEndElement();
                    writer.Close();
                    ((IDisposable) writer).Dispose();
                }
            }
        };
    wc.DownloadDataAsync(
        new Uri(url));   
}