处理大量PUT请求到休息api

时间:2015-04-26 10:16:43

标签: asp.net performance asp.net-mvc-5 httpwebrequest

我一直在努力寻找一种方法来提高这项任务的效率。我正在使用基于REST的Web服务,需要更新超过2500个客户端的信息。

我正在使用fiddler来查看请求,并且我还在更新表时更新了表。我每秒得到大约1个响应。我的期望值很高吗?我甚至不确定我要定义的是什么?快速'在这种情况下。

我正在处理控制器中的所有内容,并尝试根据该地点的示例并行运行多个Web请求,但它似乎没有什么区别。说实话,我不太了解它,只是试图让它建立起来。我怀疑它仍在等待每个请求完成再次发射之前。

我还根据另一个建议增加了我的网络配置文件中的连接但没有成功:

 <system.net>
    <connectionManagement>
      <add address="*" maxconnection="20" />
    </connectionManagement>
  </system.net>

我的控制器操作方法如下所示:

public async Task<ActionResult> UpdateMattersAsync()
        {
           //Only get  matters we haven't synced yet
            List<MatterClientRepair> repairList = Data.Get.AllUnsyncedMatterClientRepairs(true);
        //Take the next 500
        List<MatterClientRepair> subRepairList = repairList.Take(500).ToList();          

        FinalisedMatterViewModel vm = new FinalisedMatterViewModel();


        using (ApplicationDbContext db = new ApplicationDbContext())
        {
            int jobCount = 0;
            foreach (var job in subRepairList)
            {
                // If not yet synced - it shouldn't ever be!!
                if (!job.Synced)
                {
                    jobCount++;

                   // set up some Authentication fields
                    var oauth = new OAuth.Manager();
                    oauth["access_token"] = Session["AccessToken"].ToString();
                    string uri = "https://app.com/api/v2/matters/" + job.Matter;

                    // prepare the json object for the body
                    MatterClientJob jsonBody = new MatterClientJob();
                    jsonBody.matter = new MatterForUpload();
                    jsonBody.matter.client_id = job.NewClient;
                    string jsonString = jsonBody.ToJSON();                        

                   // Send it off. It returns the whole object we updated - we don't actually do anything with it
                    Matter result = await oauth.Update<Matter>(uri, oauth["access_token"], "PUT", jsonString);

                    // update our entities                       
                    var updateJob = db.MatterClientRepairs.Find(job.ID);
                    updateJob.Synced = true;
                    updateJob.Update_Time = DateTime.Now;
                    db.Entry(updateJob).State = System.Data.Entity.EntityState.Modified;
                    if (jobCount % 50 == 0)
                    {
                        // save every 50 changes
                        db.SaveChanges();
                    }
                }
            }

            // if there are remaining files to save
            if (jobCount % 50 != 0)
            {
                db.SaveChanges();
            }

            return View("FinalisedMatters", Data.Get.AllMatterClientRepairs());
        }
    }

当然还有处理Web请求的Update方法本身:

  public async Task<T> Update<T>(string uri, string token, string method, string json)
                {  
                var authzHeader = GenerateAuthzHeader(uri, method);
                // prepare the token request

                var request = (HttpWebRequest)WebRequest.Create(uri);
                request.Headers.Add("Authorization", authzHeader);
                request.Method = method;
            request.ContentType = "application/json";
            request.Accept = "application/json, text/javascript";
            byte[] bytes = System.Text.Encoding.ASCII.GetBytes(json);
            request.ContentLength = bytes.Length;
            System.IO.Stream os = request.GetRequestStream();
            os.Write(bytes, 0, bytes.Length);
            os.Close();
            WebResponse response = await request.GetResponseAsync();

            using (var reader = new System.IO.StreamReader(response.GetResponseStream()))
            {
                return JsonConvert.DeserializeObject<T>(reader.ReadToEnd());

            }

        }

如果每秒不能超过1个请求,那么我有兴趣查看Ajax解决方案,这样我就可以在处理时给用户一些反馈。在我目前的解决方案中,我无法在操作方法未达到“返回”状态时向用户提供反馈。我可以吗?

1 个答案:

答案 0 :(得分:2)

好的,我花了几天时间(以及大量的试验和错误),但我已经解决了这个问题。希望它可以帮助别人。我终于找到了我的银弹。它可能是我应该开始的地方: MSDN: Consuming the Task-based Asynchronous Pattern

最后,下面这行代码就是最重要的一步。

string [] pages = await Task.WhenAll(from url in urls select  DownloadStringAsync(url));

我替换了一些东西,使其适用于Put请求,如下所示:

HttpResponseMessage[] results = await Task.WhenAll(from p in toUpload select client.PutAsync(p.uri, p.jsonContent));

'toUpload'是MyClass的列表:

  public class MyClass
    {
        // the URI should be relative to the base pase
        // (ie: /api/v2/matters/101)
        public string uri { get; set; }

        // a string in JSON format, being the body of the PUT request
        public StringContent jsonContent { get; set; }
    }

关键是停止尝试将我的PutAsync方法放入循环中。我的新代码行仍然阻塞,直到所有响应都回来,但这就是我想要的。另外,了解我可以使用这个LINQ样式表达式来动态创建任务列表是非常有帮助的。我不会发布所有代码(除非有人想要它),因为它不像原版那样很好地重构,我仍需要检查每个项目的响应是否为200 OK,然后我将其记录为成功保存在我的数据库中。那么它的速度有多快?

结果

我测试了来自本地计算机的50个Web服务调用的示例。 (最后,Azure中的SQL数据库有一些保存记录)。

原始同步代码:70.73秒

异步代码:8.89秒

从每秒1.4146个请求下降到每秒0.1778个请求! (如果你将其平均化)

<强>结论

我的旅程尚未结束。我刚刚触及异步编程的表面并且非常喜欢它。我现在需要弄清楚如何只保存返回200 OK的结果。我可以反序列化HttpResponse,它返回一个JSON对象(它有一个我可以查找的唯一ID等)。或者我可以使用Task.WhenAny方法,并尝试交错。